Groovy/Grails Talk
Home     Login     Register
Viewing category: GORM, Persistence, DBs
Saturday, 12 September 2009
Grails 1.1.1 UUID Id Generation Trickiness
Grails exposes the id of your domains in the url. The default implementation is to use Hibernate's native generator. Depending on your database, this could result in a sequential number. Exposing this number to the world is often not acceptable. For example, you would not want your competitors to be able to judge your order volume by periodically placing an order and taking note of the id. One way to rectify this information exposure is to use a id generator to create an alternative. A very common alternative is to use UUID, which does a great job at obfuscating information. Taking a look at the online documentation, this seems to be an easy task. I have highlighted the necessary changes in the Proposal class definition below:
class Proposal {
    static constraints = {
        title(blank: false)
        level(blank: false, inList: ["Beginner", "Intermediate", "Advanced"])
        timeNeededInMinutes(inList: [30, 60, 90, 120, 180, 240])
        employerContactInfo(nullable: true)
        additionalNeeds(nullable: true)
    }

    static mapping = {
        id generator: 'uuid'
    }


    String title
    String description
    String objective
    String level
    String employerContactInfo
    int timeNeededInMinutes
    String additionalNeeds
}

Running tests things are not quite what they should be. The test results show an error:
IllegalArgumentException occurred while calling setter of Proposal.id; nested exception is org.hibernate.PropertyAccessException: IllegalArgumentException occurred while calling setter of Proposal.id

Further down, the stacktrace states:
Caused by: java.lang.IllegalArgumentException: argument type mismatch

This seems to indicate something is wrong about the type. So, firing up the HSQL Database Manager and looking at the definition for our table the id is of type BIGINT. The documentation states
The UUID is encoded as a string of 32 hexadecimal digits in length.

Considering that Grails embraces the philosophy of Convention over Configuration, making so many things simple, it is surprising that the column is defined improperly, even more so since the Grails documentation does not indicate that anything other than what was done above is needed. It turns out that simply adding the id property as a String solves the problem.
class Proposal {
    .
    .
    .
    static mapping = {
        id generator: 'uuid'
    }

    String id
    String title
    .
    .
    .
}

Turning my attention back to the domain model, I extend Proposal to define Presentation:
class Presentation extends Proposal {
    Date timeSlot
    Integer track
}

The same IllegalArgumentException is thrown, only pointing to Presentation.id rather than Proposal.id. That was really unexpected. Why the type was not inherited, I don't know, especially considering the default is to create a single table per hierarchy. At least it is obvious that all that needs to be done is to define the id property in Presentation.
class Presentation extends Proposal {
    String id
    Date timeSlot
    Integer track
}

Note that while writing this post I could not reproduce another problem I had encountered trying to implement the UUID generator involving many-to-many and one-to-many (as I recall) relationships. Perhaps that was due the sequencing of problem resolution or I am missing some subtlety that exists in my production code.

All these problems were discussed on Nabble. Definitely go there if you are having problems with many-to-many relationships when using the UUID generator. On this thread, Burt Beckwith explained the cause of the problems:
This is a problem with javassist, which replaced cglib as the default proxy provider in Hibernate 3.3. It's possible to use cglib in Hibernate (using hibernate.bytecode.provider=cglib) but Grails 1.1.1 ignores that and GroovyAwareSingleTableEntityPersister/GroovyAwareJoinedSubclassEntityPersister/GroovyAwareJavassistProxyFactory use GroovyAwareJavassistProxyFactory.
Posted by Bill Turner at 10:54 AM
Wednesday, 2 September 2009
MissingPropertyException after Grails 1.1.1 Upgrade
As part of the agreement to use the GR8 name for the Groovy/Grails conference we are organizing, we agreed to update the existing GR8 website to allow for multiple conferences. Shaun and I have been working on this and decided to upgrade the existing project to Grails 1.1.1. I then proceeded to update my other projects including the Teamwork project that is being built in conjunction with the detailed review I've been doing of Grails 1.1. Web Application Development. When I fired up that app and ran the following code:
def clearTags() {
tags.each {
it.delete()
}
tags = []
}
I received the exception groovy.lang.MissingPropertyException: No such property: delete for class: tagging.Tagger. After googling a few times and not getting satisfactory answers, I modified the above code to look like:
def clearTags() {
tags.each {
println "checking available methods on Tagger before assert"
println it.metaClass.methods*.name.sort().unique()
assert Tagger.exists(it.id)
println "checking available methods on Tagger after assert"
println it.metaClass.methods*.name.sort().unique()

it.delete()
}
tags = []
}
Two lines were added to dump the available methods before and after asserting that the particular Tagger object exists. The results demonstrated that the exception is highly misleading. It is telling us that there is a missing property, in this case the delete property, when, in fact, it is a missing method. Armed with this new found information, I was able to refine my google search and found that the bug has been addressed in the jira in thread http://jira.codehaus.org/browse/GRAILS-4580. The jira explains that this can happen when calling other methods, such as save, as well (the before and after method dumps demonstrates that there are many such methods that will cause the problem). Since the jira was not high enough in my google search results, perhaps this blog post will help someone else resolve the problem.

