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

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
}
}

Related

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!

Grails: How to create formula on an embedded class

When I create a general class to embed it to other class, I want to add a transient property which is defined by a formula, but I do not how to implement this. Here is my source code:
//Embedded
class LocationInfo {
Long country, state, city
String address, fullAddress
static constraints = {
country nullable: true
state nullable: true
city nullable: true
address nullable: true
}
static mapping = {
country column: 'l_country'
state column: 'l_state'
city column: 'l_city'
address column: 'l_address'
fullAddress formula: "SELECT (l_address || ', ' || city.name) FROM system_location city WHERE city.id = l_city"
}
static transients = ['fullAddress']
}
class SystemLocation {
String name
Long parentLocationId
String level
static constraints = {
name blank: false, maxSize: 100
parentLocationId nullable: true
level inList: ['country', 'state', 'city']
}
static mapping = { version false }
}
//Host
class User {
String email
String password
String firstName
String lastName
Team team
UserLevel userLevel
boolean enabled = true
boolean accountExpired = false
boolean accountLocked = false
boolean passwordExpired = false
boolean teamLeader = false
LocationInfo locationInfo
AuditingInfo auditingInfo
static embedded = ['locationInfo', 'auditingInfo']
transient springSecurityService
static constraints = {
email blank: false, unique: true, maxSize: 200
password blank: false, maxSize: 200
firstName blank: false
lastName blank: false
team nullable: true
teamLeader nullable: true
locationInfo nullable: true
auditingInfo nullable: true
}
static mapping = {
team column: "team_id"
userLevel column: "user_level_id"
}
}
The LocationInfo is embedded to User class, nhÆ°ng when I get a specific user by ID and check the value in user.locationInfo.fullAddress, it is always NULL; and the generated SQL does not contains the "SELECT (l_address || ', ' || city.name)..." statement.
I do not know how to use a formula in an embedded class.
Could you please help me solve this?
According to the Grails manual there is no such thing like formula in the mapping settings.
I'd solve this by simply declaring a getter method on your domain class:
class LocationInfo {
Long country, state,
SystemLocation city
String address // n.b. no longAddress here
static constraints = {
country nullable: true
state nullable: true
city nullable: true
address nullable: true
}
static mapping = {
country column: 'l_country'
state column: 'l_state'
address column: 'l_address'
}
static transients = ['fullAddress']
String getFullAddress() {
"$address $city.name"
}
}
N.B. that city is now a reference to another domain class. In your draft this is just an id which makes your domain model hard to navigate.
I also had this problem and I think you can't do this that way.
When you define formula for some derived property, you have to put SQL with names of columns you know. But when this class is used in embedded property column names of that embedded object are changed.
In your case table User has columns location_info_l_city, location_info_l_address. But in formula you used names like l_city, l_address... There is no such column in table User.
I resolved the problem by adding derived property for embedded object's owner.
In your case I would add to class User mapping:
class User {
//...
String fullAddress
//...
static mapping = {
//...
fullAddress formula: "SELECT (location_info_l_address || ', ' || city.name) FROM system_location city WHERE city.id = location_info_l_city"
}
}
Now, you can use column User.fullAddress also in HQL queries.

GORM: one-to-one table mapping using string as a key

