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
}
}
Related
Recently I've started with Groovy and Grails but I'm having the following error:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:
Unknown column 'class' in 'field list'
at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
at com.mysql.jdbc.Util.getInstance(Util.java:387)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:942)
But my Entity and its mother doesn't have class attribute
See there's no class field but on MySQL insert somehow its present
class Freight extends Base {
String codeBegin
String codeEnd
BigDecimal weight
BigDecimal value
BigDecimal minValue
FreightRange range
Integer time
FreightType type
String name
Integer leadTime
BigDecimal realFreightValue
String realFreightName
FreightType realFreightType
Manufacturer manufacturer
static embedded = ['range']
static transients = ['type', 'name', 'leadTime', 'realFreightValue', 'realFreightName', 'realFreightType', 'manufacturer']
static belongsTo = [partnerFreightType: PartnerFreightType]
static mapping = {
version false
}
static constraints = {
codeBegin nullable: true, blank: true, validator: Freight.rangeValidator
codeEnd nullable: true, blank: true, validator: Freight.rangeValidator
weight nullable: false, min: BigDecimal.valueOf(0.001)
value nullable: false, min: BigDecimal.valueOf(0), max: BigDecimal.valueOf(9999.99)
minValue nullable: false, min: BigDecimal.valueOf(0)
range nullable: true
time nullable: true
}
static rangeValidator = { val, obj ->
if (obj.codeBegin?.replace('-', '')?.toInteger() > obj.codeEnd?.replace('-', '')?.toInteger())
return 'freight.error.range'
}
String getHash() {
(this.partnerFreightType + this.type + this.value).encodeAsMD5()
}
void useRange(FreightRange range) {
this.range = range
def codes = range.codes()
this.codeBegin = codes.min
this.codeEnd = codes.max
}
}
Since you have a class hierarchy, there must be way (called discriminator) for the ORM to know what class to instantiate when fetching a row from your table.
From GORM documentation : "At the database level GORM by default uses table-per-hierarchy mapping with a discriminator column called class so the parent class (Content) and its subclasses (BlogEntry, Book etc.), share the same table."
see http://gorm.grails.org/6.0.x/hibernate/manual/#inheritanceInGORM
I have the following class. In src/groovy,
class Profile {
String firstName
String middleName
String lastName
byte[] photo
String bio
}
The domain classes BasicProfile and AcademicProfile extend Profile.
class BasicProfile extends Profile {
User user
Date dateCreated
Date lastUpdated
static constraints = {
firstName blank: false
middleName nullable: true
lastName blank: false
photo nullable: true, maxSize: 2 * 1024**2
bio nullable: true, maxSize: 500
}
static mapping = {
tablePerSubclass true
}
}
class AcademicProfile extends Profile {
User user
String dblpId
String scholarId
String website
Date dateCreated
Date lastUpdated
static hasMany = [publications: Publication]
static constraints = {
importFrom BasicProfile
dblpId nullable: true
scholarId nullable: true
website nullable: true, url: true
publications nullable: true
}
static mapping = {
tablePerSubclass true
}
}
Then there is a Publication class.
class Publication {
String dblpId
String scholarId
String title
String description
Date publicationDate
int citations
Date dateCreated
Date lastUpdated
static belongsTo = [AcademicProfile]
static hasOne = [publisher: Publisher]
static hasMany = [academicProfiles: AcademicProfile]
static constraints = {
dblpId nullable: true
scholarId nullable: true
title blank: false, maxSize: 100
description nullable: true, maxSize: 500
publicationDate: nullable: true
academicProfiles nullable: false
}
}
Finally, I have a User class.
class User {
String username
String password
String email
Date dateCreated
Date lastUpdated
static hasOne = [basicProfile: BasicProfile, academicProfile: AcademicProfile]
static constraints = {
username size: 3..20, unique: true, nullable: false, validator: { _username ->
_username.toLowerCase() == _username
}
password size: 6..100, nullable: false, validator: { _password, user ->
_password != user.username
}
email email: true, blank: false
basicProfile nullable: true
academicProfile nullable: true
}
}
My questions are as follows.
I want a relationship where each User may optionally have a Profile (either BasicProfile or AcademicProfile). I tried static hasOne = [profile: Profile] but I got errors saying Profile does not agree to the hasOne relationship. So the current setup I have is a workaround. Is there no way a user can have one Profile be it BasicProfile or AcademicProfile?
Secondly, in the current setup, I get the error: Invocation of init method failed; nested exception is org.hibernate.MappingException: An association from the table academic_profile_publications refers to an unmapped class: org.academic.AcademicProfile when I try to run it. A Google search tells me that this is a problem with classes which are inheriting from other classes. So technically, if I don't have a hasMany relationship in Publication with AcademicProfile, it should work without any issues. But I don't want that. Because a publication has many authors (AcademicProfiles in my case) and an author may have many publications. So is there a way to fix this?
You're not using Hibernate inheritance - that requires that all of the classes be mapped. You're just using regular Java/Groovy inheritance where you inherit properties and methods from base classes. But Hibernate isn't aware of that, so it can't do queries on the unmapped base class.
I'm not sure why it's complaining about AcademicProfile, but it could be a secondary bug caused by the core issue.
I find Hibernate inheritance to be way too frustrating to use in most cases, so I use this approach when there is shared code.
It should work if you move Profile to grails-app/domain. Once you do that you should move the tablePerSubclass mapping config to the base class and only specify it once.
I am using grails searchable plugin to search my domain classes. However, I cannot yet search by my hasMany (skills and interests) fields even though they are of the simple type String. This is my domain class:
class EmpactUser {
static searchable = [except: ['dateCreated','password','enabled','accountExpired','accountLocked','passwordExpired']]
String username
String password
boolean enabled = true
boolean accountExpired
boolean accountLocked
boolean passwordExpired
String email
String firstName
String lastName
String address
String phoneNumber
String description
byte[] avatar
byte[] resume
Date dateCreated
static hasMany = [
skills : String,
interests : String, // each user has the ability to list many skills and interests so that they can be matched with a project.
]
static constraints = {
username blank: false, unique: true
password blank: false
email email: true, blank: false
firstName blank: false
lastName blank: false
description nullable: true
address nullable: true
avatar nullable: true, maxSize: 1024 * 1024 * 10
resume nullable: true, maxSize: 1024 * 1024 * 10
phoneNumber nullable: true, matches: "/[(][+]d{3}[)]d+/", maxSize: 30
}
}
This is the code I am using to search:
def empactUserList = EmpactUser.search(
searchQuery,
[reload: false, result: "every", defaultOperator: "or"])
Am I missing something?
Thanks,
Alan.
Searchable has trouble recognising hasMany relations with Strings. A workaround is to create a new domain object which "belongs to" the parent object, and make a transient variable in the parent class. The following code works as an alternate to the example given in documentation.
class Article {
static searchable = {
root true
keywords (component:true)
}
static transients = ['keywords']
Set<ArticleKeyword> getKeywords() {
ArticleKeyword.findAllByArticle(this) as Set
}
}
class ArticleKeyword {
static searchable = { root false}
static constraints = {
}
String text
static belongsTo = [article:Article]
static mapping = {
text type: 'text'
}
}
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.
I am trying to use the create criteria method in grails but im getting back an empty list im not sure why.
My code is as follow
def results = PostOrder.createCriteria().list() {
posts{
author{
eq('username', lookupPerson().username)
}
}
picture{
user{
eq('username', lookupPerson().username)
}
}
}
PostOrder domain is as follows:
class PostOrder {
String pOrder
Date dateCreated
Picture picture
Post posts
Video video
Boolean favorite = false
static hasMany = [children : Child]
static constraints = {
picture nullable: true
posts nullable: true
video nullable: true
}
}
Post is as follows:
class Post {
String message
User author
Date dateCreated
Child child
boolean postedToAll
String tag
static hasMany = [tags:Tag]
static constraints = {
child nullable: true
tags nullable: true
tag nullable: true
}
}
finally picture is as follows:
class Picture {
String orgName
String urlOrg
String urlWeb
String urlThumb
Date dateCreated
String caption
Child child
User user
Album contained
String tag
boolean postedToAll
static hasMany = [tags:Tag]
static constraints = {
orgName blank: false
caption maxSize: 500
tags nullable: true
caption nullable: true
tag nullable: true
child nullable: true
}
}
To me this would work perfectly fine, can anyone see why is doesn't?
Does it have the same username in both pictures and posts???
If not, you have to surround them with and or{} because by default it is used the and logical
Maybe you should add a logical block (and/or) like this:
def results = PostOrder.createCriteria().list() {
or {
posts{
author{
eq('username', lookupPerson().username)
}
}
picture{
user{
eq('username', lookupPerson().username)
}
}
}
}