GORM hasMany using multiple columns - grails

I have a class as follows
class PrefValue extends MainDomain {
String entityClass
Long entityId
....
}
I am now trying to map a collection to two different classes
class TypeA extends MainDomain {
Long entityId
static hasMany = [preferences:PrefValue]
static mappedBy = [preferences: "entityId"]
...
}
class TypeB extends MainDomain {
Long entityId
static hasMany = [preferences:PrefValue]
static mappedBy = [preferences: "entityId"]
}
The problem arises because both TypeA and TypeB can have the same ID but they will have different entityClass values. How would I map this?

I'm assuming that entityId is the primary key, leading to issues when TypeA and TypeB both have the same value for their primary key.
In this case you can drop the primary key constraint and instead use a composite primary key instead. The documentation goes into more detail about it here.
If you it is NOT a primary key and is just has a unique constraint, then you can make the entityId unique per discriminator:
static mapping = {
entityId unique: 'entityClass'
}

Related

How to create 3 domain relationships in Grails 3?

I have 3 domain class and I need to define their relationships but I find no luck in defining them correctly. Can somebody rectify my code? I have always get the error unknown column pointing to an id. Please help. Below are my codes. Thank you.
Todo.groovy class
package todoScaff
import userScaff.User
import categoryScaff.Category
class Todo {
String name
String note
Date createDate
Date dueDate
Date completedDate
String priority
String status
User owner
Category category
static belongsTo = [User, Category]
static constraints = {
name(blank:false)
createDate()
priority()
status()
note(maxsize:1000, nullable:true)
completedDate(nullable:true)
dueDate(nullable:true)
}
String toString() {
name
}
}
Category.groovy class
package categoryScaff
import todoScaff.Todo
import userScaff.User
class Category {
String name
String description
User user
static belongsTo = User
static hasMany = [todos: Todo]
static constraints = {
name(blank:false)
}
String toString(){
name
}
}
User.groovy class
package userScaff
import todoScaff.Todo
import categoryScaff.Category
class User {
String userName
String fname
String lname
static hasMany = [todos: Todo, categories: Category]
static constraints = {
userName(blank:false, unique:true)
fname(blank:false)
lname(blank:false)
}
String toString(){
"$lname, $fname"
}
}
Errors
I'm fairly certain your SQL tables are incorrect.
I created a small project with your 3 domain classes and used the grails command schema-export; after removing everything that doesn't relate to the domain relationship, the structure should look like this:
create table category (
id bigint,
user_id bigint,
);
create table todo (
id bigint,
category_id bigint,
owner_id bigint,
);
create table user (
id bigint
);
If your table structure doesn't resemble this, you should either change it to match or use the static mapping = {} closure in the domain class to indicate the proper column names.
Static Mapping
In Todo:
static mapping = {
owner column: 'owner_id' // change owner_id to match your owner column within todo
category column: 'category_id' // change category_id to match your category column within todo
}
In Category:
static mapping = {
owner user: 'user_id' // change user_id to match your user column within category
}

grails: How to map one to many relationship with composite ForeignKey

