I'm working on an inventory management system in Grails, the application will keep track of assets information such as Users, Brand, Type etc. I have 3 domain classes (other class are not important for this discussion) namely, User, Movement and Inventory.
Each assets is owned by a particular User. When the asset is transferred from the current owner to a new owner in the Movement class, the change should be reflected in the User class and Inventory class. These are my classes:
User:
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()
}
}
Movement:
class Movement {
User oldUser
User newUser
Inventory inventoryID
Date movementDate
User userResponsible
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
Date record_date
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()
}
}
I have heard of the GORM method called beforeInsert() and beforeUpdate() that they can do the job, but I'm completely new to Grails, Groovy and of course GORM as this is the first application I develop, I have no idea how to use them in my codes. I did host of research but none match my scenario so I cannot get a clear picture of how to use it.
Question edited from here:
I have made some changes to the class User by using beforeUpdate() and beforeUpdate() but it gets an error shown below.
User class (edited version):
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']
def beforeInsert(){
this.fullName = 'oldUser'
}
def beforeUpdate(){
this.fullName = 'newUser'
}
static constraints = {
userID blank: false, unique: true
fullName blank: false
position()
department()
}
}
Error:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00000000543e4bc2, pid=7488, tid=6520
#
# JRE version: Java(TM) SE Runtime Environment (8.0_05-b13) (build 1.8.0_05-b13)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.5-b02 mixed mode windows-amd64compressed oops)
# Problematic frame:
# V [jvm.dll+0x424bc2]
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# C:\Users\Kanaan\MFEDinventory\hs_err_pid7488.log
#
# Compiler replay data is saved as:
# C:\Users\Kanaan\MFEDinventory\replay_pid7488.log
#
# If you would like to submit a bug report, please visit:
# http://bugreport.sun.com/bugreport/crash.jsp
#
| Error Forked Grails VM exited with error
Related
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
As a follow up question to this, I want to have a User domain class which has an optional one-to-one relationship with BasicProfile domain class, User being the owner which may or may not have a profile. I have this part figured out. What I also want is an optional one-to-one relationship between the AcademicProfile domain class and the User, AcademicProfile being the owner, such that an AcademicProfile may or may not have a User. When I try to replicate this the way I did the first one-to-one relationship, it does not work. Here are my classes.
class User {
String username
String password
String email
AcademicProfile academicProfile
Date dateCreated
Date lastUpdated
static hasOne = [basicProfile: BasicProfile]
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
}
}
class BasicProfile extends Profile {
User user
Date dateCreated
Date lastUpdated
}
class AcademicProfile extends Profile {
String dblpId
String scholarId
String website
Date dateCreated
Date lastUpdated
static hasOne = [user: User]
static hasMany = [publications: Publication]
static constraints = {
dblpId nullable: true
scholarId nullable: true
website nullable: true, url: true
publications nullable: true
user nullable: true
}
}
class Profile {
String firstName
String middleName
String lastName
byte[] photo
String bio
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
}
}
When I run it, I get the error: Field error in object 'org.academic.User' on field 'academicProfile': rejected value [null];. I don't understand what I am doing wrong.
You'll have to add nullable:true constraint for AcademicProfile academicProfile in User class as you mentioned that you need an 'optional' relationship between AcademicProfile & User.
The error itself is self explanatory though, that you can't create a User class's instance, without providing the academicProfile property a non-null value.
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'm sure this has to be one of the most common things done in programming, that is how to associate a certain form submission with a certain logged in user.
I've been trying for quite some time and I just end up running in circles and getting error after error.
My question is, what is the correct way to have a user own the record they submitted?
I've tried many options, unsuccessfully. :\ I even tried the less secure method of creating a String field and inserting the current user in a hidden field in the gsp and that even didn't work right.
static belongsTo = User
in the domain class, great but that only lets me see it in the User form.
Maybe the problem is how it scaffolds. Generate Controllers, Generate Views do a great generic list for ALL records. I need this list to only show THEIR records. but then additionally I need my admins to see ALL records.
Can anyone help point me in the right direction?
SalesOrderController.groovy
#Secured(['IS_AUTHENTICATED_REMEMBERED'])
def list(Integer max) {
params.max = Math.min(params.max ? params.max.toInteger() : 10, 100)
[salesOrderInstanceList: SalesOrder.list(params), salesOrderInstanceTotal: SalesOrder.count()]
// def user = params.id ? SecUser.findByUsername(params.id) : SecUser.get(springSecurityService.principal.id)
// [salesOrderInstanceList: SalesOrder.findAllBySoldBy(user), salesOrderInstanceTotal: SalesOrder.count()]
// if (!user) {
// response.sendError(404)
// return
// }
}
class SalesOrder {
static searchable = {
user(component:true)
}
enum JobStatus {
PENDING, APPROVED, COMPLETE
}
enum JobType {
INSTALL, REPAIR, WARRANTY, TINT
}
enum PaymentType {
INSURANCE, CASH
}
enum InstallLocation {
INSHOP, HOME, BUSINESS
}
enum InstallTime {
MORNING, MIDDAY, AFTERNOON
}
JobStatus jobStatus
JobType jobType
PaymentType paymentType
String custFirstName
String custLastName
String custBestNumber
String custAlternateNumber
String custEmail
String custAddress
String custAddress2
String custCity
String custState
String custZip
String vehicleYear
String vehicleMake
String vehicleModel
String vehicleVin
static hasMany = [glassTypes: GlassType, options: GlassOption]
Date insuranceDateOfLoss
String insuranceCompany
String insurancePolicyNumber
String insuranceClaimNumber
String insuranceDeductible
Date installDate
InstallTime installTime
InstallLocation installLocation
String installCrossStreet1
String installCrossStreet2
String installAddress
String installCity
String notes
Date dateCreated
String soldBy
static constraints = {
jobType blank: false
custFirstName blank: false
custLastName blank: false
custBestNumber blank: false, size:10..10
custAlternateNumber nullable: true, sizeMax: 10
custEmail blank: false, email: true
custAddress blank: false
custAddress2 nullable: true
custCity blank: false
custState blank: false, size: 2..2
custZip blank:false, size: 5..5
vehicleYear blank: false, size:4..4
vehicleMake blank: false
vehicleModel blank: false
vehicleVin blank: false, size:17..17
glassTypes blank: false
options blank: false
insuranceDateOfLoss nullable: true
insuranceCompany nullable: true
insurancePolicyNumber nullable: true
insuranceClaimNumber nullable: true
insuranceDeductible nullable: true
installDate blank: false
installTime blank: false
installLocation blank: false
installCrossStreet1 blank: false
installCrossStreet2 blank: false
installAddress nullable: true
installCity nullable: true
paymentType blank: false
jobStatus blank: false
notes nullable: true, size:0..1024
soldBy blank: false
}
static belongsTo = SecUser
}
<div class="fieldcontain ${hasErrors(bean: salesOrderInstance, field: 'soldBy', 'error')} required">
<label for="soldBy">
<g:message code="salesOrder.soldBy.label" default="Sold By" />
</label>
<g:textField name="soldBy" value="${salesOrderInstance?.soldBy}" default="${sec.loggedInUserInfo(field:'username')}" />
<sec:loggedInUserInfo field="username"></sec:loggedInUserInfo>
I tried following an example where it uses a Service, but that just left me lost as the example only required one field to be passed. as you can see i have more than one, and some many to one joins. Left me with more questions than answers.
Regarding the question of user's seeing their own data vs an admin seeing everyone's data, see my answer to a similar question here.
As far as associating users to their own data...if the data should belong to a user, simply associate the User domain to said data, as you have already done, but with a slight modification:
class SalesOrder {
static belongsTo = [secUser : SecUser]
}
And then add the appropriate mapping to your SecUser class:
class SecUser {
static hasMany = [salesOrders : SalesOrder]
}
Then, when you post the data to the server:
#Secured(['IS_AUTHENTICATED_REMEMBERED'])
def save() {
def authenticatedUser = SecUser.findByUsername(springSecurityService.principal.username)
def salesOrder = new SalesOrder(params)
// this assumes you're SecUser and SalesOrder are a 1-to-many bidirectional association
authenticatedUser.addToSalesOrders(salesOrder)
authenticatedUser.save()
}
There's no need store the info in a hidden field since the same data is available in the controller. Scaffolding is a great way to get started with a proof of concept. However, I never use scaffolding anymore.
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.