Grails clear hasMany entries and add new ones error? - grails

I am currently working on a grails applications and I have a list of addresses that are attached to an account. Basically what I want to do is when the Account is edited is displays a current list of all the attached Addresses and then I can delete/add as many as I want from the view. When this data is captured it is picked up by the controller and what I want to do is to be able to clear all of the current Addresses from this account and then create the list again with what exists on the view, my code is below:
Account Domain:
class Account {
String name
Date dateCreated
Date lastUpdated
static hasMany = [addresses:Addresses]
static mapping = {
addresses cascade:"all-delete-orphan"
}
def getAddressesList() {
return LazyList.decorate(
addresses,
FactoryUtils.instantiateFactory(Addresses.class))
}
static constraints = {
name(blank:false, unique: true)
}
}
Address Domain:
class Addresses {
int indexVal
String firstLine
String postcode
String area
static belongsTo = [account:Account]
static mapping = {
}
static transients = [ 'deleted' ]
static constraints = {
indexVal(blank:false, min:0)
}
}
Account Controller:
def update() {
def accountInstance = Account.get(params.id)
if (!accountInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'account.label', default: 'Account'), params.id])
redirect(action: "list")
return
}
if (params.version) {
def version = params.version.toLong()
if (accountInstance.version > version) {
accountInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: 'subscriptions.label', default: 'Subscriptions')] as Object[],
"Another user has updated this Account while you were editing")
render(view: "edit", model: [accountInstance: accountInstance])
return
}
}
accountInstance.properties = params
accountInstance.addresses.clear()
accountInstance.save(flush: true)
....
}
Error:
A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.tool.Account.addresses. Stacktrace follows:
Message: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.tool.Account.addresses
This error seems to be occurring in the controller on line:
accountInstance.save(flush: true)
I have tried several different way to get this to work and would really appreciate some help.

So it seems like you have done some work that Grails can do for you.
class Account {
String name
Date dateCreated
Date lastUpdated
List addresses
static hasMany = [addresses:Address]
static mapping = {
addresses cascade:"all-delete-orphan"
}
static constraints = {
name(blank:false, unique: true)
}
}
class Address {
String firstLine
String postcode
String area
static belongsTo = [account:Account]
}
This will produce the effect you want of having addresses being a list.
I've found either
instance.addresses = null
or
instance.addresses.clear()
to work for me

When you define addresses cascade:"all-delete-orphan" in Account class you don't need static belongsTo = [account:Account] in Addresses. So just try to remove that statement and test your code. See related link.

Related

Saving related entity in grails