Obviously, the assert statement above is a workaround. A better workaround, recommended on the jira, amounts to:
import org.codehaus.groovy.grails.commons.ApplicationHolder

class BootStrap
{
def init = { servletContext ->
// workaround for GRAILS-4580
ApplicationHolder.application.domainClasses.each { dc ->
dc.clazz.count()
}
}
}

Posted by Bill Turner at 01:05 PM
Sunday, 16 August 2009
Book - Grails 1.1 Web Application Development - Part 11
Chapter 10 - Managing Content through Tagging

This chapter continues building the application by adding tagging capabilities. Achieving this requires that we learn how to work with inheritance in domain classes, understand the available GORM persistence strategies, and gain insight into polymorphic queries over domain inheritance hierarchies. We also need to use manage collections using collect and sort to get the behavior we want.

Initially we are given an overview of just what tagging is, and quickly move to a domain model needed to support tagging. After creating the initial model of Tag and Tagger, we create TagService. This provides methods for adding tags and is where we use the collect method. We are also shown the Elvis operator. A bit of trivia explains that the name for this operator comes from the fact that it looks Elvis' hair style. The author then has us write some integration tests to verify that all is working properly. We are required to implement further functionality in the Message class to make the tests pass.

After making changes and having the tests pass successfully, we turn our attention to tagging File classes. Here is where we learn GORM supports two of the several Hibernate inheritance persistence strategies, table-per-hierarchy and table-per-subclass. The former creates one table per hierarchy and is the Grails default. The latter creates one table for each class in the hierarchy. Table-per-hierarchy has a drawback; a number of columns will have to be nullable. To override the default behavior, we are told to add the mapping property to the parent domain as follows:
static mapping = { tablePerHierarchy false }
In order to implement tagging File objects, we re-examine the domain model and add a superclass to Message and File called Taggable. We then add another test and quickly discover that our tests fail. This is due to the fact that the polymorphic nature of our queries returns all Message and File objects with the given tag even though we think we are querying objects of the specific type. This is remedied by adding another method on the Taggable class, and adding nearly identical methods to Message and File that override the finder method on Taggable.

Once the domain and persistence logic is coded and tested, we move on to modifying the user interface. Jon discusses templating in Grails. In Grails, a template is the same as a GSP except that it is prefixed with and underscore. He shows the various ways templates are rendered and provides a warning regarding rendering a collection via the template. He states that we must be careful to pass in the actual collection by using the ${} notation.

As we closeout the chapter, we create another service, ContentService and make greater use of templating. The added templating allows either Message or File posts to be displayed interchangeably. These final touches allow us to create a homepage wherein the user can specify which tags are of interest. The view will display the five most recent updates, either messages or files, and the five most recent items of interest, again, either messages or files.

Throughout this chapter, there were few problems. In a few tests a user variable named fred is used that was not defined elsewhere, nor is the variable used in the book source. Again, as he has done in previous chapters, he references a view named post where none exists. And, he continues to unnecessarily use some optional things such as the access modifier public. These are all rather trivial issues.

Part 1 of this series can be seen here.
Part 2 of this series can be seen here.
Part 3 of this series can be seen here.
Part 4 of this series can be seen here.
Part 5 of this series can be seen here.
Part 6 of this series can be seen here.
Part 7 of this series can be seen here.
Part 8 of this series can be seen here.
Part 9 of this series can be seen here.
Part 10 of this series can be seen here.

Posted by Bill Turner at 08:39 PM
Friday, 31 July 2009
Book - Grails 1.1 Web Application Development - Part 9
Chapter 8 - More GORM and Criteria