I have a pre-existing database with two tables FileContainer and FileObjects. FileContainer has a composite primary key of clientId and fileContainerId. FileContainerId is unique per client. FileObjects also has a composite primary key of clientId(FileContainerClientId) and fileObjectId. FileContainer and FileObjects has a one to many relationship. Foreign Key Relationship is having multiple columns(clientId and fileContainerId). So my Domain classes look like the following.
class FileContainer implements Serializable {
BigDecimal clientId
BigDecimal fileContainerId
...
...
static hasMany = [fileObjects: FileObject]
static mapping = {
table 't_container'
id composite : ["clientId", "fileContainerId"]
}
}
class FileObject implements Serializable {
BigDecimal FileContainerClientId
BigDecimal fileObjectId
FileContainer fileContainerId
...
...
static belongsTo = [FileContainer]
static mapping = {
table 't_file_object'
id composite : ["FileContainerClientId", "fileContainerId"]
column fileContainerId: 'custom_name_container_id'
column FileContainerClientId: 'client_id'
}
}
But unfortunately above Domain classes fails validation and give the following exception with text org.hibernate.MappingException: 'Repeated column in mapping for entity: FileObject column: file_container_client_id (should be mapped with insert="false" update="false")'.
I tried using a custom UserType consisting of columns (client_id and file_container_id). But got all sorts of different errors.
How should domain classes be created in this case ?
Also, If somebody can give an example of using org.hibernate.usertype.UserType that would be helpful.
===============================================================
Also tried the following in the FileObject class.
class FileObject implements Serializable {
BigDecimal clientId
BigDecimal fileObjectId
BigDecimal fileContainerId
...
...
static belongsTo = [fileContainer: FileContainer]
static mapping = {
table 'file_object'
id composite : ["FileContainerClientId", "fileContainerId"]
column fileContainerId: 'custom_name_container_id'
column clientId: 'client_id'
fileContainer column: 'client_id'
fileContainer column: 'file_container_id'
}
}
But got the missing file_container_client_id column exception.
=========================================================================
Since the FileContainerClientId in Object class is a foreign key to clientId in Container class, it should never be inserted or updated directly, it should always come from FileContainer class. Going by the above logic, I tried adding the following mapping
static mapping = {
FileContainerClientId insertable: false
FileContainerClientId updateable: false
}
but Compile failed since the property name starts with capital letter. If I change the property name to fileContainerClientId then it compiles but cannot map the column to foreignKey and throws a missing file_container_client_id column exception during validation
The following code can be used to map such cases.
class FileContainer implements Serializable {
BigDecimal clientId
BigDecimal fileContainerId
...
...
static hasMany = [fileObjects: FileObject]
static mapping = {
table 't_container'
id composite : ["clientId", "fileContainerId"]
}
}
class FileObject implements Serializable {
BigDecimal clientId
BigDecimal fileObjectId
BigDecimal fileContainerId
FileContainer fileContainer
...
...
static belongsTo = [FileContainer]
static mapping = {
table 't_file_object'
id composite : ["clientId", "fileObjectId"]
columns {
fileContainer {
column name:'client_id'
column name:'file_container_id'
}
}
fileContainer insertable:false, updateable:false
}
}

Grails object references an unsaved transient instance

i have this issue when i want to save my object
My Customer
String firstName
String lastName
LocalDate dateOfBirth
CountryCode nationality
My CountryCode
#Audited
class CountryCode implements Serializable {
String code
String symbolA2
String symbolA3
String countryName
static constraints = {
code size:3..3, unique: true, matches: '[0-9]+'
symbolA2 size:2..2, nullable: false, unique: true, matches: '[A-Z]+'
symbolA3 size:3..3, nullable: false, unique: true, matches: '[A-Z]+'
countryName size:1..50
}
static mapping = {
id generator: 'assigned', name: 'code'
}
def beforeValidate() {
symbolA2 = symbolA2?.toUpperCase()
symbolA3 = symbolA3?.toUpperCase()
}
#Override
String toString() {
return countryName
}
}
when i try to save my object i recieve this error
Class
org.hibernate.TransientObjectException
Message
object references an unsaved transient instance - save the transient instance before flushing: lookup.iso.CountryCode
Do you have ideas how to fix this?
Thankx
Use Grails relation convention
static hasOne = [nationality:CountryCode]
at Customer class and
static belongsTo = [customer:Customer]
at CountryCode class
Check grails docs about that, especially paragraph stating about cascade saves.
If this doesn't fit your case than you need to call save() on CountryCode instance before assigning it to a Customer instance.
You can also use static embedded if apply to your case.
Another thing if you consider CountryCode as a dictionary entity, then load desired ContryCode instance from repository before assigning it to the Customer instance.
The specific reason for your error is because you haven't saved CountryCode before assigning it to the Customer, so Hibernate (the underlying ORM for Grails) considers it transient. Basically you do not have any GORM relationships (e.g, has*, belongsTo) defined. By defining a GORM relationship, you gain the ability to have cascade save/delete depending on how the relationships are defined.
Before simply adding hasOne or belongsTo to Customer and CountryCode respectively, you may want to consider how you are using CountryCode. Is CountryCode used as:
one-to-many lookup/reference/dictionary entity where many Customers may be mapped to a particular CountryCode
one-to-one unique entity where there's a unique CountryCode per Customer
To implement #1, you should define just a uni-directional relationship using belongsTo in CountryCode WITHOUT a hasOne in Customer like so:
class CountryCode {
static belongsTo = [customer: Customer]
...
}
This will create a foreign key on the Customer table referencing a particular CountryCode - basically a one-to-many.
To implement #2, you should define a bi-directional relationship using belongsTo in CountryCode WITH a hasOne in Customer like so:
class Customer {
static hasOne = [country: CountryCode]
..
}
class CountryCode {
static belongsTo = [customer: Customer]
..
}
This will create a foreign key on the CountryCode table back to a particular Customer - basically a one-to-one mapping.

