Grails find results in null List for hasMany relationship - grails

I have created an object (which is nested within a nested object) with a hasMany relationship and when I save the object, the items are saved, the list is fully populated. But when I use find to get the object back from the database, the list is null
class Parent {
int age
String name
List<Child> children
static hasMany = [
children: Child
]
}
So when I use Parent.findAll(), I get
{
age: 34
name: 'John Smith'
children: null
}
What's weird is other classes I have don't display this behavior. I set lazy to false for the list, but that doesn't seem to do anything. What gives?
EDIT: Here is the child class
class Child {
static constraints = {
firstName nullable: true
middleName nullable: true
displayName nullable: true
story nullable: true
}
static belongsTo = [
parent: Parent,
teacher: Teacher
]
String firstName
String middleName
String displayName
String story
}

OK, I figured it out. Lazy loading is to blame. All I had to do was add
children lazy: false
to the constraints on the parent class and the problem was solved!

Related

Convert Sql Query to Grails/Gorm query

How do I convert the following SQL query to Grails/Gorm? Can this be done with a basic query? I would like to avoid using Criteria, Projections and HQL to keep it consistent with the structure of the other queries in my code base (which are basic queries).
SELECT dealer_group_user_dealer_users_id, COUNT(user_id)
FROM db.dealer_group_user_user
GROUP BY dealer_group_user_dealer_users_id;
And is it possible to perform the query in the gsp page to display the result for a specific user_id as opposed to running the query in the controller?
To update from a comment made, below is my domain classes.
class DealerGroupUser extends User{
static hasMany = [dealerUsers: User]
static constraints = {
}
}
class User {
transient authService
Boolean active = true
String firstName
String lastName
String title
String username
String emailAddress
String passwordHash
Date lastLoginTime
Date dateCreated
Date lastUpdated
Retailer dealer
Client client
Date passwordUpdated
Long dealerUser
Boolean isReadOnlyClientManager = false
String regionClientManager
// Transient properties
String fullName
String password
static transients = ['fullName', 'password']
static mapping = {
//permissions fetch: 'join'
sort firstName: 'asc' // TODO: Sort on fullName
}
static hasMany = [roles: Role, permissions: String]
static constraints = {
// NOTE: If a username is not provided, the user's email address will be used
firstName maxSize: 30, blank: false
lastName maxSize: 30, blank: false
title maxSize: 50, blank: false, nullable: true
username blank: false, unique: true
emailAddress email: true, unique: false, blank: false
passwordHash blank: false
lastLoginTime nullable: true
active nullable: true
dealer nullable: true
client nullable: true
passwordUpdated nullable: true
dealerUser nullable: true
regionClientManager nullable: true
}
void setEmailAddress(String emailAddress) {
if (EmailValidator.instance.isValid(emailAddress)) {
this.emailAddress = emailAddress
if (!username) {
username = emailAddress
}
}
}
static namedQueries = {
dealerGroupUsers {
eq 'class', 'com.db.torque.DealerGroupUser'
}
}
Integer setPassword(String plainTextPassword) {
plainTextPassword = plainTextPassword?.trim()
if (plainTextPassword) {
if (!plainTextPassword.matches("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=\\S+\$).{8,}\$")){
return -1
}
String previousPassword = this.passwordHash
String newPassword = authService.encrypt(plainTextPassword)
if (previousPassword != newPassword) {
this.passwordHash = newPassword
return 1
}
else {
return -2
}
}
return -1
}
#Transient
public static List<User> findAllByRolesContains(Role role) {
return User.executeQuery("""
SELECT u
FROM User as u
WHERE :role IN elements(u.roles)
""", [role: role])
}
String fullName() {
return "${firstName} ${lastName}"
}
String toString() {
return fullName()
}
}
SELECT dealer_group_user_dealer_users_id, COUNT(user_id)
FROM db.dealer_group_user_user
GROUP BY dealer_group_user_dealer_users_id;
In your user class you have :
Retailer dealer
So lets start from the beginning what is query doing, listing users from user then getting a count of how many times user appears on Retailer domain class ?
The best way to do this would
be
String query = """ select
new map(
u.id as userId,
(select count(d) from DealerGroupUser d left join d.dealerUsers du where du=u) as userCount
)
From user u order by u.id
"""
def results = User.executeQuery(query,[:],[readOnly:true]
This should do what you are doing I think.
And is it possible to perform the query in the gsp page to display the
result for a specific user_id as opposed to running the query in the
controller?
Views are presentation layer and hardcore work should be kept out of it - if needed to use a TagLib call.
Controllers although used in grails examples and rolled out as part of defaults to make things easier also not the best place. You should be doing that in a service which is injected in a controller and presented in view as the actual model of what is needed.
That is the proper way - GSP have a runtime size - keep it short, keep it sweet

Error loading plugin manager in Grails/Groovy

I'm working on a simple grails project when I encountered a problem. I have done lot of research but I haven't found the right answer.
The thing is I have 3 domain classes namely Inventory, User and Movement and the relationship between them is one-to-many for Inventory and Movement and the same for User and Movement so Movement is pretty much in the middle. I managed to connect the Inventory and Movement well but the other relationship shows an error below.
Error |
Error loading plugin manager: Property [movements] in class [classcom.inventory.User] is a
bidirectional one-to-many with two possible properties on the inverse side.
Either name one of the properties on other side of the relationship [user] or use the
'mappedBy' static to define the property that the relationship is mapped with.
Example: static mappedBy = [movements:'myprop'] (Use--stacktrace to see the full trace)
| Error Forked Grails VM exited with error
This are my domain classes:
Users:
class User {
String userID
String fullName
String position
Department department
String toString(){
fullName
}
static hasMany = [inventories: Inventory, movements: Movement]
static constraints = {
userID blank: false, unique: true
fullName blank: false
position()
department()
movements nullable: true
}
}
Movement:
class Movement {
User oldUser
User newUser
Inventory inventoryID
Date movementDate
User userResponsible
//static belongsTo = User
static constraints = {
inventoryID blank: false
oldUser blank: false
newUser blank: false
movementDate()
userResponsible blank: false
}
}
Inventory:
class Inventory {
String inventoryID
String code
String description
String serial_num
Date purchase_date
byte[] image
Date record_date
String remarks
Type type
Brand brand
User user
static hasMany = [movements: Movement]
String toString(){
"$inventoryID, $type"
}
static constraints = {
inventoryID blank: false, unique: true
code blank: false
description nullable: true, maxSize: 1000
serial_num blank: false
purchase_date()
image nullable: true, maxSize: 1000000
record_date()
remarks nullable: true, maxSize: 1000
type()
brand()
user()
}
}
Any idea how to solve the error..??
The problem here is that gorm is unable to distinguish between the newUser and the oldUser on your Movements class. Try adding a mappedBy section and adding another part to your hasMany property to your user class, below is an example that should work:
class User {
String userID
String fullName
String position
Department department
String toString(){
fullName
}
static hasMany = [inventories: Inventory, movementsByOldUser: Movement, movementsByNewUser: Movement]
static mappedBy = [movementsByOldUser: 'oldUser', movementsByNewUser: 'newUser']
static constraints = {
userID blank: false, unique: true
fullName blank: false
position()
department()
movements nullable: true
}
}
For some documentation reference see: http://www.grails.org/doc/2.2.x/ref/Domain%20Classes/mappedBy.html

addTo failing to add

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.

CreateCriteria grails

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

Inconsistency in GORM session if addTo* vs pass as argument at initialization

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.

Resources