In my current project I'm unable to persists a one-to-many relationship.
Both sides of the relationship will be saved - just the link between them is missing.
The one-to-many relationship between my two domains looks like this :
class Parent {
String name
...
static hasMany = [childs: Child]
Parent() {}
Parent(SomeOtherClass obj) {
this.properties = obj.properties
}
}
class Child {
int code
String description
}
I'm creating my Parent instances within a ParentService:
public Boolean createParents(List parentIds) {
boolean savedSuccessfully = true
Parent.withTransaction {status ->
parentIds.each { parentIdString ->
if (parentIdString && parentIdString.isInteger()) {
int parentId = parentIdString.toInteger()
def parentInstance = new Parent(someOtherService.getExternalParentObj(parentId))
someOtherService.getExternalChilds(parentId).each { entry ->
parentInstance.addToChilds(Child.findOrSaveWhere(code: entry.key, description: entry.value))
}
if(!parentInstance.save()) {
status.setRollbackOnly()
savedSuccessfully = false
}
}
}
}
return savedSuccessfully
}
Both, the Parent instances and the Child instances are created and saved successfully. Just the link between parent and child is missing. The childs property of each Parent instance is an empty list.
I dont't know what is wrong here.
Why does the relationship won't be persited? Any ideas?
Update
I've added an integration test for the ParentService to test the existence of the childs list after creating all Parent instances :
...
assert parentService.createParents(["123","234","356"]) == true
def parent = Parent.get(1)
assert parent.childs.size() > 0
...
Unexpectedly - the test passed.
I found the origin of the error.
I totally missed that some entry.value are null. Don't know why I didn't see it during debugging.
Now I only use Child.findOrSaveWhere() if entry.value is not null.
I would have expected that the whole parentInstance.save() would pass or fail completely. Still don't understand why the join table won't be filled without giving any error.
Related
I'm pretty much certain I'm doing something wrong since this obviously works. Simplified classes:
class Person {
String name
static hasMany = [cats:Cat]
}
class Cat {
String name
Person person
static belongsTo = Person
static constraints = {
person(nullable:false)
}
String toString() {
"${person.name}-${name}"
}
}
Simple stuff, a person has many cats, cats must belong to only a single person.
Now when I do the following in a Service class, I get strange results:
delete(Cat cat) {
Person owner = cat.person
log.debug("Cats before removing ${cat} (id=${cat.id}): ${owner.cats} -- ${owner.cats*.id}")
owner.removeFromCats(cat);
log.debug("Removed from owner ${owner}, owner now has ${owner.cats} -- ${owner.cats*.id}")
log.debug("Cat to delete is now: ${cat} and belongs to... ${cat.person}")
cat.delete(flush:true)
}
And the error is "object would be resaved, blah blah"
org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations)
The weird bit is the debug results, when called to remove cat "Fluffy" who's owned by "Bob":
Cats before removing Bob-Fluffy (id=1356): [Bob-Fluffy] -- [1356]
Removed from owner Bob, owner now has [null-Fluffy] -- [1356]
Cat to delete is now: null-Fluffy and belongs to... null
What's going on that "removeFrom" isn't actually removing the object from the collection? I cleaned and recompiled. Pretty much at a loss as to why I can't delete this object.
I would try to remove the field person as Person person and leave only the belongsTo field like this
class Cat {
String name
static belongsTo = [person:Person]
static constraints = {
person(nullable:false)
}
String toString() {
"${person.name}-${name}"
}
}
It looks like what was happening in my case is that cat.person has getting stale somehow, even though it's the first thing in the method. Calling cat.refresh() didn't work, but calling owner.refresh() after extracting it from the cat.
I would change the domain class as such.
class Person {
String name
static hasMany = [cats:Cat]
}
class Cat {
String name
Person person
// no need to add belongs to property here. it creates a join table that you may not need
static constraints = {
person(nullable:false)
}
String toString() {
"${person.name}-${name}"
}
}
In the service class
delete(Cat cat) {
cat.delete(flush:true)
}
Once you make the domain changes, start with a fresh database since the schema will change.
I think that should solve your problem.
I am using this Domain Class :
#XmlRootElement(name="Target")
#XmlAccessorType(XmlAccessType.NONE )
class TargetElement {
static constraints = {
testPurpose()
}
#XmlAttribute(name="TestPurpose")
String testPurpose
// instead of bellow in comment use this
#XmlElementWrapper(name = "Bag")
#XmlElement(name="Child")
List<Child> bag= new ArrayList<Child>()
// 1..n Child
static hasMany = [child:Child]
}
If I want to add Child elem, I use
def target = new TargetElement(testPurpose:"TestPurpose")
target.getBag().add(child)
target.save(flush:true)
if (target.hasErrors()){
println '3 ***********' + target.errors
}
But I get list == null for bag.
Why my list is null? It is working for another project and only name is changing. May be my eyes are missing something...
For whom who get the same trouble : having #XmlElementWrapper value(bag) to null
in my Bootstrap.groovy.I have to declare :
ArrayList<Child> bag = new ArrayList<Child>()
bag.add(child1)
bag.add(child2)
targetElement.getBag().addAll(bag)
also I have met another issue whith GROM lazy loading and JAXB. Check that objects nested are retreived :
eg :
static mapping = {
nested lazy: false
}
Hope this will helps
I have three classes in grails application
class Category {
String name
}
class Application {
String name
static hasMany =[specialCategoryies:SpecialCategory]
}
class SpecialCategory {
Category category
Integer points
static belongsTo =[application:Application]
}
Here while I am saving the applicationInstance I don't want save duplicate
specialCategories values like ..specialCategories does not have same
category value again ..
application.addToSpecialCategoryies(newSpecialCategory(category:Category.get(1),points:2))
application.addToSpecialCategoryies(newSpecialCategory(category:Category.get(1),points:3))
here i application instance should rise error that category value repeated..
so how define constraints for hasMany properties in domain class......?
suggest how to write constraints to avoid duplicate values of category
You might try using a custom validator in your Application constraints section. For example, one way to check for duplicate property values is to collect the values into an array and compare them to the corresponding unique array (with duplicated elements removed):
class Application {
String name
static hasMany =[specialCategoryies:SpecialCategory]
static constraints = {
specialCategoryies validator: { specialCategories, obj ->
def specialCategoriesIdArray = specialCategories.collect {it?.category?.getId()}
return (specialCategoriesIdArray.size() == specialCategoriesIdArray.unique().size())?true:'application.validator.specialcategoryduplicate.error'
}
}
}
When trying to save a special category with an existing category, it will throw a validation error when saving. You can test with the following:
def cat1 = new Category(name:"Cat 1").save(flush:true)
def cat2 = new Category(name:"Cat 2").save(flush:true)
def app = new Application()
app.name = "Test"
app.addToSpecialCategoryies(new SpecialCategory(category: Category.get(1), points:2))
app.addToSpecialCategoryies(new SpecialCategory(category: Category.get(2), points:2))
app.addToSpecialCategoryies(new SpecialCategory(category: Category.get(1), points:3))
if ( app.save(flush:true) ){
log.info "Saved!"
} else {
log.error "NOT Saved. Error:"
app.errors.each {
log.error it
}
}
I know there are several questions on this subject but none of them seem to work for me. I have a Grails app with the following Domain objects:
class Tag {
String name
}
class SystemTag extends Tag {
// Will have additional properties here...just placeholder for now
}
class Location {
String name
Set<Tag> tags = []
static hasMany = [tags: Tag]
}
I am trying to query for all Location objects that have been tagged by 1 or more tags:
class LocationQueryTests {
#Test
public void testTagsQuery() {
def tag = new SystemTag(name: "My Locations").save(failOnError: true)
def locationNames = ["L1","L2","L3","L4","L5"]
def locations = []
locationNames.each {
locations << new Location(name: it).save(failOnError: true)
}
(2..4).each {
locations[it].tags << tag
locations[it].save(failOnError: true)
}
def results = Location.withCriteria {
tags {
'in'('name', [tag.name])
}
}
assertEquals(3, results.size()) // Returning 0 results
}
}
I have validated that the data is being created/setup correctly...5 Location objects created and the last 3 of them are tagged.
I don't see what's wrong with the above query. I would really like to stay away from HQL and I believe that should be possible here.
Welcome to hibernate.
The save method informs the persistence context that an instance should be saved or updated. The object will not be persisted immediately unless the flush argument is used
if you do not use flush it does the saves in batches so when you setup your query right after the save it appears that the data is not there.
you need to add
locations[it].save(failOnError: true, flush:true)
You should use addTo* for adds a domain class relationship for one-to-many or many-to-many relationship.
(2..4).each {
locations[it].addToTags(tag)
}
I'm new to Grails, Groovy and GSP.
I have a domain class "ProductCategory".
class ProductCategory {
static constraints = {
}
static mapping = {
table 'product_category';
version false;
cache usage: 'read-only';
columns {
parent column: 'parentid';
procedure column: 'procid';
}
}
static hasMany = [children:ProductCategory];
ProductProcedure procedure;
Integer lineorder;
String name;
ProductCategory parent;
String templatelink;
char offline;
String toString() {
return id + " (" + name + ")";
}
}
Each category CAN have a parent. I am using an existing database, and the table has a column 'parentid' to do that. When a category has no parent (root level), its parentid is 0.
I have a GSP trying to show data about the parent if any.
<g:if test="${category.parent}">
hello
</g:if>
I was under the impression that this would test for existence.
It works fine if the category DOES have a parent, but as soon as parentid=0, it blows up.
No row with the given identifier exists: [ProductCategory#0]
I tried to check for ==0, but it didn't work, I assume because 'parent' is supposed to be an object.
So how can I make it so that it assumes that parentid=0 is the same as parent=null, or NO parent?
Thanks
I think I may have found the answer:
parent column: 'parentid', ignoreNotFound: true;
ignoreNotFound is nowhere on the documentation, but it seems to work!
parentid should not be equal to 0. It should be null.
What I don't understand in your question, is how can you have parentid == 0 ?
You don't need to handle the parentid manually. As soon as you define a domain class like this:
Class Foo {
Bar bar
}
Gorm/Grails will automatically create a foreign key column for you. And if you define the property nullable:
Class Foo {
Bar bar
static constraints = {
bar(nullable:true)
}
}
...you can just set it to null and test for null:
def f = new Foo(bar:null)
if (f.bar == null) { ... }