I have a case that validation is done on domain properties but not on the an associated (hasMany) properties.
Is there any configuration I can add to enable the validation on both properties (domain and hasMany).
grails version : 3.1.14
Example:
class Person {
String name;
static hasMany = [location: Location]
static constraints = {
name nullable: true
}
}
class Location {
String address
String city
State state
String zip
static constraints = {
address nullable: true
}
}
According to the documentation the validation should work for has-many associations as you wish: http://docs.grails.org/3.1.14/ref/Domain%20Classes/validate.html
But in my test's it does not work eather.
An other solution is to work with the constraints:
static constraints = {
name nullable: true
location validator: {val, obj ->
val.every { it.validate() } ?: 'invalid'
}
}
Related
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 have 2 domain classes: Category and CatAttribute, they have a many-to-one relationship, Category has 2 List of CatAttribute.
class Category {
static constraints = {
description nullable: true
name unique: true
}
static hasMany = [params:CatAttribute, specs:CatAttribute]
static mappedBy = [params: "none", specs: "none"]
static mapping = {
}
List<CatAttribute> params //required attributes
List<CatAttribute> specs //optional attributes
String name
String description
}
and my CatAttribute class:
class CatAttribute {
static constraints = {
}
static belongsTo = [category: Category]
String name
}
When I tried to create new objects, it fails to save:
def someCategory = new Category(name: "A CATEGORY")
.addToSpecs(new CatAttribute(name: "SOMETHING"))
.addToParams(new CatAttribute(name: "onemore attribute"))
.save(flush: true, failOnError: true)
The domain classes here are simplified/data are mocked for illustration purposes only, the real production code is a lot more complex, but the relationship between the two domains is the same.
Validation errors occur on .addToSpec line:
Field error in object 'Category' on field 'specs[0].category': rejected value [null];
This error has to do with me putting 2 lists of CatAttribute objects in the same domain Category, if I remove either of those and proceed with my object creation,everything is perfectly fine, the way I mapped the domain class Category is all based on grails ref
, so i don't think there is anything wrong with the mapping, but if there is, please let me know.
Do you really need the associations as List (do you index), by default they are Set.
Modify Category as below and you should be good:
class Category {
String name
String description
static hasMany = [params: CatAttribute, specs: CatAttribute]
static mappedBy = [params: "category", specs: "category"]
static constraints = {
description nullable: true
name unique: true
params nullable: true //optional
}
}
if you need a 1:m relation.
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)
}
}
}
}
I have Many-To-Many relationship between RentalUnit and Review(there may be review for guests staying in multiple rental units). There is cascading on Delete from RentalUnit to Review but none cascading from Review to RentalUnit
While working with tests, i found following inconsistency in GORM session
def review2 = new Review(rentalUnits: [rentalUnit], ...., isApproved: false).save(flush: true)
review2.addToRentalUnits(rentalUnit2)
The 'rentalUnit2' object will have association to the 'review2' whereas the 'rentalUnit' does not.
How do i ensure consistent session while pass RentalUnit object at initialization or via addTo*?
p.s. Here is complete code
class Review {
String submittedBy
String content
String dateReceived
boolean isApproved
final static DateFormat DATEFORMAT = DateFormat.getDateInstance(DateFormat.MEDIUM)
static belongsTo = RentalUnit
static hasMany = [rentalUnits: RentalUnit]
static mapping = {
rentalUnits cascade: "none"
}
static constraints = {
submittedBy blank: false, size: 3..50
content blank: false, size: 5..255
dateReceived blank: false, size: 11..12, validator: {
try{
Date date = DATEFORMAT.parse(it)
return DATEFORMAT.format(date) == it
}catch(ParseException exception){
return false
}
}
rentalUnits nullable: false
}
}
class RentalUnit {
String name
String nickname
Address address
static hasMany = [reviews:Review]
static mapping = {
reviews cascade: "all-delete-orphan"
}
static constraints = {
name blank: false, unique: true
nickname blank: false
}
}
Your answer is in your question - use addToRentalUnits. It does three things; it initializes the collection to a new empty one if it's null (this will be the case for new non-persistent instances, but not for persistent instances from the database which will always have a non-null (but possibly empty) collection), adds the instance to the collection, and sets the back-reference to the containing instance. Simply setting the collection data just does the first two things.
I am currently working on a grails application. I have two command objects (AccountInfoCommand and EducInfoCommand) in a controller. On my EducInfoCommand, I wanted to check if the yearGraduated property is earlier than the set birthDate(a property of AccountInfoCommand) on its validator constraints. How will I do that?
This is my code for my AccountInfoCommand:
class AccountDetailsCommand implements java.io.Serializable {
String username
String password
String confirmPassword
String emailAddress
Date birthDate
}
This is my code for EducInfoCommand:
class EducInfoCommand implements java.io.Serializable {
Integer graduated
EducationLevel educationLevel
String schoolName
String yearGraduated
String honorsReceived
}
static constraints = {
yearGraduated nullable: false, maxSize:4, blank: false, matches: /([0-9]*)/,
validator: {
Date.parse('yyyy',it) <= new Date() ? true : false
}
}
Please help!
Thanks!
You need some reference to which AccountDetails the EducInfo is for. For example, if you added a username field to the EducInfoCommand you could look up the account details from that (assuming there is an AccountDetails gorm object which is similar to the command object):
class EducInfoCommand implements java.io.Serializable {
String yearGraduated
String username
// ...
}
static constraints = {
yearGraduated nullable: false, maxSize:4, blank: false, matches: /([0-9]*)/,
validator: { val, obj ->
Date.parse('yyyy',val) > AccountDetails.findByUsername(obj.username).birthDate
}
}