Part 1 of this series can be seen here.
Part 2 of this series can be seen here.
Part 3 of this series can be seen here.
Part 4 of this series can be seen here.
Part 5 of this series can be seen here.
Part 6 of this series can be seen here.
Part 7 of this series can be seen here.
Part 8 of this series can be seen here.

Chapter 8 builds upon our application by adding file versioning capability to our file sharing system. This provides the excuse to revisit GORM and criteria queries as promised in an earlier chapter.

As we start the chapter, file sharing is modeled through two domains, File and FileData in a 1:1 relationship. Here we add FileVersion. FileVersion takes on all the responsibilities that File had, while File creates a hasMany property for the versions. In order to achieve the capability of retrieving the versions in the desired order, the author added a SortedSet property using the same name as the container specified in the hasMany map for the versions. In order to allow this ordering, FileVersion had to implement the Comparable interface and a compareTo method. Jon used the public access modifier on the compareTo method, which is unnecessary in Grails. It was nice having implementation of an ordered relationship between the two domains.

After the requisite changes and additions were made, File is 1:m to FileVersion, and FileVersion is 1:1 to FileData. With this change, it became necessary to change how data was retrieved on the home page. This lead to the discussion of criteria queries. The first thing the author did was to show three dynamic queries from chapter 5 and the equivalent versions written using criteria. This clearly demonstrated how criteria queries are built. He then explains that nodes within the criteria can be used to specify logical operators and to query across associations. For the latter, a property added to File meant to hold a reference to the current FileVersion was used in demonstration. He also discussed the use of FetchMode as we still want to retrieve the User eagerly from the current version. After providing two tables, one a criteria reference, the other a listing of logical criteria operators, we are shown the use of criteria properties, in this case, limiting the results of our File query to ten. He closes the discussion of criteria queries by providing links to the Hibernate Criteria Javadoc, and informing us of and providing links to documentation on Projections and Scrollable Results.

At this point, we are directed to modify the save method in the FileController. Here we run into a problem. The code listing on page 160 meant to display the errors does not work. In the listing, a view of post is referenced. No post view has been created thus far, nor is it contained in the downloaded source. The downloaded source references create as expected. Worse, though, is that by changing the model here from file to currentVersion breaks causes no messages to be displayed on an upload error. The problem is that the save is done on the File object and the Error object is bound to that, not to currentVersion. There are multiple solutions to this. One possible solution is to add a line just before the render to set the current version error object to the file error object. This is shown below.
if (file.save()) {
flash.userMessage = "File [${currentVersion.name}] has been uploaded."
redirect(controller: 'home')
}
else {
currentVersion.errors = file.errors
render(view: 'create', model: [file: currentVersion])
}

However, this smells to me. Rather, the preferred change in my opinion is to the create.gsp. Here the code currently reads:
< g:hasErrors bean="${file}" >
< div class="validationerror" >
< g:renderErrors bean="${file}" as="list" / >
< /div >
< /g:hasErrors >

Change this to:
< g:hasErrors >
< div class="validationerror" >
< g:renderErrors as="list" / >
< /div >
< /g:hasErrors >

After writing the rather complex save logic in the controller, Jon tells us that he understands it is complex and difficult to test. He states that we will be moving much of this logic to a service in the chapter.

The chapter is closes with the required changes to our home page and message properties, and by updating the download method in the FileController.

I have to admit to being a bit frustrated by the errors encountered, though most books do seem to have some problems. None encountered so far have been insurmountable to me, but a noob may be really dazed and confused. Then again, problem solving is a great way to learn. If you feel that way, I suggest you download the free version of Jason Rudolph's mini-book Getting Started with Grails available at InfoQ, which was based on very early version of Grails and Groovy.
Posted by Bill Turner at 08:53 PM
Wednesday, 15 July 2009
Book - Grails 1.1 Web Application Development - Part 6
Chapter 5 - Authentication with JSecurity Plug-in

Part 1 of this series can be seen here.
Part 2 of this series can be seen here.
Part 3 of this series can be seen here.
Part 4 of this series can be seen here.
Part 5 of this series can be seen here.

