class Book {
String title
Date releaseDate
String ISBN
static belongsTo = [person:Person] // it makes relationship bi-directional regarding the grails-docs
}
class Person {
Book book; // it will create person.book_id
String name
Integer age
Date lastVisit
static constraints = {
book unique: true // "one-to-one". Without that = "Many-to-one".
}
}
There is a test which test if it is real bidirectional or not. As i understand it.
public void testBidirectional() {
def person = new Person(name:"person_c1", age: 99, lastVisit: new Date())
def book = new Book(
title:"somebook_c1",
ISBN: "somebook_c1",
releaseDate: new Date()
)
person.setBook (book)
assertNotNull(person.save())
def bookId = person.getBook().id
Book thatBook = Book.get(bookId)
assertNotNull(thatBook.person) // NULL !!!
}
So, i save a person with a book, and then i got that book from db by id. Then from that book i try to get back the person which book should refer to (because it should be bidirectional, right?). Eventually i got null instead of an instance of the person.
The questing is: how to make that test working?
i have found the solution how to get it working, but still can not understand why it does not work without 'refresh', see below:
public void testBidirectional() {
def person = new Person(name:"person_c1", age: 99, lastVisit: new Date())
def book = new Book(
title:"somebook_c1",
ISBN: "somebook_c1",
releaseDate: new Date()
)
person.setBook (book)
def p = person.save()
assertNotNull p
person.refresh() //load the object again from the database so all the changes made to object will be reverted
//person = Person.get(p.id) // BUT this also gets the object from db ...?
def bookId = person.getBook().id
assertNotNull bookId
def thatBook = Book.get(bookId)
assertNotNull(thatBook.person)
}
So, here as you can see i use 'refresh' to get it working, but why it does not work without 'refresh' but with the following line after 'refresh' - this one:
person = Person.get(p.id) // BUT this also gets the object from db ...?
If i just want to get object from database by id, then it would be without bidirectional?
Your problem is probably caused by the way that Hibernate works. Grails used Hibernate under the hood.
Even when you call "save", the object person may (and usually) not saved in database. That's because Hibernate is programmed to optimize the query, so it often waits to perform all query at then end of the Hibernate session.
That means if you don't call "refresh", the book-person relation (person.setBook) is still in memory, but not saved in database. Hence you can't get the book.person from book.
To enforce the save, you can use "refresh" like the previous answer, or use flush:true.
I still not try, but it's very likely that you will produce desired results with:
person.save(flush:true)
Related
I have tow domain
class Book {
String name
Long id
}
class BookRef {
String name
Long id
String refId
}
In Book table, I stored data like id=1, name='Java'.
Now I can initiate BookRef by getting Book like bellow
def book = Book.get(1)
def bookref = new BookRef()
bookref.id = book.id
bookref.name = book.name
bookref.refId = '1'
bookref.save()
But I want to initiate the bookref object by using the book object like params binding not by binding each individual properties.
For now, I have now used the property to property initialization but in a domain with various properties, it's time-consuming.
How can I do this?
Here you go:
Book book = Book.get(1)
// Option 1
BookRef bookref = new BookRef(book.properties)
bookref.refId = '1'
bookref.save()
// Option 2
BookRef bookref = new BookRef()
bookref.properties = book.properties
bookref.refId = '1'
bookref.save()
Basically, in Grails, any domainInstance.properties gives you a map of all the domain fields.
Please note, this approach is good for primitive types like String, boolean, int, Long etc but Grails might throw an exception when the domain has collections, one-to-many or has-many relationship fields.
I have problem for updating domain that has many-to-many relation. For instance, consider these 2 simple domains.
class Student {
String name
static hasMany = [courses: Course]
}
class Course {
String name
static hasMany = [students: Student]
static belongsTo = [Student]
}
To update student's name along with his/her courses' name, I use data binding like this:
def params = [
'courses[0].id': c2.id,
'courses[0].name': 'c11',
'courses[1].id': c1.id,
'courses[1].name': 'c22'
]
s1.properties = params
s1.save(flush: true)
However, this will cause error:
org.springframework.beans.InvalidPropertyException: Invalid property 'courses[1]' of bean class [tb.Student]:
Invalid list index in property path 'courses[1]'; nested exception is java.lang.IndexOutOfBoundsException:
Index: 1, Size: 1
After some searching, I found that all answers suggest to use List for relation instead of Set. However, I still prefer to use Set.
Environment
Grails: 2.2.3
Java: 1.6.0_45
OS: Ubuntu 13.04
My solution is to clear the children list before data binding. This is the full code to test above domains. The line s1.courses.clear() will prevent above error.
def s1 = new Student(name: 's1').save(flush: true)
def s2 = new Student(name: 's2').save(flush: true)
def c1 = new Course(name: 'c1').save(flush: true)
def c2 = new Course(name: 'c2').save(flush: true)
s1.addToCourses(c1)
s1.addToCourses(c2)
s1.save(flush: true)
def params = [
'courses[0].id': c2.id,
'courses[0].name': 'c11',
'courses[1].id': c1.id,
'courses[1].name': 'c22'
]
s1.courses.clear()
s1.properties = params
s1.save(flush: true)
However, I still think this problem is a bug. And my solution is a work around.
A Set isn't ordered hence it will fail if you specify an index.
If you don't want to use List, try to use SortedSet instead.
You can find more info # (http://grails.org/doc/latest/guide/single.html#ormdsl) 6.5.3 Default Sort Order
I'm using Entity Framework 4.1
I have a "DomainEntities" table that holds the common info for all my domain entities.
I have a users table the the UserID is a Foreign Key from "DomainEntities".
see EDMX:
When I run the following code i get an error:
Unable to determine a valid ordering for dependent operations.
Dependencies may exist due to foreign key constraints, model
requirements, or store-generated values.
The code:
static void addUserTest()
{
DomainEntity userToAdd = new DomainEntity()
{
EntityName = "Test User",
EntityTypeID = DomainEntity.eEntityType.User,
EntityCreationDate = new DateTime(),
EntityLastUpdateDate = new DateTime(),
EntityCreatorUserID = 0,
EntityUpdaterUserID = 0,
EntityParentID = null,
UserDetails = new User()
{
Username = "TestUser",
Password = "123",
FirstName = "Test",
LastName = "User"
}
};
using (var context = new CamelotDB())
{
context.DomainEntities.Add(userToAdd);
context.SaveChanges();
}
}
I cant understand what is the reason that EF can understand what is the INSERT order required,
It should be One record into "DomainEntities" and then one record into "Users".
What am I doing wrong ?
After searching for one more day I found it the problem was with the Creator and Updater self referenced foreign keys.
CreatorID is not Nullable so does UpdaterID and this is why EF requires the navigation properties to point to actual entities from the database so i added the following lines in the initializer of Test User.
EntityCreatorUserID = 0,
Creator = context.DomainEntities.Find(0),
EntityUpdaterUserID = 0,
Updater = context.DomainEntities.Find(0),
It seems that instead of having your User be related to your DomainEntity, you should make your User a subclass of DomainEntity. In the Entity Model designer, this is done by using the Inheritance tool (Double-click the Inheritance tool in the toolbox, then click once on the parent entity and once on the child entity.)
This more accurately describes the nature of a User; a User is a DomainEntity. Your current model, suggests that a User is related to a DomainEntity, which doesn't seem right.
I have a legacy database and I'm using Grails. Mistake #1.
It uses compound keys. Mistake #2.
Given these domain classes:
Movie {
static hasMany = [ roles: Role ]
}
Person {
static hasMany = [ roles: Role ]
}
Role {
Movie movie
Person person
String foo
}
I want to move roles from one person to another, like so:
Role x = person1.roles[0]
x.person = person2
save(flush:true)
But nothing happens. At all. I've turned on trace and debug level logging for hibernate in log4j and it shows no update statement. But, if I do this:
Role x = person1.roles[0]
x.person = person2
x.foo = "i can haz update?"
save(flush:true)
An update does happen for foo, but the foreign key pointing to person is not modified, as shown by:
DEBUG hibernate.SQL - update ct_roles set foo=? where movie_id=? and person_id=?
TRACE sql.BasicBinder - binding parameter [1] as 'i can haz update?'
TRACE sql.BasicBinder - binding parameter [2] as [BIGINT] - 999
TRACE sql.BasicBinder - binding parameter [3] as [BIGINT] - 2
Note that person_id 2 belongs to person2, which as yet, has no roles, so the update fails.
So, short of simply deleting the old role and creating a new one attached to the desired person, is there any way to solve this?
Try:
person1.removeFromRoles(role) //role is the role object from which person is to be removed
role.person = person2
Should work.
The answer is:
class Role {
Movie movie
Person person
String fuGorm
def move(Person newbie) {
def m = this.movie?.id
def p = this.person?.id
def n = newbie?.id
// bypass GORM and issue a raw SQL update command...
def q = "update ct_roles set person_id=$n where person_id=$p and movie_id=$m"
def success = runCommand(q)
if (success) {
// the above bypasses GORM, so we should still
// perform the operation using GORM speak
// otherwise our objects will be out of sync with the DB.
// The following EPIC FAILS to update the DB,
// but it does update the local Grails domain objects
this.person.removeFromRoles(this)
this.person = newbie
newbie.addToRoles(this)
}
return success
}
def runCommand = { query ->
def db = new Sql(dataSource)
db.execute (query)
}
}
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.