I have an object Foo that has a bidirectional one-to-one relationship with Bar and another one with Baz. When I try to do a .load with Foo and only give it a Bar, I get referential integrity exceptions complaining that there isn't a Baz.
Should this really be the case? In a real world environment isn't it possible that there wouldn't be any matching Baz object in the database?
I tried manually setting baz:null in the fixtures load closure, but I still get the same thing. On a side note, when I only set properties (such as a simple string), everything works fine. It's only when I start setting relationships.
This is with Grails 2.2.4, Fixtures 1.2, and without the build-test-data plugin installed.
EDIT: I have the constraints specifying Baz to be nullable and unique. Just for giggles I tried adding the blank constraint too, but no luck.
static constraints = {
baz nullable:true, unique: true, blank: true
}
EDIT 2: Here is a simplified version of the code:
class Foo {
String someValue1
String someValue2
String whatever
Bar bar
Baz baz
static mapping = {
id composite: ['someValue1', 'someValue2'], generator: 'assigned'
columns {
bar([:]) { column name: 'some_other_value' }
baz ([insertable:false, updateable: false]) {
column name: 'some_value_1'
column name: 'some_value_2'
}
}
version: false
static constraints = {
//there are no constraints for Bar
baz nullable:true, unique:true
}
}
class Bar {
String someOtherValue
static hasMany = [foos:Foo]
static mapping = {
id generator:'assigned', name:'someOtherValue'
}
}
class Baz {
String someValue1
String someValue2
String asdf
static mapping = {
id composite: ['some_value_1', 'some_value_2']
version false
}
}
class MyTest {
def fixtureLoader
#Before
void setup() {
fixureLoader.load {
myBar(Bar, someOtherValue:"shibby")
myFoo(Foo, someValue1:"test", someValue2:"test2", bar:myBar)
//i also tried this
//myFoo(Foo, someValue1:"test", someValue2:"test2", bar:myBar, baz:null)
}
}
}
Here is part of the exception:
Caused by: org.h2.jdbc.JdbcBatchUpdateException: Referential integrity
constraint violation: "FK190E74B120F4F2BC: MYSCHEMA.FOO FOREIGN
KEY(SOME_VALUE_1, SOME_VALUE_2) REFERENCES MYSCHEMA.BAZ(SOME_VALUE_1,
SOME_VALUE_2)"; SQL statement: insert into MYSCHEMA.foo (whatever,
some_other_value, some_value_2, some_value_1) values (?, ?, ?, ?, ?,
?, ?, ?) [23506-164]
EDIT: Sorry, I misspoke earlier. Bar has a many-to-one relationship with Foo.
Your simplified version of code (except hasMany in Bar) works for me without any FK exception. Although I would prefer a different approach to achieve a true one-one bidirectional relationship if I am correct with the parent and child mapping.
Below is my setup which works fine without the FK constraint exception. Note that I have also mentioned in comments how would I achieve true one-to-one bidirectional assuming Foo has one Bar and has one Baz.
class Foo implements Serializable{
String someValue1
String someValue2
String whatever
//True one to one can be achieved by doing as below
//static hasOne = [bar: Bar, baz: Baz]
Bar bar
Baz baz
static mapping = {
id composite: ['someValue1', 'someValue2'], generator: 'assigned'
columns {
bar([:]) { column name: 'some_other_value' }
baz ([insertable:false, updateable: false]) {
column name: 'some_value_1'
column name: 'some_value_2'
}
}
version: false
}
static constraints = {
//baz nullable:true, unique:true
}
}
class Bar {
String someOtherValue
//True one to one can be achieved by doing as below
//Below entry makes the relation bi-directional
//Foo foo
static mapping = {
id generator:'assigned', name:'someOtherValue'
//Optional, added for clarity
someOtherValue column: 'some_other_value'
}
}
class Baz implements Serializable{
String someValue1
String someValue2
String asdf
//True one to one can be achieved by doing as below
//Below entry makes the relation bi-directional
//Foo foo
static mapping = {
id composite: ['someValue1', 'someValue2']
//Optional, added for clarity
someValue1 column: 'some_value_1'
someValue2 column: 'some_value_2'
asdf column: 'asdf'
version false
}
}
class MyTests extends GroovyTestCase {
def fixtureLoader
void setUp() {
fixtureLoader.load {
myBar(Bar, someOtherValue:"shibby")
myFoo(Foo, someValue1:"test", someValue2:"test2",
whatever: "whatever", bar: myBar)
}
}
void testSomething() {
Foo.all.each{println it.properties}
Bar.all.each{println it.properties}
}
}
//Bootstrap Test in Dev mode
new Bar(someOtherValue: 'shibby').save()
new Foo(someValue1:"test", someValue2:"test2",
whatever: "whatever", bar: myBar).save(failOnError: true, flush: true)
Notes
Above I have used your exact simplified code but the hasMany relation in Bar.
Constraints on Baz is optional.
Fixtures works as expected.
Columns are created in Foo as expected.
logSql showed expected DML.
To witness the table changes, I also BootStraped the same test data in dev mode following a run-app. I was able to see the expected table structure with data in it, using dbconsole.
Following the general way of one-to-one bidirectional (mentioned as commented code), FKs are created in the child tables [Bar and Baz], so the explicit mapping you provided in the sample code would not hold good.
The question will be more clear if the owning side of the relationship and the rationale behind having hasMany in Bar is mentioned.
Related
I have a MS SQL DB (which I can't change) with 3 tables :
contact
id, name, number
c_group
id,name,email
contact_group
id, contact_id, group_id
In Grails I have 3 domain classes :
class Cgroup {
String name
String email
static constraints = {
}
static mapping = {
table name: "c_group"
version false
}
}
class Contact {
String name
String number
static constraints = {
name nullable:false
}
static mapping = {
version false
}
}
class Contact_group {
Cgroup cgroup
Contact contact
static constraints = {
}
static mapping = {
version false
cgroup_id column: "c_group_id", sqlType: "int"
}
}
I'm trying to create a list using :-
def contactInstance = Contact.get(id) //I have previously got the id
List userGroupList = Contact_group.createCriteria().list(params) {
eq("contact", contactInstance)
}
And it is throwing an error
Invalid column name 'cgroup_id'.. Stacktrace follows:
As you can see, I'm trying to map the correct column name (in the Contact_group domain class) as the table has been renamed from group to c_group in the DB, and just to complicate matters for some reason I decided to call the domain class Cgroup in my Grails app. (I can change this if needs be)
So, I'm now in a bit of a muddle about how best to create the list. Any suggestions or pointers?
In your mapping you should point to the class property (cgroup_id should be cgroup, since that is what you named it in your domain.)
Also, your column name in your table is id not c_group_id so you should set the column as id.
static mapping = {
version false
table name: "contact_group"
cgroup column: "id", sqlType: "int"
}
This is a syntax question. I want a one-to-many relationship between Foo -> Bar (simplified here):
class Foo {
String fooPK1, fooPK2
static mapping = {
id composite: ["fooPK1", "fooPK2"]
}
static hasMany = [bars: Bar]
}
class Bar {
String fooPK1, dumbNameForFooPK2, barPK1, barPK2
Foo myFoo
static mapping = {
id composite: ["barPK1", "barPK2"]
columns {
myFoo[:] {
column name: "FOO_PK_1"
column name: "?????????????"
}
}
}
}
In this case, obviously Foo.fooPK1 maps to Bar.fooPK1, but i need Foo.fooPK2 to map to Bar.dumbNameForFooPK2. Hopefully this makes sense.
My problem is I have no idea what the syntax is supposed to be (or if there's a better way to do this!) and from what I could find, the grails documentation wasn't really helpful.
You need to rename the foreign key columns declaration inside Bar, wright?
class Bar {
Foo myFoo
static mapping = {
columns {
myFoo {
//declare them in the order of your id composite.
column name: "foo_pk_1"
column name: "dumb_name_for_foo_pk_2"
}
}
}
}
I have an abstract Event class which holds properties shared by all Event types and beneath it I have two subclasses with one table per class (MasterEvent and ParentEvent).
The problem is that in MasterEvent the id column is of type "number" and in the ParentEvent it is of type "varchar2".
This means that when I try to run the app I get :
Caused by HibernateException: Wrong column type in * for column event_id. Found: number, expected: varchar2(255 char).
Note that this is a legacy database, so there's no chance in changing the column types on the database level.
The sample code below should help in understanding better:
Event.groovy
package a
abstract class Event {
String id
static mapping = {
tablePerHierarchy "false"
id column: "id"
}
}
ParentEvent.groovy
package a
class ParentEvent extends Event {
static mapping = {
id column: "id"
}
}
MasterEvent.groovy
package a
class MasterEvent extends Event {
static mapping = {
id column: "id", type: "number"
}
}
I have tried putting type: number all sorts of combinations, but it's always giving me the same error.
Is there anyway to either cast the sqlType directly to String or have grails ignore this validation?
type: 'number' does not work because it is not a hibernate basic type
Try changing it to type: 'long' (or whatever fits your legacy DB)
Also see the docs: Link
Edit after your comment
I don't know what you are doing in your abstract class, but perhaps you can do something like that and use the getter in your abstract class (not tested!):
class MasterEvent extends Event {
Long longId
static mapping = {
longId column: "id"
}
#Override
String getId() {
longId?.toString()
}
}
Did you try to define id field in subclasses? "String id" in Parent event and "long id" in Master event?
You could try a custom Hibernate UserType which takes the number id column and converts it to a String. This would then go in the MasterEvent class:
static mapping = {
id column: "id", type: NumberToStringType
}
I have two domain object with the same structure as the class Foo below
class Foo {
static mapping = {
id column: "old_id_column"
prop1 column: "old_prop_1"
... some more properties ...
}
}
class Bar {
... looks the same as Foo ...
}
and in the controllers of the classes I try to accomplish the same thing, creating a new object and saving it to the DB.
When I do this
class FooController {
def methodName() {
final Foo foo = new Foo()
foo.prop1 = "val1"
foo.prop2 = val2
etc.
foo.save(flush: true)
}
}
Everything works but when I try to do the same in the BarController i get the following exception
Cannot insert the value NULL into column 'old_id_column', table 'System.db.Bar'; column does not allow nulls.
I'm trying to model a legacy database so obviously I'm missing something in the configuration of the underlying DB but I can't find anything. Any ideas about things that could raise this exception?
You need to specify a generator
i.e.
id generator:'assigned', column: 'old_id_column', type: 'string'
See the documentation # http://grails.org/doc/latest/ref/Database%20Mapping/id.html
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) { ... }