I'm trying to create a pair of GORM domain objects with a one-to-one mapping with the caveat that the key between the two tables is not a long, but instead a UUID string/varchar. I've looked all over for discussions on how to do this in grails/gorm, and found a few questions, but no answers of use. Here are my two classes, I hope someone can point me in a reasonable direction.
Thanks.
class ItemLastChange {
static belongsTo = [item: Item]
static mapping = {
cache true
version false
id column: 'item_hash_msb', name: 'itemHashMsb'
item column: 'item_uuid', key: 'uuid', sqlType: 'text', type: 'text', insertable: false, updateable: false
}
static constraints = {
}
long itemHashMsb;
String itemUuid;
String itemMapCode;
String dbActionType;
String version;
Calendar modificationDate;
}
class Item {
static hasOne = [itemLastChange: ItemLastChange]
static mapping = {
cache true
version false
id column:'item_id'
columns {
itemLastChange column: 'uuid', lazy: false, key: 'item_uuid', type: 'text', insertable: false, updateable: false
}
}
static constraints = {
}
ItemLastChange itemLastchange
long id
String uuid
//other fields eliminated
}
... for reasons relating to existing data and tables, etc. having the ItemLastChange table utilize the item_id as the FK is not a doable solution (as much as we all wish it was...)
This results in the following error:
java.sql.SQLException: Invalid value for getLong() - '6890daf634873fbaac307cad258561be'
Where the value '6890daf634873fbaac307cad258561be' is the varchar UUID from the ItemLastChange table.
Per comments below, here's a rough schema:
Item
----
Field Type * Collation Null Key
item_id int(11) NO PRI
item_type_id int(11) {null} YES MUL
organization_id int(11) {null} YES MUL
item_map_code varchar(36) utf8_bin YES
uuid char(32) utf8_bin NO UNI
name varchar(64) utf8_bin NO
...
ItemLastChange
--------------
Field Type Collation Null Key
item_hash_msb bigint(20) NO PRI
item_uuid varchar(32) utf8_bin NO UNI
item_map_code varchar(36) utf8_bin NO
db_action_type varchar(64) utf8_bin NO
item_version varchar(16) utf8_bin NO
description longtext utf8_bin YES
ine_app_version varchar(16) utf8_bin YES
creation_date datetime NO
modification_date datetime NO
There is no defined FK relationship between these tables.
Thanks in advance.
-Steighton
Try declaring your id as a String:
String id
static mapping = {
[...]
id column:'item_id', generator: 'assigned'
[...]
}

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

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

Grails static mapping fails (repeated id)

I'm trying to do reverse engineering and create a model class for a given table schema in a database.
The table's name is infopac_usersProva and it has two columns:
strCip varchar(15) which is the id
USERNM varchar(75)
I have written the model like this:
class Infopac_usersProva {
String strCip
String usernm
static mapping={
datasource 'gpaq'
table 'infopac_usersProva'
version false
columns{
id column: 'strCip'
usernm column: 'USERNM', sqlType: "varchar(75)"
strCip column: 'strCip', sqlType: "varchar(15)"
}
}
static constraints = {
strCip (nullable:true, insert:false, update:false)
}
}
But I get this error:
Repeated column in mapping for entity: edu.upc.gpaq.domain.generic.Infopac_usersProva column: strCip (should be mapped with insert="false" update="false")
I need to specify the column name for strCip because if I take out that line the model is trying to fetch str_cip instead of strCip. And if I take out "id column: 'strCip' then I get an error saying that there is no id column.
What am I doing wrong?
I think that you can get rid of strCip definition.
Instead define the id field properly.
See if this works for you:
class Infopac_usersProva {
String usernm
static mapping={
datasource 'gpaq'
table 'infopac_usersProva'
version false
columns{
id generator: 'assigned', name: 'strCip', type: 'string'
usernm column: 'USERNM', sqlType: "varchar(75)"
}
}
I didn't check this...
This one should work:
class Infopack_usersProva {
String strCip
String usernm
static constraints = {
strCip(nullable: false, maxSize: 15)
usernm(nullable: true, maxSize: 75)
}
static mapping = {
datasource('gpaq')
table('infopac_usersProva')
version(false)
autoTimestamp(false)
usernm(column: 'USERNM')
strCip(column: 'strCip')
id(column: 'strCip', name: 'strCip', generator: 'assigned')
}
}
But it has strCip as not null. But AFAIK you need an id column that is not null so i do not see any way around this. At least with grails/hibernate.
And if you want strCip never be included in any save() you need to specify
strCip(column: 'strCip', insertable: false, updateable: false)
in the mappings block. I am not aware of any constraints called insertor update and would expect them to just get ignored there.
Might be a little late for this, but you need to use the updateable and insertable properties instead. It worked for me:
class Infopac_usersProva {
String strCip
String usernm
static mapping={
datasource 'gpaq'
table 'infopac_usersProva'
version false
columns{
id column: 'strCip'
usernm column: 'USERNM', sqlType: "varchar(75)"
strCip column: 'strCip', updateable: false, insertable: false
}
}
}
I ended up doing the following (see the bold text)
class Infopac_usersProva {
String usernm
String id
static mapping={
datasource 'gpaq'
table 'infopac_usersProva'
version false
autoTimestamp false
columns{
**id column: 'strCip', sqlType: "varchar(15)"**
usernm column: 'USERNM', sqlType: "varchar(75)"
}
}
static constraints = {
}
}
It works now. Thank you a lot!

Resources