I'm wondering if its possible to create a grails domain object, but only have it persist on command as opposed as when we do operations on it.
To be more precise, this is what I have to do now:
Foo foo = new Foo(name:"asdf")
Bar bar = new Bar(name:"gzxj")
bar.save() // persist here
foo.save() // persist here
foo.addToBars(bar) // now I have to build the relationship
What I want:
Foo foo = new Foo(name:"asdf")
Bar bar = new Bar(name:"gzxj")
foo.addToBars(bar) // just build the relationship
bar.save() // would be great to ignore this step
foo.save() // can I now atomically build both objects and create the relationships?
My impressions is that the latter would be far faster if there are many relationships to associate. Am I really just wanting NoSQL here?
Depending on how you have your relationships set up, this is entirely possible. It really has nothing to do with what database you have implemented.
Parent class
class Foo {
String name
static hasMany = [bars: Bar]
}
Child class
class Bar {
String name
Foo foo //optional back reference
static belongsTo = Foo
}
Execution
Foo foo = new Foo(name: 'a')
Bar bar = new Bar(name: 'b')
foo.addToBars(bar)
foo.save()
//or even more terse
Foo foo = new Foo(name: 'c')
foo.addToBars(new Bar(name: 'd'))
foo.save()
The key is the belongsTo which defaults to a cascade of all. This can be explicitly set as well:
class Foo {
String name
static hasMany = [bars: Bar]
static mapping = {
bars cascade: 'all'
}
}
Related
I'm new to grails and must be missing something important in trying to define a one-to-many mapping without using a join-table.
I have a domain object Foo
class Foo {
static hasMany = [remarks: ChangeRemark]
static mapping = {
remarks column:'PART_REMARKS_ID', joinTable:false
}
}
and another domain object ChangeRemark
class ChangeRemark {
String remark
String name
}
I initialize a Foo object, and add two ChangeRemark objects to it as such
ChangeRemark remark = new ChangeRemark(remark:"test", name:"t").save(flush:true)
ChangeRemark remark2 = new ChangeRemark(remark:"test2", name:"y").save(flush:true)
Foo foo = new Foo().save(flush:true)
foo.addToRemarks(remark).save(flush:true)
foo.addToRemarks(remark2).save(flush:true)
When I print out the size of the remarks on foo it is indeed 2, however the database column for the foreign key PART_REMARKS_ID on the ChangeRemarks table is empty. This means after I restart my application the connection between the object and the remarks can't be made
What am I missing to have grails insert the Foo id in the table.
To avoid the join table use belongsTo to create a reference from ChangeRemark to Foo.
class Foo {
static hasMany = [remarks: ChangeRemark]
}
class ChangeRemark {
String remark
String name
static belongsTo = [foo: Foo]
}
When you do this, the change_remark table gets a foo_id foreign key. This means a ChangeRemark instance can reference only a single Foo instance; which I'm assuming is your intention.
This changes how you add ChangeRemarks:
ChangeRemark remark = new ChangeRemark(remark:"test", name:"t")
ChangeRemark remark2 = new ChangeRemark(remark:"test2", name:"y")
Foo foo = new Foo()
foo.addToRemarks(remark)
foo.addToRemarks(remark2)
foo.save(flush: true)
Basically, Foo is responsible for persisting itself and the ChangeRemarks.
Suppose I have the following:
class ObjectA implements Serializable {
Foo foo
String objectType
static mapping = {
version false
id composite: ['foo', 'objectType']
foo column: 'foo'
objectType column: 'objecttype'
}
}
Now, I need to reference this object from another Domain.
class ObjectB {
ObjectA objectA
columns {
objectA{
column name: 'foo'
column name: 'objecttype'
}
}
}
When I try and load an ObjectB, I get the following:
Cannot treat multi-column property as a single-column property
How should I be mapping this?
What are you attempting to do with the columns section, I only ask because I have am unfamiliar with that concept? This stack overflow link may help.
But, as show in link, if you want to reference the object you should create a relationship (the one in the example is unidirectional) between the two domains.
class Object B {
static hasOne[objectA:ObjectA]
....
}
There really isn't much more to it then that. You can access the fields from objectA within you views just by using ObjectBInstance.objectA.channelName.
FYI, feel free to suggest a better title for this. I have the following domain model (we have no control over this):
class Foo {
int id
String baz
Date someDate
static hasMany = [bars:Bar]
static mapping = {
id composite: [ "id", "baz" ]
}
}
class Bar {
int id
String baz
Date someDate
Foo foo
static mapping = {
id composite: ["id", "baz", "someDate"]
columns {
foo([:]) {
column name: "id"
column name: "baz"
}
}
}
}
My problem is: I have a List of Foos and I need to find all Bars where Foo.someDate == Bar.someDate, but only in one query instead of a query for each Foo, with no lazy loading involved. Also setting Bars to eager fetching is not an option.
For example if this is my data (pseudocode, I'm combining id and baz into simply "id" for simplicity):
[
Foo (someDate:4/1/2013)
|___ bars: [{someDate:12/4/2012, id:1}, {someDate:4/1/2013, id:2}]
Foo (someDate:5/10/2012)
|___ bars: [{someDate:{4/1/2013, id:3}
Foo (someDate:3/3/2013)
|___ bars: [{someDate:3/3/2013, id:4}, {someDate:9/5/2013, id:5}]
]
I need to return the Bars with id 2 and 4 in one query. I'm really not sure how to approach this.
It could be HQL if necessary but it's preferred to be a GORM query.
Try this:
Bar.executeQuery("select b from Bar b join b.foo as f where b.someDate = f.someDate and f in :fooList", [fooList: fooList])
Not sure if it is the best way to do so, but I think it will give you the result
:)
Bar.executeQuery("select b.id from Bar b, Foo f where b.someDate = f.someDate")
although I have removed your composite mapping constraints and then tried it, worked for me :)
or using where query:
def km = Bar.where {
foo.someDate==someDate
}
println(km.list())
If you already have foo list then can be filtered through findAll closure:
fooList.findAll{it.someDate in it.bars.someDate}
:)
What are the benefits of using the grails insert flag when saving a domain class?
Here is an example:
Lets say I have a Domain Object FooBar:
FooBar foo = FooBar.find("foo")?: new FooBar(id:"foo")
foo.bar = "bar"
foo.save()
Would it be better do do something more like this:
boolean insertFlag
FooBar foo = FooBar.find("foo")
if(foo == null){
insertFlag = false
}else {
foo = new FooBar(id:"foo")
insertFlag = true
}
foo.bar = "bar"
foo.save(insert: insertFlag)
I was thinking that the save would run smoother somehow with the insert flag verses not having it.
insert inside save is highly useful if you have the id generator for a domain class as assigned. In that case the id has to be assigned by the user.
This is a way to inform hibernate that whether you want to insert a record or just want to update.
class FoofBar{
String bar
static mapping = {
id generator: 'assigned'
}
}
def fooBar = new FooBar(bar: 'foo')
fooBar.id = 100
fooBar.save() //inserts a record with id = 100
def secondFooBar = FooBar.get(100)
secondFooBar.id = 200
//want to insert as a new row instead of updating the old one.
//This forces hibernate to use the new assigned id
fooBar.save(insert: true)
This will make it clear.
I am using Grails 1.3.6 and I am having problems getting saves to cascade properly. The problem is specifically with classes that have two parent classes. A simplified version of what I am attempting is this:
class Location {
String city
static hasMany = [authors: Author, publishers: Publisher]
}
class Author {
String name
static belongsTo = [location: Location]
static hasMany = [books: Book]
}
class Publisher {
String name
static belongsTo = [location: Location]
static hasMany = [books: Book]
}
class Book {
String title
static belongsTo = [author: Author, publisher: Publisher]
}
class Srv1Service {
static transactional = true
def loadData() {
def l1 = new Location(city: "London")
def a1 = new Author(name: "Graham Greene")
l1.addToAuthors(a1)
def p1 = new Publisher(name: "Some Press")
l1.addToPublishers(p1)
def b1 = new Book(title: "The Comedians")
a1.addToBooks(b1)
p1.addToBooks(b1)
l1.save()
}
}
If I run the above loadData, the Book instance is saved before the Publisher instance, resulting in the error "not-null property references a null or transient value: adhoc.Book.publisher".
I have tried various different ways of defining the relationships with little success. I have tried interim saves, and this does work, but I can see that parent tables are updated as I save the child data - ie Location, Author and Publisher all get updated to version 1. (And also I would like to keep the code as simple as I can.) I would like to avoid linking tables.
Any advice is gratefully received!
Okay, so the key here is that saves are cascaded from parent to children. You have a problem when it comes to Book because Book is the child to both Publisher and Author. GORM tries to save Location, Location tries to save Author, Author tries to save Book BUT the save fails because Book has a transient Publisher.
Try adding an intermediate save right before you create your Book:
def loadData() {
def l1 = new Location(city: "London")
def a1 = new Author(name: "Graham Greene")
l1.addToAuthors(a1)
def p1 = new Publisher(name: "Some Press")
l1.addToPublishers(p1)
l1.save() // add this save
def b1 = new Book(title: "The Comedians")
a1.addToBooks(b1)
p1.addToBooks(b1)
l1.save()
}
I created a local Grails project with your domain classes adding in this save. The cascading is working as you would expect.