Reputation: 1231
in the grails/gorms docs it says you can put the embedded class in the same domain class file as the top level parent domain class - this works from code perspective but it still generates a GeoAddress table, as well as embedding the columns into the source Venue table. test data is input in venue - geoAddress table stays empty.
documentation implies this embedded table shouldnt be generated. I can try and move the GeoAddress into its own src/groovy file, so its out of the grails-app/domain folder, but then i have to 'remember i have done this'. it would be much 'cleaner' to keep in same file as the containing class.
other than promoting the GeoAddress back to full domain class in its own right - how can i tell gorm not to generate the table for it when its use is embedded ?
my venue.groovy in grails-app/domain folder
class Venue {
String name
LocalDate dateCreated
LocalDate lastVisited
LocalDate lastUpdated
GeoAddress location
Collection posts
static hasMany = [posts:Post]
static embedded =['location']
static constraints = {
lastVisited nullable:true
location nullable:true, unique:true
posts nullable:true
}
static mapping = {
location cascade: "all-delete-orphan", lazy:false, unique:true
posts sorted: "desc", cascade:"save-update"
}
}
class GeoAddress {
String addressLine1
String addressLine2
String addressLine3
String town
String county
String country = "UK"
String postcode
//adds addTo/removeFrom methods to venue
static belongsTo = Venue
static constraints = {
addressLine1 nullable:true
addressLine2 nullable:true
addressLine3 nullable:true
town nullable:true
county nullable:true
country nullable:true
postcode nullable:true
}
}
Upvotes: 0
Views: 626
Reputation: 1231
I appear to have put postings on what is essentially same problem - careless. See also Can't get cascade save nor delete on embedded class ref to work
However to reprise: having the embedded class sitting below the main class in the grails-app/domain folders does not stop the embedded class from being generated (as it implies in the docs).
So Gorm goes ahead and creates you a Venue and a GeoLocation (my example here). However the usage is not what I expected either, which is why the test failed.
If you add the new GeoLocation as constructor arg to the venue, the transient instance is saved in the venue table in the embedded location columns in same table. If you delete the venue the entire row is deleted from venue table.
However, if you create a GeoLoaction first and save()
it - it's actually stored in the GeoLocation table instead. Then adding it to the venue updates the columns the venue tables.
So when the test does a GeoLocation.list()
/get()
, it finds the row in that table rather than looking in the venue embedded columns.
I can't find a nice way out of this. The only way to stop geoLocation going into the DB is to move the the definition out of domain folders and put into src/groovy. But then your domain model is split across two subtrees, and getting the gorm validations to fire outside of the domains folder is more fiddly to achieve.
I think I have just had to accept using GeoLocation as full domain class rather than use embedded - and accept the join cost. At least that way the model/tests work as you might expect.
The outcome is: I am not sure embedded was a wise modelling decision and I'd recommend not using unless you really have to.
Upvotes: 0
Reputation: 1231
ok - i did find a way to use the geoLocation as embedded and stop it creating a table .
I had to setup GeoLoction as static inner class in side venue. When you do that and run the app you dont get the extra spurious domain table in in the DB. It has to be static inner class as the GeoLocation has a static declaration for constraints.
I'm not sure if this is a bug or what - but the grails documentation is incorrect in the suggestion in section 5.2 - composition in gorm, which says
If you define the Address class in a separate Groovy file in the grails-app/domain directory you will also get an address table. If you don’t want this to happen use Groovy’s ability to define multiple classes per file and include the Address class below the Person class in the grails-app/domain/Person.groovy file
modified class shown below. when you want to create one of these the format (for bootstrap/tests ) would look like new Venue.GeoLocation (...)
class Venue implements Serializable {
String name
LocalDate dateCreated
LocalDate lastVisited
LocalDate lastUpdated
GeoAddress location
Collection posts
//static hasOne = [location:GeoAddress] //, temp:TempLocation
static hasMany = [posts:Post] //but doesn't really own thats with user
static embedded =['location']
static constraints = {
lastVisited nullable:true
location nullable:true, unique:true
posts nullable:true
}
static mapping = {
location cascade: "all-delete-orphan", lazy:false, unique:true //eager fetch strategy
posts sorted: "desc", cascade:"save-update"
//comment out for now
//posts joinTable: [name:"venue_posts", key:"venue_id", column:"posts", type:Post]
}
static class GeoAddress {
String addressLine1
String addressLine2
String addressLine3
String town
String county
String country = "UK"
String postcode
//adds addTo/removeFrom methods to venue
static belongsTo = Venue
static constraints = {
addressLine1 nullable:true
addressLine2 nullable:true
addressLine3 nullable:true
town nullable:true
county nullable:true
country nullable:true
postcode nullable:true,
matches: /^([gG][iI][rR] {0,}0[aA]{2})|([a-pr-uwyzA-PR-UWYZ](([0-9](([0-9]|[a-hjkstuwA-HJKSTUW])?)?)|([a-hk-yA-HK-Y][0-9]([0-9]|[abehmnprvwxyABEHMNPRVWXY])?)) ?[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2})$/
}
}
}
Upvotes: 1
Reputation: 126
Grails table generation base on Class name, try put embedded as Collection rather than Class.
Upvotes: 0