I have a problem with saving form values from two domain classes
One class is
class Ip {
String inetAddress
String dns
String os
String toString(){
"${inetAddress}"
}
Hoster hoster
static constraints = {
....
and the second one is just
class Hoster {
static HOSTER_OPTIONS = ["Name1", "Name2", "Name3"]
String name;
String toString(){
"${name}"
}
List ips = new ArrayList()
static hasMany = [ips : Ip]
static constraints = {
name(unique: true, blank: false, inList: HOSTER_OPTIONS)
}
I have a Controller where I handle the data from a form
def systems = new Ip()
systems.inetAddress = params.ip
systems.dns = params.dns
systems.os = params.os
systems.hoster.name = params.hoster
def result = systems.save(flush: true, failOnError: true)
But I didn't get it managed that the data is saved.
You're not associating correctly your domain classes in the controller:
systems.hoster.name = params.hoster
Instead of setting the name, you need to set the instance that exists in the database:
systems.hoster = Hoster.findByName(params.hoster)

how to prevent casade on updates

I'm working in grails and I've noticed some strange behavior to which I'd like to fix but I cannot figure out what I'm missing.
I have a location which can have may buildings. Each building can have multiple suites.
In addition I have a tenant who has a tenant location. The tenant location is used more for historical reporting so that we can see if tenants have moved locations.
The classes look like this
class Location {
String name
String shortName
Country country
LocationTypeEnum locationType = LocationTypeEnum.STUD // as default
static hasMany = [building: Building]
Date dateCreated
Date lastUpdated
static constraints = {
name unique: true, maxSize: 30
shortName unique: true, maxSize: 3
country nullable: false
locationType nullable: false
}
static mapping = {
cache usage: 'nonstrict-read-write', include: 'non-lazy'
}
}
class Building {
String type
String name
Date dateCreated
Date lastUpdated
static belongsTo = [location: Location]
static hasMany = [suites: Suite]
static constraints = {
name unique: true, maxSize: 25
type inList: ["A", "B", "C"], nullable: true
}
String toString() {
name
}
}
class Suite {
int suiteNumber
Date dateCreated
static belongsTo = [building: Building]
static constraints = {
pen(validator: { return it > 0 && (it.toString().length()) <= 3 })
}
String toString() {
suite.toString()
}
}
class Tenant {
static hasMany = [tenantLocation: TenantLocation]
----
Suite suite
String comments
Date dateCreated
Date lastUpdated
boolean active = true
static constraints = {
--------
type
nullable: false
comments maxSize: 5000
}
}
class TenantLocation {
static belongsTo = [tenant: Tenant]
Tenant tenant
Suite suite
Date dateCreated
}
So the idea is that a tenant location is created when tenant is created and a new tenantLocation is created only if and when the current tenant suite changes.
However, what I'm seeing is not only is the tenantLocation being saved (which is what I want) the suite is also being updated (which is not what I want).
For example I have Building 1 and suites 1 - 20 and Building 2 with suites 1 - 25. I have a tenant that is in Building 1 suite 5, they move to Building 2 suite 23. Now all of a sudden Building 2 has two suites with a suite number of 5.
How do I keep my suites from moving from one building to another when I only want the suite the tenant has to change?
The Code that is doing the updating is in the Tenant Controller and looks like this:
def update() {
def tenantInstance = Tenant.get(params.id)
if (!tenantInstance) {
flash.message = message(code: 'default.not.found.message', args: [
message(code: 'tenant.label', default: 'Tenant'),
params.id
])
redirect action: "list"
return
}
if (params.version) {
def version = params.version.toLong()
if (tenantInstance.version > version) {
tenantInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[
message(code: 'tenantInstance.label', default: 'Tenant')] as Object[],
"Another user has updated this Tenant while you were editing")
render view: "edit", model: [tenantInstance: tenantInstance, BuidingListInstance: Building.list()]
return
}
}
tenantInstance.properties = params
if (!tenantInstance.save(flush: true)) {
render view: "edit", model: [tenantInstance: tenantInstance, BuildingListInstance: Building.list()]
return
}
def tenantLocation= TenantLocation.findAllByTenant(tenantInstance)
def locationCheck = tenantLocation.last()
//if tenant location is not null and the suite/building change create new Tenant Location entry
if (tenantLocation!=null)
{
if(locationCheck.pen.id !=tenantInstance.suite.id)
{
def location = new TenantLocation(
tenant: tenantInstance,
suite: tenantInstance.suite,
dateCreated: new Date()
).save(flush:true)
}
}
flash.message = message(code: 'default.updated.message', args: [
message(code: 'tenant.label', default: 'Tenant'),
tenantInstance.id
])
redirect action: "show", id: tenantInstance.id
}

Gorm change primary key, not found with id null

I have read tons of questions about this and it seems that nobody gets it to work.
I am using grails, and I am creating a class that doesn't have id as the primary key.
I get the message "usuario not found with id null". this is the code of my domain class:
class Usuario implements Serializable{
String nombre
String celular
String telefono
String apellido
String password
String nick
static mapping = {
table 'Usuarios'
version false
id composite: ['nick']
}
}
I also tried the normal way with the:
static mapping = {
table 'Usuarios'
version false
id name: 'nick'
}
It actually maps the table the way I want to with the natural key and everything, it inserts the new usuarios, but the gui is incapable of retrieving any objects and shows the error: "usuario not found with id null"
I tried to modify the show(Long id) method, but it wont help either, this is my show method from the controller:
def show(String nick) {
def usuarioInstance = Usuario.get(nick)
if (!usuarioInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'usuario.label', default: 'Usuario'), nick])
redirect(action: "list")
return
}
[usuarioInstance: usuarioInstance]
}
You need to specify the assigned generator:
static mapping = {
...
id column: 'nick', generator: 'assigned'
}
Plus it might be wise to add the following constraints:
static constraints = {
nick blank:false, nullable:false, unique: true
}