Many applications require security. Luckily, through Grails plug-in architecture and a number of available plug-ins devoted to the task, adding security a Grails application is relatively easy. In this chapter, Jon takes us through implementation of many normally encountered requirements using the JSecurity plug-in. I have only used the Spring Security (Acegi) plug-in prior to this, so I really looked forward to the experience (I have read about JSecurity and CAS previously). My impression after this chapter is that implementing JSecurity via the plug-in is a fairly simple process and is one I'll strongly consider on future projects. The only caveat to this is the same complaint I've had of features the book covered earlier, namely the decision to "roll your own" rather than using the supplied commands to create artifacts and teaching and modifying those. In the end, I am not sure what it is I don't know and will be forced into digging into the features of the plug-in before I use it. Certainly, this being a tutorial, a few additional pages covering the plug-in in greater detail does not seem unwarranted.

The chapter starts off introducing us to the fact that there is a plug-in mechanism and tells us of several of the many areas for which plug-ins have been written. Jon explains that plug-ins can generate a number of artifacts, from classes to static resources, and can take part in build and runtime events. Further, he states that the core features of Grails are implemented as plug-ins, thus making the plug-in architecture fundamental to Grails, and provides the sample of GORM as a core plug-in, the plug-in used to abstract Hibernate.

It seems that the reason JSecurity was used in the project is the ease with which it could be integrated with the existing domain model. This proved to be true, in my opinion.

Jon explained that plug-ins are installed in a default directory, your user account. He then states that this gives rise to a number problems and provides two:
  • Other users may not be able to execute the plug-in.
  • If the name of the project is changed, you must remember to dig around in your user directory to change the installation location of the plug-in.
I am not sure that these are truly serious concerns. Perhaps they are on larger projects than those on which I've worked. He then tells us how to override the default plug-in location, putting it into another location. Prior to Grails 1.1, the default location was within the project tree. With the change, a number of people had problems and one of the common solutions suggested on the mailing list was to do what Jon demonstrates here, except to use the path as had been used in earlier releases. I personally now just use the default. However, I followed Jon's instructions. What I found, and it could be a user error, is that my editor, Intellij IDEA, did not recognize the path specified in the BuildConfig.groovy file causing a cannot resolve symbol JSecurity error when I created the TeamworkRealm.groovy file. This error is specific to the browser. There were no problems building the project outside of IDEA. I did try many possible solutions, and think I covered them all, but no "solution" worked. So, I just ended up installing the plug-in into the default directory. But, I digress.

He went on to explain more about the structure of plug-ins, which is certainly nice to know information. He then tells us of the scripts the JSecurity plug-in makes available to the developer. However, we are then told that one will not be used because the default domain model generated is more complicated than the simple model used by the application. We do eventually use the CreateAuthController script to generate the login action and page.

We are then provided a quick overview of JSecurity, covering the role, permissions, subject, principal and realm concepts. Permissions are not used in the application. Realms are delved in more deeply than the other subjects, and then we go on to creating our own realm. There are a couple scripts supplied by JSecurity for creating realms, CreateDbRealm and CreateLdapRealm, that are not used. What these create should be self evident from their names.

Next, we go on to implementing the authenticate method in the realm. This gives rise to a discussion of dynamic finders. This is covered relatively sufficiently. Explaining how the term dynamic is derived from dynamically constructed method names rather than the fact that the methods are added to the domains dynamically, he covers all the operators that can be used when constructing a name. He rightly implies that the junction operators And and Or are limited to two conditions. This could easily be missed by the noob and gives rise to one of my complaints about Grails. For example, you may write a finder such as Book.findAllByAuthorLastNameAndPublicationDate(name, date). This is perfectly legitimate. When you try running your code with finder Book.findAllByAuthorLastNameAndPublicationDateAndBinding(name, date, binding), you'll find that it fails and will have a large stack trace on the console. You cannot have three conditions. This violates the principle that you should always write your code so that someone else has a clear mental model of what to expect. Unless you've read the one line in the Grails document that points this out or you've properly inferred from other writings, and it has stuck with you, you will run into this error. Perhaps this is a problem with the underlying Hibernate implementation. Either way, it is wrong. Never make people guess. I digress again.

From here we are introduced to the use of criteria for more complex queries. This is barely touched upon, but we are promised that we will get a deeper introduction in a later chapter.

Then come the concepts of filters. These are similar to filters in Java Servlets. They are quick and easy to implement. And, soon, we have our first filters written.

Because the authentication is in place, we are not able to log into our application. There are no users! So, Jon takes us through adding users via our bootstrap class and we are introduced to the encryption function provided by the plug-in. This gives us the required background for encrypting the password when the user is created or updated. Jon did teach a trick here of which I was unaware. He updated the password field after calling hasErrors and save. This takes advantage of the Open Session in View pattern for hibernate. This means that changes to your object will not be persisted until all server-side operations have finished.

