I'm new to Grails&GORM so this may be a quick question. We are currently looking at using GORMs mongo support and I am having a few issues mapping to existing collection data. I basically want to map to a hierachical object structure whereby my object "Merchant" has reference to another parent merchant. The BSON structure is fairly simple i.e.:
{
name: "name",
parent_id: ObjectId("[Object Id ref]")
}
In my model I attempted to map this relationship as follows:
class Merchant {
ObjectId id
String name
Merchant parent
static belongsTo = [parent: Merchant]
static mappedBy = [parent: "parentId"]
static mapping = {
collection "merchants"
}
static constraints = {
}
}
This resulted in the following BSON:
{
"_id" : ObjectId("4ea6be91ce5f56cd49f43ab8"),
"name" : "where will you g",
"version" : NumberLong(1),
"parent" : {
"$ref" : "merchants",
"$id" : ObjectId("4ea6be91ce5f56cd49f43ab8")
}
}
This has two issues, namely:
- The name of the parent merchant field is "parent" and not "parent_id", which is required.
- The value of the parent field has additional meta infomation other than the id in i.e. $ref : "merchants".
Is there anyway I can keep our existing BSON structure and still have a rich object mapping.
Cheers, Chris.
For the two issues, you need an additional mapping:
static mapping = {
collection 'merchants'
parent attr:'parent_id', reference:false
}
You should also drop the mappedBy block, since you only need it when there are multiple fields of the same type.
Finally, note that reference:false is the default in recent versions of the plugin (1.2+ I think). Note that 'attr' is named 'columnName' in other flavours of GORM.
Related
Suppose I have the following:
class ObjectA implements Serializable {
Foo foo
String objectType
static mapping = {
version false
id composite: ['foo', 'objectType']
foo column: 'foo'
objectType column: 'objecttype'
}
}
Now, I need to reference this object from another Domain.
class ObjectB {
ObjectA objectA
columns {
objectA{
column name: 'foo'
column name: 'objecttype'
}
}
}
When I try and load an ObjectB, I get the following:
Cannot treat multi-column property as a single-column property
How should I be mapping this?
What are you attempting to do with the columns section, I only ask because I have am unfamiliar with that concept? This stack overflow link may help.
But, as show in link, if you want to reference the object you should create a relationship (the one in the example is unidirectional) between the two domains.
class Object B {
static hasOne[objectA:ObjectA]
....
}
There really isn't much more to it then that. You can access the fields from objectA within you views just by using ObjectBInstance.objectA.channelName.
I have an entity, Student, defined in Student.groovy as:
#EqualsAndHashCode(includes = ['id'])
class Student {
Long id
String name
String type
University university
static mapping = {
university column : 'UNIVERSITY_ID'
}
}
and a University entity, defined in University.groovy as:
class University {
Long id
String name
static mapping = {
id column : 'id', generator : 'assigned'
}
}
I've been trying to switch from calling
Student.list(sort: ..., order: ...)
to calling:
Student.findAll("from Student s where type = :type ", [type : 'T'], [ sort : 'name' ])
This fails to order correctly by the name field. The previous version, using list worked fine.
I've also tried calling something like
Student.findAll(sort : 'name') { type == "T" }
which worked fine like this, but when trying to sort by the university.name
Student.findAll(sort : 'university.name') { type == 'T" }
it raised an error regarding the university.name field not being found.
Anybody have any idea on how to do this properly?
Thank you.
Use executeQuery instead of findAll - they should function the same, but I've found that executeQuery is for some reason a more direct caller of the HQL, and findAll fails or returns unexpected results in some cases.
So that first query would be
Student.executeQuery(
'select s from Student s where s.type = :type order by s.name',
[type : 'T'])
and ordering by university name would be
Student.executeQuery(
'select s from Student s where s.type = :type order by s.university.name',
[type : 'T'])
I like HQL and tend to use it a lot, but it couples you to Hibernate and relational databases - if you want to switch to a NoSQL database these queries will fail. Criteria queries, "where" queries and finders all use criteria queries internally, and those are converted to native query API calls by the GORM implementation.
The equivalent criteria queries would be
Student.withCriteria {
eq 'type', 'T'
order 'name', 'asc'
}
and
Student.withCriteria {
eq 'type', 'T'
university {
order 'name', 'desc'
}
}
Some unrelated notes:
You shouldn't use id in equals or hashCode calculations; if you have a persistent Student and a new non-persistent instance with the same name, type, and University, they should be considered equal, but since the non-persistent instance's id will be null they'll be considered different.
You don't need to specify the id property - Grails adds it and the version field to the bytecode via an AST transformation during compilation.
There's no need to map the column name of the university property to 'UNIVERSITY_ID' - that's what it would be anyway.
You can omit the redundant column setting in the id mapping.
Here's the Student class with cruft removed:
#EqualsAndHashCode(includes = ['name', 'type', 'university'])
class Student {
String name
String type
University university
}
and University:
class University {
String name
static mapping = {
id generator: 'assigned'
}
}
Working with Grails 1.3.7, I've to deal with a legacy DB. I've a domain object ''Cake'' which have it's own collection of embedded ''Ingredients'', in a join table.
Ingredient.groovy
class Ingredient {
String name
IngredientCategory category
mapping {
table "foo_ingredient"
version false
id composite:['name', 'category']
columns {
word column:"the_name"
category column:"lol_category_id"
}
}
}
Cake.groovy
class Cake {
String name
static hasMany = [ ingredients : Ingredient ]
static mapping = {
table "foo_cake"
version false
columns {
id column:"id"
name column:"the_name"
}
ingredients joinTable: [
name : "foo_cake_ingredient",
key : "cake_id"
]
}
}
Problem is, Grails expect that the table ''foo_cake_ingredient'' to have two columns ''ingredient_name'' and ''ingredient_category_id''. I would like to specify manually those column names and not let Grails (wrongly) guess those. I can't figure how to do that.
I showed how to do this here: http://grails.1312388.n4.nabble.com/Composite-foreign-key-td3046351.html#a3046436
You probably have to create a hibernate xml mapping for the two tables in this relationship.
Have you read http://jasonrudolph.com/blog/2006/06/20/hoisting-grails-to-your-legacy-db/ ?
I have a domain object that has a property that i want to be used as the id by GORM, the reason being is that ill be saving lists of this object and i want existing rows to be updated if the id already exists in the database
Lets assume that my property i want as the PK is called listId
Ive seen several approaches to this, which is best?
1:
id generator: 'identity', column: 'listId'
2:
static mapping = {
id generator:'assigned'
}
def getKey = {
return listId;
}
or something entirely different?
static mapping = {
id generator: 'assigned', name: "listId", type: 'string'
}
Using ArgoUML, I very quickly created this trivial representation of a few Domain classes (Person, Store, Product) and their relationships.
I'm struggling with the implementation of the relationships. Below was my initial approach for the Person domain, but it seems that I am missing something important.
class PersonToPerson {
Person from
Person to
String relation
static constraints = {
relation(inList:["Friend to", "Enemy of", "Likes", "Hates"])
}
static belongsTo = [ Person ]
}
class Person {
String firstName
String secondName
.
.
.
static hasMany= [ personToPerson:PersonToPerson,
personToStore:PersonToStore ]
}
Edit: updated question for clarity
After thinking on the problem I think I have a better way to ask the question(s). In the implementation of PersonToPerson above I have the relation as a simple string. I want the user to be able to select from a list of unique relations, which are defined in the constraints, for the string value for PersonToPerson. So this leads to the questions...
Should personToPerson and personToStore be consolidated into one list of type Relationship? Or should they stay independent lists as shown?
What is the mechanism to allow the user to add new values to the relation constraint?
1) Domain model
Keep your code simple. Don't create generic data model. It's way to hell. When you personToPerson and personToStore keep separate, it's much easier to follow your code.
Actually suggested solution makes it possible to access relations as consolidated and independent list simultaneously.
For this problem I would use inheritance feature in GORM.
Your classes would look like this:
class Person {
String name
static hasMany = [personToPerson:PersonToPerson,
personToProduct:PersonToProduct,
personToStore:PersonToStore]
static mappedBy = [personToPerson:"from"]
}
class Product{
String productName
}
class Relationship{
String note
}
class Store{
String storeName
}
class PersonToPerson extends Relationship{
Person from
Person to
String relation
static constraints = {
relation(inList:["Friend to", "Enemy of", "Likes", "Hates"])
}
static belongsTo = [ from:Person ]
}
class PersonToProduct extends Relationship{
Person person
Product product
String relation
static constraints = {
relation(inList:["likes", "dislikes"])
}
static belongsTo = [ person:Person ]
}
class PersonToStore extends Relationship{
Person person
Store store
String relation
static constraints = {
relation(inList:["Stock person", "Owner", "Manager", "Patron"])
}
static belongsTo = [ person:Person ]
}
DB schema for Person, Product and Store is usual. But for Relational domains look like this:
Relationship
Field Type Null Default
id bigint(20) No
version bigint(20) No
note varchar(255) No
class varchar(255) No
person_id bigint(20) Yes NULL
product_id bigint(20) Yes NULL
relation varchar(8) Yes NULL
from_id bigint(20) Yes NULL
to_id bigint(20) Yes NULL
store_id bigint(20) Yes NULL
Relationship domain makes possible to access all relational domain throw one domain.
2) Constraint
Just switch inList to validator. Than you can store constrain in file or DB.
See documentation or file example.
Example how to store constraint values in DB. First create a domain object.
class Constrain{
String name
String type
}
Than the domain class looks:
class PersonToPerson extends Relationship{
Person from
Person to
String relation
static constraints = {
relation(nullable:false, validator:{val, obj ->
def list = Constrain.findAllByType('person').collect{it.name}
return list.contains(val)
})
}
static belongsTo = [ from:Person ]
}
Look fine. You may want to consider a belongsTo in the PersonToPerson class.
Also, your has many in Person should be: [ personToPersons:PersonToPerson.... <- remove the s