Creating domain classes with foreign key constraints in groovy

Hi I am trying to create a domain class with foreign key constraint to another existing domain class
New domain class is ArEntitlement defined as below
package ars
import gra.Resources
class ArEntitlement {
long id
String entitlementname
String entitlementdesc
String entitlementcomm
Integer departmentid
Resources resource
Integer version
static belongsto =[resource : Resources]
static mapping={
table 'ar_entitlement'
id(generator:'increment')
column{
id(column:'id')
}
}
}
Resource domain class is defined as follows (it was created before)
package gra
import com.gra.transaction.UserTransaction
class Resources {
Long id=1
String resourceName
Double resourceType
String resourceOwner
Double riskScore
Integer decommissioned
String resourceClass
Long resourceSegment
String targetIp
String resCriticality
Long resourceGroup
Integer disabled
static hasMany=[userTransactions:UserTransaction]
static mapping = {
table 'resource'
version false
id(generator:'increment')
column{
id(column:'id') }
}
static constraints=
{
resourceName(nullable:true,blank:false)
resourceType(nullable:true,blank:false)
resourceOwner(nullable:true,blank:false)
riskScore(nullable:false,blank:false)
decommissioned(nullable:false,blank:false)
resourceClass(nullable:false,blank:false)
resourceSegment(nullable:false,blank:false)
targetIp(nullable:false,blank:false)
resCriticality(nullable:true,blank:false)
resourceGroup(nullable:false,blank:false)
disabled(nullable:false,blank:false)
}
}
The resulting tables created dont have a foreign key mapping of ar_entitlement table to resource table, it does create a column called 'resource_id' but without foreign key constraint.
I need it to be pointing to id column of resource table
I tried several options, not having the belongsto specifier, using hasOne (in both domain classes) but not getting required foreign key relationship.
Any Idea what the issue is here?
log error i get is
2011-12-21 19:50:17,258 [main] ERROR hbm2ddl.SchemaUpdate - Unsuccessful: alter table ar_entitlement add index FKDCD6161FEFD54E5E (resourceid_id), add constraint FKDCD6161FEFD54E5E foreign key (resourceid_id) references resource (id)
Remove 'Resources resource' row from ArEntitlement.
static belongsTo = [resource : Resources]
Should cover that and having it defined twice might be the issue. Also you shouldn't need to specify the table names, the id generator or id column as it appears you are using the GORM defaults. Try removing these as well.
Here is the modified code...
package ars
import gra.Resources
class ArEntitlement {
String entitlementname
String entitlementdesc
String entitlementcomm
Integer departmentid
Integer version
static belongsTo = [resource : Resources]
static mapping = {
}
}

Grails: master domain field by detail domain field [hasMany]

There are domains:
class Order {
static hasMany = [execDepartments: Department]
String orderNo
//boolean signature
...
}
class Department {
String name
...
}
I want to add boolean signature field for Order but by Department, so I need field signature by execDepartments for Order. Is it possible? Please help.
Is this an m:m relationship?
why not create another domain like
class Order {
static hasMany = [execDepartments: DepartmentOrder]
String orderNo
//boolean signature
...
}
class DepartmentOrder{
boolean signature
Order order
Department department
}
class Department {
String name
static hasMany = [execOrders: DepartmentOrder]
}
Then you can write the belongTo
What you need is a list of signatures in the Order domain.
Possibly an OrderSignature domain that maps signatures to orders.

Resources