Id is set to NULL when trying to save DomainObject in Grails - grails

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

Related

propertyMissing doesn't work in Criteria?

So I've got a couple of classes with the following relationship:
class Foo {
Bar bar
/* ... other fields ... */
}
class Bar {
String name
}
In class Foo I've got a couple of named queries:
static namedQueries = {
userFoos { user ->
/* ... get Foos for this user ... */
}
limitFoos { colname, dir ->
order(colname, dir)
}
...which I can then chain together in a controller:
def foos = Foo.userFoos(currentUser).limit(colname, dir)
Pretty straightforward so far. The problem is when I try to sort on bar; I get the error:
could not resolve property: bar.name of: package.Foo.
Now, I also got this error when the queries were Criteria that were declared in the controller. So, I went and wrote a propertyMissing handler for Foo:
def propertyMissing(String name) {
if (name.contains(".")) {
def (String propertyname, String subproperty) = name.tokenize(".")
if (this.hasProperty(propertyname) && this."$propertyname".hasProperty(subproperty)) {
return this."$propertyname"."$subproperty"
}
}
}
I don't know if this is really the best way to do it, but it did work! However, now that I've moved the query into the class as a named query, propertyMissing doesn't appear to work anymore! Is this use not supported, or am I just missing something here?
EDIT
So I tried moving the Criteria back into the controller and sure enough, the sub-property sort did not work there either! So I guess Criteria just don't support propertyMissing at all :/
To answer dmahapatro's question, I am using jQuery DataTables to present the information. Clicking on a column header does an AJAX call to a controller action with parameters to indicate which column to sort on and in which direction. Once I determine the column name, I call the named queries like so:
def foosFilteredLimited = params.sSearch ?
Foo.userFoos(currentUser).filterFoos(params.sSearch).limitFoos(offset, max, colName, sortDir).list()
: Foo.userFoos(currentUser).limitFoos(offset, max, colName, sortDir).list()
(filterFoos takes a search string and narrows the results of userFoos.)
Try modifying limitFoos namedQuery as below and it should work. There is a caveat to it though. We cannot use bar.baz.name if required. ;)
limitFoos { column, ord ->
def colStrs = column.tokenize(/./).toList()
if( colStrs?.size() > 1 ) {
"${colStrs[0]}" {
order( "${colStrs[1]}", ord )
}
} else {
order(column, ord)
}
}

Grails cast sqltype "number" to String

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
}

Grails 2 abstract domain inheritance issue

The following is not working for me when using abstract (or non-abstract for that matter) inheritance in Grails.
Very quickly, my inheritance is as follows:
abstract BaseClass { ... }
SomeClass extends BaseClass { ... }
SomeOtherClass extends BaseClass { ... }
And then in another domain object:
ThirdClass {
...
BaseClass baseProperty
...
}
But now, when I try to set that property to either a SomeClass or SomeOtherClass instance, Grails compains:
ERROR util.JDBCExceptionReporter - Cannot add or update a child row: a foreign key constraint fails
...
Is this not possible?
I have also tried having the base class not be abstract, and also tried casting the SomeClass or SomeOtherClass instances to BaseClass. They generate the same error.
UPDATE
I just checked. It works for the first sub-class that I add. But as soon as I try to add the other sub-class it fails.
In other words:
def prop1 = new ThirdClass(baseProperty: instanceOfSomeClass).save()
works fine. But when I then try and do:
def prop2 = new ThridClass(baseProperty: instanceOfSomeOtherClass).save()
it fails.
UPDATE 2
Further investigation shows that something goes wrong during the table creation process. It correctly adds two foreign keys to the ThirdClass table, but the keys incorrectly references:
CONSTRAINT `...` FOREIGN KEY (`some_id`) REFERENCES `base_class` (`id`),
CONSTRAINT `...` FOREIGN KEY (`some_id`) REFERENCES `some_class` (`id`)
Don't know why it's choosing the base class and one of the sub-classes? I have tried cleaning etc.
First of all, create your BaseClass outside domain structure. It must be an external class, put it on script folder, source folder.
package com.example.model
/**
* #author Inocencio
*/
class BaseClass {
Date createdAt = new Date()
}
Now, create a regular domain class and extend it.
package com.example.domain
import com.example.model.BaseClass
class SomeClass extends BaseClass {
String name
static constraints = {
name(nullable: false)
}
}
As you can see, once you persist SomeClass a createdAt field is filled and saved as well. Check the test class out:
#TestFor(SomeClass)
class SomeClassTests {
void testSomething() {
def some = new SomeClass()
some.name = "Hello There"
some.save()
//find it
def someFind = SomeClass.list()[0]
assert someFind
assertTrue someFind.createdAt != null
// println "Date: $someFind.createdAt"
// println "Name: $someFind.name"
}
}
I hope it can be helpful.
I have just created class structure as yours (Grails 2.1.0) and there is no problem. It works when mocked and unit-tested. The same when scaffolded and SomeClass and ThirdClass instances saved from forms.
Try clean your DB, especially if you haven't used 'create-drop' mode. Maybe there is some old constraint left.
Last thing, you haven't specified when the error occurs - on save (create or update)? It's rather not probable to get JDBC exception on property set, is it?
I don't remember for sure but it's possible that simple property isn't cascaded by default then try to save SomeClass instance before saving the ThirdClass instance. Also you can auto-cascade instead of declaring simple property by use hasOne relation like:
class ThirdClass {
...
static hasOne = [baseProperty:BaseClass]
}

Problem with GRAILS select - trying to insert a number for a field that appears as text in dropdown list

Here is the domain class I have defined:
package mypackage
public enum UFModeType {
I(0),
O(1),
R(3)
Integer mode
public UserFileModeType(Integer mode) {
this.mode = mode;
}
static list() {
[I, O, R]
}
}
This is a property of another domain Parent where it is as follows:
package mypackage
class Parent {
String name
... ... ...
UFModeType uFMode
static mapping = {
table 'parent_table_with_ufMode_col_as_number'
version false
tablePerHierarchy false
id generator:'sequence', params:[sequence:'myseq']
columns {
id column:'parentid'
uFMode column: 'UFMODE'
}
}
static constraints = {
userFileMode(nullable: true)
}
}
The gsp call for this looks like this:
g:select name="uFMode" from="${mypackage.UFModeType?.list()}" value="${parentInstance?.uFMode?.name()}" /
I have tried a lot of variants of the above in the gsp call but I am getting error that the db insert fails saying the entry of ufmode is invalid number, thus this is not being passed as a number. I printed the params in the controllers save and it shows this:
Params in save=[uFMode:I ...
I am sure I may be missing some minor thing in syntax, but I have tried a lot of things without much success, so any inputs will be greatly appreciated.
Thanks!
Try changing
value="${parentInstance?.uFMode?.name()}
to
value="${parentInstance?.uFMode?.mode()}
From the definition of UFModeType you give you do not have a name attribute.

Grails - check if item has parent

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) { ... }

Resources