How to save associated object in Grails?

I am a grails beginner.
i have a 2domain class
class Employee {
String name
String department
static constraints = {
}
public String toString() {
name
}
}
class Address {
String line1
String line2
Employee employee
static belongsTo = Employee
static constraints = {
}
}
where Address belongs to Employee .. so i have given belongsTo association.
My Employee/create.gsp page takes input for fields specified in Employee and Address.
so on creation of employee , address must be get save automatically .
so what could be the save action in EmployeeController
i have tried some thing like this but did not work.
def save = {
def employeeInstance = new Employee(params)
def addressInstance = new Address(params)
if (employeeInstance.save(flush: true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'employee.label', default: 'Employee'), employeeInstance.id])}"
redirect(action: "show", id: employeeInstance.id)
}
else {
render(view: "create", model: [employeeInstance: employeeInstance])
}
}
how to save this associated model ?
Here you have a one-to-one relationsip - add an address property to the Employee class.
class Employee {
String name
String department
Address address
public String toString() {
name
}
}
Change your belongsTo of the Address like this:
class Address {
String line1
String line2
static belongsTo = [employee: Employee]
}
Now you could create an Employee like this:
def employeeInstance = new Employee(params)
employeeInstance.address = new Address(params)
if (employeeInstance.save(flush: true)) {
// your logic
}
Read the docs (one-to-one relationship) for further informations.

Grails Integration test: Failure...Cannot invoke method addToPosts() on null object

:D
I was following a tutorial in a book, and I did follow it thoroughly.
However, come the part where I am supposed to write an integration test, it suddenly failed saying: Cannot invoke method addToPosts() on null object right after I ran the test. I wonder, what could be wrong... :| Please help! :) Below is the code for the test:
void testFirstPost() {
def user = new User(userID: 'joemillan', password:'youaretheonly',
homepage: 'www.geeee.com').save()
def post = new Post (content: 'hellloo oasdo sjdosa daodkao ')
user.addToPosts(post)
assertEquals 1, User.get(user.id).posts.size()
}
Here is the user class:
class User {
String userID
String password
String homepage
Profile profile
static hasMany=[posts:Post, tags:Tag]
static constraints = {
userID (unique: true, size: 6..20)
password (size: 6..20, validator:{password,userID-> return password !=userID.userID}) //validator = The password must not match the username.
homepage (url:true, nullable: true)
profile (nullable: true)
}
}
Here is the Post class:
class Post {
String content
Date dateCreated
static constraints = {
content (blank:false)
}
static belongsTo = [user:User]
static hasMany = [tags:Tag]
static mapping = {
sort dateCreated: "desc"
}
}
save() returns null if validation fails, and "www.geeee.com" isn't a valid URL. It would work with "http://www.geeee.com".
But you should split the creation and saving into 2 steps so you can check it:
def user = new User(userID: 'joemillan', password:'youaretheonly',
homepage: 'www.geeee.com')
user.save()
assertFalse user.hasErrors()
or use failOnError if you are confident that that part should succeed and only want to test the other parts, e.g.
def user = new User(userID: 'joemillan', password:'youaretheonly',
homepage: 'www.geeee.com').save(failOnError: true)

Resources