Grails/GORM Mapping To Non-Standard-Named Foreign Key - grails

I have a table that stores a hierarchy of agents:
create table agent (
agent_id int not null,
agent_name varchar(255),
agent_parent_id,
constraint pk_agent primary key (agent_id));
alter table agent
add constraint fk_agent_agent foreign key (agent_parent_id) references (agent_id);
I've modeled it as:
class Agent {
String agentName
Agent agentParent
static mapping = {
id column: 'agent_id'
id generator: 'sequence', params: [sequence: 'agent_id_seq']
}
}
Each agent may have many properties:
create table agent_property (
agent_property_id int not null,
agent_property_name varchar(255),
agent_id int,
constraint pk_agent_property primary key (agent_property_id));
alter table agent_property (
add constraint fk_agent_property_agent foreign key (agent_id) references agent(agent_id);
I've modeled that as:
class AgentProperty {
String agentPropertyName
static hasOne = [agent: Agent]
static mapping = {
id column: 'agent_property_id'
id generator: 'sequence', params: [sequence: 'agent_property_id_seq']
}
}
I've created a view to easily see the heirarchy of agents:
create view pathogen as
select c.agent_id as id, a.agent_name as genus, b.agent_name as species, c.agent_name as strain, d.agent_name as toxin
from agent a
left join agent b on a.agent_id = b.agent_parent_id
left join agent c on b.agent_id = c.agent_parent_id
left join agent d on c.agent_id = d.agent_parent_id
where a.agent_parent_id is null;
My problem is in modeling the pathogen view. I've done this:
class Pathogen {
String genus
String species
String strain
String toxin
static hasMany = [agentProperties: AgentProperty]
}
This implies that there is a foreign key 'pathogen_id' in the agent_property table. But, that is not the case.
The foreign key is agent_id.
I want AgentProperty to relate to Pathogen on agent_id as if there were the constraint:
alter table agent_propery
add constraint fk_agent_property_pathogen foreign key (agent_id) references pathogen (id);
I tried to map the implied property agentProperties to agent_id in my Pathgeon class, something like:
static mapping = {
agentProperties column: agent_id // or AgentProperty.agent
}
but that didn't work.
How do I tell GORM to use agent_property.agent_id as the foreign key?

The solution to my original problem is that I failed to to put agent_id in quotes.
agentProperties column: 'agent_id'
This works now:
class Pathogen {
String genus
String species
String strain
String toxin
static hasMany = [agentProperties: AgentProperty]
static mapping = {
// use agent_id to releate to AgentProperty
agentProperties column: 'agent_id'
}
}
class AgentProperty {
String agentPropertyName
static belongsTo = [agent: Agent]
static hasOne = [pathogen: Pathogen]
static mapping = {
id column: 'agent_property_id'
id generator: 'sequence', params: [sequence: 'agent_property_id_seq']
// use agent_id to relate to Pathogen
pathogen column: 'agent_id', insertable: false, updateable: false
}
}

Your domain classes needs little bit of modification to stick to the design you have in database,
class Agent {
String agentName
Agent agentParent
//agent_id Foreign Key to AgentProperty. Agent has many AgentProperties
static hasMany = [agentProperties: AgentProperty]
static mapping = {
id column: 'agent_id'
id generator: 'sequence', params: [sequence: 'agent_id_seq']
}
}
class AgentProperty {
String agentPropertyName
//AgentProperty belongs to an Agent. Cascade delete is enabled
static belongsTo = [agent: Agent]
static mapping = {
id column: 'agent_property_id'
id generator: 'sequence', params: [sequence: 'agent_property_id_seq']
}
}
class Pathogen {
String genus
String species
String strain
String toxin
//like foreign key pathogen_id in agent table
static hasMany = [agents: Agent]
}
You can get hold of AgentProperty from Pathogen via Agent.
If I read your question correctly, then this is what you need.
Pathogen hasMany Agents
Agent hasMany AgentProperty

Related

How to represent a many-to-many relationship where the join table contains the identifying type

Running Grails 3.3.10, I have a Product entity which has a many-to-many relationship with a Message table on 2 separate fields (product.type1Messages and product.type2Messages) and the "type" field is located in the join table in the database.
Is it possible to use mappings to get the right messages into the right collections?
Please find my code below:
CREATE TABLE `productmessage` (
`productId` int(11) NOT NULL,
`messageId` int(11) NOT NULL,
`type` int(11) NOT NULL,
PRIMARY KEY (`productId`,`messageId`,`type`)
);
class Product {
static hasMany = [ type1Messages: Message, type2Messages: Message ]
static mappedBy = [ type1Messages: "type1Product", type2Messages: "type2Product" ]
static mapping = {
type1Messages joinTable: [name: 'productmessage', key: 'productId', column: 'messageId']
type2Messages joinTable: [name: 'productmessage', key: 'productId', column: 'messageId']
}
}
class Message {
Product type1Product
Product type2Product
static mapping = {
type1Product column: "productId"
type2Product column: "productId"
}
}
I need my product.type1Messages to only contain those with type 1 in the join table and product.type2Messages to contain those with type 2. Is that possible and, if so, what do I need to add?
Edit
I just realised that I may have confused things by stating many-to-many relationship and then simplifying the example to only display one-to-many. Note that my problem is with the type being in the join table as opposed to how to represent the many-to-many.
I would use some special class for this. Something like this:
abstract class ProductMessage {
Product product
Message message
}
class Type1ProductMessage extends ProductMessage {
}
class Type2ProductMessage extends ProductMessage {
}
class Product {
static hasMany = [ type1Messages: Type1ProductMessage, type2Messages: Type2ProductMessage ]
}
If you do not want to play with abstract classes you can make the first one as main and extend the second one from first.
In the DB it will be mapped as:
'id'
'product_id'
'message_id'
'class'

Grails GORM: Change foreign key column name

I'm trying to change the foreign key column name that is used in Visitor table for User's id. The column is named now user_id, I want to change that to who_id.
Minimal User Domain Class:
class User {
static hasMany = [
visitor: Visitor
]
String uid
...
}
Minimal Visitor Domain Class:
class Visitor {
static belongsTo = [user: User]
....
}
Question:
I've tried with mappedBy but with no success, is there another way to use a property from User as a foreign key in Visitor?
I think you want to use the mapping static block:
class Visitor {
static belongsTo = [user: User]
static mapping = { user column: 'who_id' }
}
You can mark the uid in User as the id (primary key). That will make it automatically the foreign key in the Visitors domain.
class User {
String uid
static mapping = {
id column: 'uid'
}
...

Grails custom names for composite key in many to many relationship in joinTable (legacy db)

grails/gorm seems to ignore column names on join table on many to many relationship if one domain class has composite id, example:
class Following implements Serializable {
...
static hasMany = [operationTypes:OperationType]
static belongsTo = OperationType
static mappedBy = [operationTypes:'followings']
static mapping = {
...
id composite: ['offer', 'user']
offer column: 'offer_oid'
user column: 'user_oid'
operationTypes joinTable: [name: 'operationtype_following', key: ['favorite_user_offer_oid', 'favorite_user_user_oid']]
}
}
and:
class OperationType implements Serializable {
...
static hasMany = [offers:Offer, advices:Advice, followings:Following]
static mappedBy = [followings:'operationTypes']
static mapping = {
....
followings joinTable: [name: 'operationtype_following', key: 'operationtype_oid']
}
}
Results in:
MappingException: Foreign key (FK_lhri681gwbef5a9y6ylhoakpj:operationtype_following [favorite_user_offer_oid, favorite_user_user_oid,Following_offer_id,Following_user_id])) must have same number of columns as the referenced primary key (favorite_user [user_oid,offer_oid])
So why it not really ignores column names but adds generated column names to the specified ones?
Grails 2.4.3 is used. Any help appreciated
I was able to use a composite foreign key in a m:m relationship. this using the following mapping syntax. SITE and MACHINE_FK are the attributes of the join table.
static mapping = {
mstAttributeList {
column name: 'SITE'
column name: 'MACHINE_FK', sqlType: 'nvarchar2'
}
mstAttributeList joinTable: [name: 'MST_MACHINE_ATTRIBUTE', key: ['MACHINE_FK', 'SITE']]
}
static hasMany = [ mstAttributeList : com.gknsintermetals.mda.master.MstAttribute ]

Grails GORM One-to-one relation issue: column: (should be mapped with insert="false" update="false")

i've two db tables as follow:
CREATE TABLE `customer` (
`id` char(36) NOT NULL,
`name` varchar(50) NOT NULL,
`lastname` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `customer_detail` (
`customer_id` char(36) NOT NULL,
`creation_date` date DEFAULT NULL,
`deletion_date` date DEFAULT NULL,
PRIMARY KEY (`customer_id`),
CONSTRAINT `FK_customer_detail_customer_id` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
and two domain classes that map these tables
class Customer {
String name
String lastname
static hasOne = [detail: CustomerDetail]
static mapping = {
id generator: "assigned"
version false
}
static constraints = {
name maxSize: 50
lastname maxSize: 50
detail nullable: true, unique: true
}
}
class CustomerDetail {
Date creationDate
Date deletionDate
static belongsTo = [customer:Customer]
static mapping = {
id generator: "assigned", column: "customer_id", insert: false, update: false
version false
}
}
when I run the application (run-app) I get the following error:
Caused by MappingException: Repeated column in mapping for entity: my.data.domain.CustomerDetail column: customer_id (should be mapped with insert="false" update="false")
If I remove GORM sets the one-to-one reference on Customer.id and CustomerDetail.id but
the field customer_detail.id doesn't exists in db and I can not modify the db structure.
What is the solution to that?
Thank you in advance
Mono
I've found the solution here Grails domain-classes mapping in one-to-one relation so these are the working domain classes:
class Customer {
String id;
String name
String lastname
static hasOne = [detail: CustomerDetail]
static mapping = {
id generator: "assigned"
version false
}
static constraints = {
name maxSize: 50
lastname maxSize: 50
detail nullable: true, unique: true
}
}
class CustomerDetail {
String id;
Date creationDate
Date deletionDate
Customer customer
static mapping = {
id column: '`customer_id`', generator: 'foreign', params: [ property: 'customer'], type: "text"
customer column: '`customer_id`', insertable: false, updateable: false, type: "text"
version false
}
}

Grails domain-classes mapping in one-to-one relation

I have ready database table schema and I need use it in my Grails app.
My tables in PostgreSQL:
CREATE TABLE "user" (
id serial NOT NULL,
login character varying(32) NOT NULL,
password character varying(32) NOT NULL,
email character varying(255) NOT NULL,
date_created time with time zone NOT NULL DEFAULT now(),
last_updated time with time zone NOT NULL DEFAULT now(),
is_banned boolean DEFAULT false,
CONSTRAINT "PK_user_id" PRIMARY KEY (id),
CONSTRAINT "UN_user_email" UNIQUE (email),
CONSTRAINT "UN_user_login" UNIQUE (login)
)
CREATE TABLE profile (
"user" integer NOT NULL DEFAULT nextval('profile_id_seq'::regclass),
first_name character varying(25) NOT NULL,
middle_name character varying(25) NOT NULL,
last_name character varying(25) NOT NULL,
address integer,
CONSTRAINT "PK_PROFILE_user" PRIMARY KEY ("user"),
CONSTRAINT "FK_PROFILE_user_USER_id" FOREIGN KEY ("user")
REFERENCES "user" (id) MATCH SIMPLE
ON UPDATE RESTRICT ON DELETE CASCADE
)
As you can see, "profile" table has primary key, which is its foreign key too. This is main "feature" with which the problems with geails mapping.
My implementation of tables mapping to grails domain classes:
class User {
...
static hasOne = [profile : Profile];
...
}
class Profile {
...
User user;
...
static mapping = {
id name: 'user'
version false
address column: 'address'
user column: '`user`'
};
...
}
This class mapping crash with exception:
Invocation of init method failed; nested exception is org.hibernate.MappingException: Could not determine type for: ru.redlisa.model.User, at table: profile, for columns: [org.hibernate.mapping.Column(user)]
How to correctly map the tables to grails domain-classes?
How to get interaction interface?
like this:
User user = new User();
user.addToProdile(new Profile());
Or
new User(profile: new Profile()).save();
You could try to use this approach:
class User {
...
Profile profile
...
static mapping = {
id column: 'user', generator: 'foreign', params: [ property: 'profile']
...
}
}
Big thanks to araxn1d for showing right way with foreign generator. I've rewrite my domains like this:
class User {
...
static hasOne = [profile : Profile];
...
}
class Profile {
...
static belongsTo = [address: Address,
user: User];
...
static mapping = {
id column: '`user`', generator: 'foreign', params: [ property: 'user']
version false
address column: 'address'
user column: '`user`', insertable: false, updateable: false
};
...
}
and it works!

Resources