I use grails 2.2.3 and have a problem with my grails apps.
I have few classes :
class Block {
[...]
String myPropName
Rule rule
[...]
static mapping = {
[...]
rulecolumn: 'RULEID'
[...]
}
class Rule {
[...]
static hasMany = [lkrRuleCrit: LkrRuleCrit]
[...]
static mapping = {
[...]
lkrRuleCrit joinTable: [name: 'LK_RULE_CRIT', key: 'RULEID' ]
[...]
}
class LkrRuleCrit implements Serializable{
Rule rule
String CriteriaValue
static mapping = {
id composite: ['RuleID', 'CriteriaType']
table 'LkrRuleCrit_T'
Rule column: 'RULEID' ,lazy: true
CriteriaType column: 'CRITERIA_TYPE_ID' ,lazy: true
}
}
Block => N lines
Rule => 100 000 000 lines
LkrRuleCrit => Rule x 100 lines
A simple update of Block.myPropName is really slow in production because of the amount of data.
In debug I see that after my block.executeUpdate("update of myPropName ") the framework do :
a select of all the Block updated
and a select of LkrRuleCrit where RULEID is the on of the Block which is not required and possible in this case (I have a link in this for specific use)
How could I disable the refresh of all linked object please ?
I have played which :
mapping {
cache true
}
And with lazy without any effect...
Thanks !
JF
I answer to myself.
Never ever use the org.apache.commons.lang3.builder.ToStringBuilder to produce toString method of Domain class like :
public String toString() {
return ToStringBuilder.reflectionToString(this)
}
It was the cause of all my trouble.
Related
I want to do reflexion on Domain associations to automatically generate a JSON/XML format class descriptor file for a RESTful WS (scaffolding). In the case of HasOne (supposedly bidirectional), I am trying to use the referencedPropertyName in the Association object to show the reverse key (that's the only information that the WS has to show).
Nevertheless I find that hasOne Associations are not correctly initialized.
What follows is the test I made using Grails 2.3.7.
My domains are:
class House {
Long number
Integer inhabitants
static hasOne = [ roof: Roof ]
static hasMany = [ doors: Door ]
static constraints = {
roof nullable: true, unique: true
}
}
class Roof {
String color
House house
static constraints = {
}
}
Then:
import org.grails.datastore.mapping.model.types.Association
import org.junit.Test
import spock.lang.*
import test.House
class AssociationsSpec {
#Test
void "test something"() {
House.gormPersistentEntity.associations.each { Association association ->
String key = association.name
println association.properties
assert association.bidirectional
}
}
}
This test fails:
Assertion failed:
assert association.bidirectional
| |
| false
test.House->roof
at test.AssociationsSpec$_test_something_closure1.doCall(AssociationsSpec.groovy:26)
at test.AssociationsSpec.test something(AssociationsSpec.groovy:23)
Additional info (println association.properties):
[list:false, type:interface java.util.Set, owner:org.codehaus.groovy.grails.domain.GrailsDomainClassPersistentEntity#149a797, class:class org.codehaus.groovy.grails.domain.GrailsDomainClassPersistentEntity$5, cascadeOperations:[ALL], inverseSide:test.Door->house, capitilizedName:Doors, bidirectional:true, referencedPropertyName:house, associatedEntity:org.codehaus.groovy.grails.domain.GrailsDomainClassPersistentEntity#99a9c3, mapping:null, circular:false, owningSide:true, name:doors, fetchStrategy:EAGER]
[capitilizedName:Roof, bidirectional:false, referencedPropertyName:null, circular:false, owningSide:true, name:roof, list:false, type:class test.Roof, owner:org.codehaus.groovy.grails.domain.GrailsDomainClassPersistentEntity#149a797, class:class org.codehaus.groovy.grails.domain.GrailsDomainClassPersistentEntity$4, cascadeOperations:[ALL], inverseSide:null, associatedEntity:org.codehaus.groovy.grails.domain.GrailsDomainClassPersistentEntity#16289cc, mapping:null, foreignKeyInChild:true, fetchStrategy:EAGER]
Thank you
In order to make this bidirectional you need to add static belongsTo = [house: House] and remove your House house property from the Roof domain.
You can read more about the belongsTo property in the documentation.
The problem was that inverse side wasn't initialized.
Fixed in: https://github.com/grails/grails-data-mapping/commit/3c6b7f296be359742062c26113d2ce5c617167c7
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've got a grails domain class I have to persist in Redis, something like this:
class A {
String one
Integer two
B three
E four
mapWith = "redis"
}
class B {
String name
}
enum E {
VALUE1, VALUE2
}
When I persist an instance of class A with the GORM .save() method, Redis saves it correctly except for the enum field "four".
As you can see the fact is known and reported here: http://jira.grails.org/browse/GPREDIS-3
Is there a good workaround to save Enum or something similar?
We're thinking about an array of String objects, what do you think?
I've got this mostly implemented but it doesn't work for Gemfire and I'm waiting until it's fixed for all the supported nosql providers before pushing the fix. As a workaround you can use the inList constraint with a combination of a persistent String property and a non-persistent get/set pair with the name of your current property, e.g.
class A {
String one
Integer two
B three
String fourString
void setFour(E e) {
fourString = e?.name()
}
E getFour() {
fourString ? E.valueOf(fourString) : null
}
static constraints = {
fourString inList: E.values()*.name()
}
static transients = ['fourString']
static mapWith = "redis"
}
I have a Grails domain called People, and I want to check that each People has childs or not. Childs are other People objects. Here is my domain structure:
class People implements Serializable {
static constraints = {
name (nullable : false, unique : true)
createdBy (nullable : false)
creationDate (nullable : false)
}
static transients = ['hasChild']
static mapping = {
table 'PEOPLE'
id generator: 'sequence', params : [sequence : 'SEQ_PK_ID']
columns {
id column : 'APEOPLE_ID'
parentPeople column : 'PARENT_PEOPLE_ID'
}
parentPeople lazy : false
}
People parentPeople
String name
String description
Boolean hasChild() {
def childPeoples = People.createCriteria().count {
eq ('parentPeople', People)
}
return (childPeoples > 0)
}
}
But I cannot call people.hasChild() at anywhere. Could you please helpe me on this? Thank you so much!
It's because in eq ('parentPeople', People), Grails can't understand what "People" is (it's a class). You should replace "People" by this. For example:
static transients = ["children"]
def getChildren() {
def childPeoples = People.findAllByParentPeople(this, [sort:'id',order:'asc'])
}
Another way to get the same result is to use Named Queries. It seems more concise and was created specifically for this purpose. I also like it because it fits the pattern of static declarations in a domain model and it's essentially a criteria, which I use throughout my applications. Declaring a transient then writing a closure seems a bit of a work-around when you can declare named queries ... just my opinion.
Try something like this:
static namedQueries = {
getChildren {
projections {
count "parentPeople"
}
}
}
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.