Finally, we add some other features to our application: an improved permission denied page, a sign out link, and displaying the author of our messages. In the latter, we are introduced to services briefly and told we will learn more about services in an upcoming chapter. We are told that Grails supports the concept of a service layer, though little more. We are also required to relate messages to users. This was done simply, but Jon does not use belongsTo or hasMany properties to do so. We are also introduced to Hibernate's lazy loading feature, the n + 1 problem to which this gives rise, Hibernate fetch strategies, and using the eager strategy to resolve the n + 1 problem.

One error was found in the code listings in the chapter on page 100. It is fairly minor. The author wrote Sha1Hash(user.password).toHex() and it should read Sha1Hash(userInstance.password).toHex(). I've submitted an error report to the publisher. Hopefully it will show up on the errata soon. Similarly, there was an instance where he used the word principle rather than principal on page 89 in the text that was not listed in the errata. I have not submitted this and likely will not.
Posted by Bill Turner at 04:33 PM
Thursday, 28 May 2009
Grails Persistence with GORM and GSQL
Being a fellow member of Groovy Users of Minnesota (GUM), I was keeping an eye on the publication of Grails Persistence with GORM and GSQL by Robert Fischer. Being that so much of Grails development revolves around domains, it seemed that a book on GORM would prove beneficial. This seems even more true now that GORM is a stand alone product. I highly recommend this book! When I first glanced through it, I thought I had wasted my time. After all, I had been coding in the Grails environment for several months and had dealt with many problems, most of which were due to an incomplete comprehension of GORM. I had spent a lot of time asking questions on the GUM mailing list and on Nabble. So, it seemed that I had a pretty good handle on GORM by the time it arrived. I read it anyway. Within the first thirty pages or so, I called a friend and told him he had to read it! While I knew much, there were many subtleties he explained and that was more true the further I went into the book. I am so enthused by this book that I recommended it to just about everyone I spoke with at GR8 in Copenhagen.

Okay, so no review is honest without some criticisms. I have a few, and all are minor.

My first criticism is that I felt a bit lost at times when trying to understand what was happening. I was trying to visualize the actual db tables, etc. Perhaps that was the wrong way to think about it, but I think understandable considering my datamodeling and sql background. It might've been nice to throw in a few code samples of the resulting table structures. I no longer remember where this seemed most appropriate.

A second criticism, and this probably is out of the author's control, is that there does not seem to be any accompanying source code distribution. At least I was unable to find it at the Apress site. At those points where I was a bit more confused, having the code available so I could start up my IDE and play around a bit sure would have been nice. It would save me from hours of tedious test case writing.

Anothor criticism, and this too could be out of the author's control, is that there is no index. Being that the cover states that Robert wants this book to be a go-to reference, I feel it is essential. Yes, the book is small, so relatively easy to find what you are looking for, but an index makes it even quicker (searching an electronic version would be even faster, so maybe this is my fault for purchasing the dead tree version). Your book will undoubtedly be heavily used as a reference by me regardless.

My final criticism is surely out of the author's control. I could have used the book much sooner. I've been able to purchase EAPs from Manning and get the dead-tree once published. The fee being no different than the price for the paper version only. I would've done that with this book if APress had that as an option.

Robert reply to my criticisms, sent in an email, follows.

All the criticisms are valid, and I agree with you 100%. APress has the option for an updated/expanded second edition, which I'm trying to push for and which would include an index, forum, source code distribution, and more content in general. Personally, I think I shafted the last two chapters pretty bad, plus I wanted to talk about plugins in the database arena: I was capped at 150 pages of core content, though, and I went right up to it (edits brought it down to 148).

The database tables I intentionally side-stepped for two reasons. The first is space, but the second is that different database engines produce different SQL, and so I felt like I'd have to share SQL from a variety of databases, which would add a lot of effort, a lot of length, and. If the expanded version comes out, do you think a version with only postgres SQL code would be acceptable?


Posted by Bill Turner at 12:00 AM
Friday, 6 February 2009
Domain Integration Testing Getting Validation Errors
In my previous post, I complained of having errors where the messages were meaningless to me, even pointing in the wrong direction entirely. I've run into this more in my domain persistence tests than anywhere else. Like a good little Test Driven Developer, I generate my domain, then start updating the integration test before adding attributes or anything else to the domain. My persistence test will look something like:

new FirmDetail(name: "Sagman, Bennet, Robbins, Oppenheim and Taft", cumulativeHoursTrigger: 100.0, periodHoursTrigger: 10.0).save()
new FirmDetail(name: "Meshbesher and Spence", cumulativeHoursTrigger: 150.0, periodHoursTrigger: 25.0).save()
new FirmDetail(name: "Dewey, Cheatum and Howe", cumulativeHoursTrigger: 500.0, periodHoursTrigger: 100.0).save()

assert 3 == FirmDetail.count()

Somewhere in the test/code loop, I will suddenly see an error that states:

java.lang.AssertionError: Expression: (3 == FirmDetail.count()).

A failure I can understand, but an error, no. Seeing this frequently enough, I realize that my object probably was not saved. The reason for not saving is not obvious. The stacktrace provides no answers. Well, after RTFMing and googling a bit, I came up with a better way to write the persistence test that will tell me when the object is not saved and the reason for it:

FirmDetail firmDetail
if (!(firmDetail = new FirmDetail(name: "Sagman, Bennet, Robbins, Oppenheim and Taft", cumulativeHoursTrigger: 100.0)).save()) {
println "Object not saved. Errors:"
firmDetail.errors.allErrors.each {
println it
}
}

new FirmDetail(name: "Meshbesher and Spence", cumulativeHoursTrigger: 150.0, periodHoursTrigger: 25.0).save()
new FirmDetail(name: "Dewey, Cheatum and Howe", cumulativeHoursTrigger: 500.0, periodHoursTrigger: 100.0).save()

assert 3 == FirmDetail.count()

The if statement against the save of the first object will cause sysout to be written that can be viewed from test results html of the failing case via the System.out link at the bottom of the page. The above test was written to fail for this example. Viewing the link shows:
--Output from testPersist--
Object not saved. Errors:
Field error in object 'FirmDetail' on field 'periodHoursTrigger':
<snip>
--Output from testToString--


This probably should be written to use the fail method rather than a write to sysout. I'll leave that for the future.



Posted by Bill Turner at 08:01 AM
Friday, 30 January 2009
Handling Db Criteria That Differs Across Domains
In my previous post, I spoke of having forgotten to change the test environment settings when I configured for SQL Server 2005. There really was another solution. The out of the box definition of DataSource.groovy defines the driverClassName, username and password in the outermost dataSource closure. Any or all of these attributes can be moved into the various environment dataSource closures. By doing this, you can have different logins/passwords per environment, or even have different databases, if you wish (though I cannot imagine why you would want your database to differ across environments).
Posted by Bill Turner at 08:56 AM

Installing and Configuring SQL Server 2005 in Groovy
This was a relatively simple task to do, though I ran into one gotcha (mainly because I was not paying attention). Here are the steps followed:

1) Created database MyDb using MS SQL Server Management Studio Express

2) Added user MyUser and defined a password. Roles were specified as: db_datareader, db_datawriter, db_ddladmin. The first two allow reading and updating of data. The last allows creation of tables and other DDL commands.

3) Downloaded jdbc driver. http://www.microsoft.com/downloads/details.aspx?FamilyId=C47053EB-3B64-4794-950D-81E1EC91C1BA&displaylang=en

4) After executing the archive and extracting to the drive, copy sqljdbc.jar to [APPLICATION_HOME]/lib.

Changed [APPLICATION_HOME]/grails-app/conf/DataSource.groovy as follows:

1) Set dataSource pooled to false

2) Set dataSource driverClassName to "com.microsoft.sqlserver.jdbc.SQLServerDriver"

3) Set dataSource username

4) Set dataSource password

5) Set environments.development.dataSource dbCreate to "update"

6) Set environments.development.dataSource url to "jdbc:sqlserver://localhost:5356;DatabaseName=MyDb"

The port in the url defaults to 1433. I am using a non-standard port number.

I created a domain with no implementation and defined an integration test. Here is the gotcha. When I ran the integration test, it dumped for not being able to find a suitable driver. I had only changed the development dataSource in DataSource.groovy. Once I updated the test dataSource, the tests ran properly.


Posted by Bill Turner at 08:42 AM
sun mon tue wed thu fri sat
    1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30   

Latest Posts
Archives
Categories
Bookmarks
Authors
Search
Syndicate This Site
Add to Technorati Favorites