Grails static mapping fails (repeated id) - grails

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!

Related

Grails: domain object sort mapping not working

Here is my domain object:
package org.olr.nonadmin
import org.olr.admin.User
class Question {
// Integer id // autogenerated by db
// Integer version // autogenerated integer
Date dateCreated // auto filled
Date lastUpdated // auto filled
String qText // question text - can contain markup!
String aText // answer text - can contain markup!
boolean publik // public to all usersot needed
String figureBase64 // image in BASE64 format, used in data-URI for figure
String figureName // figure name (for documentation)
Integer format // how to format for projector display
Integer difficulty // e.g. 1 (easy), 2 (medium), 3 (hard)
Integer gradeLevel // grade: 5,6,7,8,9
static belongsTo = [owner: User]
User owner; // filled by controller save() method
static constraints = {
id generator: 'identity'
owner nullable: false, editable: false
qText sqlType: 'text', nullable: false, widget: 'textarea'
aText sqlType: 'text', nullable: false
figureBase64 sqlType: 'text', nullable: true, blank: true, maxSize: 1024*12
figureName nullable: true, blank: true
format min: 0, max: 5, sqlType: 'smallInt', nullable: true, blank: true
difficulty min: 1, max: 4, sqlType: 'smallInt', nullable: true, blank: true
gradeLevel min: 6, max: 8, sqlType: 'smallInt', nullable: true, blank: true
}
static mapping = {
sort id: "asc"
}
#Override
String toString() {
return "${id}"
}
}
Notice that I would like all Hibernate queries to return Question objects in id order.
Here is the service by which data is retrieved from the Question table:
package org.olr.nonadmin
import grails.gorm.services.Service
import org.olr.admin.User
#Service(Question)
interface QuestionService {
Question get(Serializable id)
List<Question> list(Map args)
List<QuestionFile> findByOwner(User owner) // Added, but implemented by GORM service
Long count()
void delete(Serializable id)
Question save(Question question)
}
When my index view retrieves data (which due to editing has changed the physical order of rows on disk) the data is not listed in id order:
The data is in the order that a raw SELECT statement returns:
Why is
static mapping = {
sort id: "asc"
}
being ignored?
Grails ver. 3.3.1
POSTGRES ver. 11.2
Note: I tried using dateCreated as the sort key -- no luck.
Also tried qText as the sort key without success.

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

Derived Properties using aggregate functions in Grails

I'm trying to create derived properties based on contained objects.
Example below:
class Generation {
String name
DateTime productionStart
DateTime productionEnd
static belongsTo = [line: Line]
static hasMany = [bodyStyles: BodyStyle, engines: Engine, models: Model]
static constraints = {
line nullable: false
name nullable: false, unique: ['line'], maxSize: 255, blank: false
}
static mapping = {
// I've tried but this solution causes errors
productionStart formula: 'MIN(engines.productionStart)'
// I've tried but this solution causes errors
productionEnd formula: 'MAX(engines.productionEnd)'
}
}
class Engine {
String name
Integer horsePower
DateTime productionStart
DateTime productionEnd
static belongsTo = [generation: Generation]
static hasMany = [models: Model]
static constraints = {
generation nullable: false
name nullable: false, unique: ['generation', 'horsePower'], maxSize: 255, blank: false
horsePower nullable: false
productionStart nullable: false
productionEnd nullable: true
}
static mapping = {
productionStart type: PersistentDateTime
productionEnd type: PersistentDateTime
}
}
I've readed Derived Properties Documentation but my case is a little bit more complicated than formulas not associated with complex objects.
The solution that you can find in the code above results in an error::
Caused by GrailsTagException: Error executing tag : Error evaluating expression [Generation.findAll()] on line [23]: could not execute query; SQL [select this_.id as id22_0_, this_.version as version22_0_, this_.line_id as line3_22_0_, this_.name as name22_0_, MAX(engines.productionEnd) as formula0_0_, MIN(engines.productionStart) as formula1_0_ from generation this_]; nested exception is org.hibernate.exception.SQLGrammarException: could not execute query
Another way to try it is to create a getter instead of derived properties:
class Generation {
String name
DateTime productionStart
DateTime productionEnd
static transients = ['productionStart','productionEnd']
static belongsTo = [line: Line]
static hasMany = [bodyStyles: BodyStyle, engines: Engine, models: Model]
static constraints = {
line nullable: false
name nullable: false, unique: ['line'], maxSize: 255, blank: false
}
DateTime getProductionStart() {
def datetime = Engine.createCriteria().get {
eq('generation',this)
projections {
min('productionStart')
}
}
return datetime
}
DateTime getProductionEnd() {
def datetime = Engine.createCriteria().get {
eq('generation',this)
projections {
max('productionEnd')
}
}
return datetime
}
}

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.

How to map Domain Object Properties to Columns

I have this domain class.
class Book {
String code
String description
static mapping = {
table 'Book'
version false
}
}
and I have table BookStore with columns COD and DSC.
I need map to this table.
How can I achieve this?
If I understand your question correct, the sections within Mapping in the documentation should help you
For your example, the following should work:
class Book {
String code
String description
static mapping = {
table 'BookStore'
version false
code column: 'COD'
description column: 'DSC'
}
}
Also, within DataSource.groovy, make dbCreate = "update" under the appropriate environment that you are using. Refer the documentation on DataSource for this.
Hope this helps.
class Book implements Serializable {
String code
String description
static mapping = {
table 'BookStore'
version false
id composite: ['code']
code column: 'COD'
description column: 'DSC'
}
boolean equals(other) {
if (!(other instanceof Book)) {
return false
}
other.code == code
}
int hashCode() {
def builder = new HashCodeBuilder()
builder.append code
builder.toHashCode()
}
}

Resources