Save List of numbers in domain model error? - grails

I am currently trying to save a List of numbers associated to a domain model in Grails and I keep getting errors. So the scenario is:
I have a Client Domain Model that is shown below and this has a HasMany relationship with a PhoneNumbers Domain model that is also shown below. There is a view where a bunch of numbers are added and then these are stored in a String [] for processing on the controller.
Client Domain:
class Client {
String name
List numbers = new ArrayList()
//This represents a message belonging to a single department
static hasMany = [numbers:PhoneNumbers]
static constraints = {
name(blank:false)
}
}
Phone Numbers Domain:
class PhoneNumbers {
String number
//This represents a message belonging to a single department
static belongsTo = [client:Client]
static constraints = {
number(blank:false)
}
}
As you can see the Client hasMany Phone Numbers and the Phone Numbers belongTo a Client. So in my controller I presumed all's I would need to do is to pass these numbers to the Domain in an Array or List and it would handle the cascade save, my controller function is below:
//This is an array of phone numbers stored in a sesson object as String []
def numbers = session.getAttribute("phoneNumbers")
def numbersConvert = numbers as List
def client = new Client(numbers: numbersConvert, name: params.name)
if (!client.save()) {
client.errors.each{
println(it)
}
}
so the controller above I would expect to take the array of numbers which I have confirmed has values and then convert that to a List. Then save the new Client data and cascade save all the numbers that that client has in the List. However this does not work and I get the error below:
GRAILS-7799: Subtype 'java.lang.String' of reloadable type com.tool.PhoneNumbers is not reloadable: may not see changes reloaded in this hierarchy (please comment on that jira)
| Error 2013-07-30 07:56:50,831 [http-bio-8080-exec-4] ERROR property.BasicPropertyAccessor - IllegalArgumentException in class: com.smstool.PhoneNumbers, getter method of property: id
| Error 2013-07-30 07:56:50,836 [http-bio-8080-exec-4] ERROR errors.GrailsExceptionResolver - IllegalArgumentException occurred when processing
object is not an instance of declaring class. Stacktrace follows:
Message: object is not an instance of declaring class
I did also try another approach to the save the data within the controller as shown below however this did not work either:
numbers.each{
def phoneNumber = new PhoneNumber(number: it).save(flush: true)
client.addToPhoneNumber(phoneNumber).save(flush: true)
}
I presume I am missing something silly and this is probably a really easy thing to do just so tired and need a little help.
Thanks in advance

Related

Grails - Command object, service method

I'm not a programming savvy person, so please bear with me.
I've read blog entries and docs about command object. I've never used it and was wondering if I should. (I probably should...)
My project requires parsing, sorting, calculating, and saving results into database when users upload files.
So according to one of the blog entries I read and its corresponding github code,
1) SERVICE should receive file uploads, parse uploaded files (mainly docs and pdfs), sort parsed data using RegEx, and calculate data,
2) COMMAND OBJECT should call SERVICE, collect results and send results back to controller, and save results into the database,
3) CONTROLLER should receive request from VIEW, get results from COMMAND OBJECT, and send results back to VIEW.
Did I understand correctly?
Thanks.
I found this to be the best setup. Here is an example that I use on production:
Command Object (to carry data and ensure their validity):
#grails.validation.Validateable
class SearchCommand implements Serializable {
// search query
String s
// page
Integer page
static constraints = {
s nullable: true
page nullable: true
}
}
Controller (directs a request to a Service and then gets a response back from the Service and directs this response to a view):
class SomeController {
//inject service
def someService
def search(SearchCommand cmd) {
def result = someService.search(cmd)
// can access result in .gsp as ${result} or in other forms
render(view: "someView", model: [result: result])
}
}
Service (handles business logic and grabs data from Domain(s)):
class SomeService {
def search(SearchCommand cmd) {
if(cmd.hasErrors()) {
// errors found in cmd.errors
return
}
// do some logic for example calc offset from cmd.page
def result = Stuff.searchAll(cmd.s, offset, max)
return result
}
}
Domain (all database queries are handled here):
class Stuff {
String name
static constraints = {
name nullable: false, blank: false, size: 1..30
}
static searchAll(String searchQuery, int offset, int max) {
return Stuff.executeQuery("select s.name from Stuff s where s.name = :searchQuery ", [searchQuery: searchQuery, offset: offset, max:max])
}
}
Yes, you understood it correctly except the one thing: command object shouldn't save the data to DB - let service to do that. The other advantage of command object is data binding and validation of data from the client. Read more about command objects here grails command object docs
You can also find helpful information regarding your question in this article
grails best practices
I guess not. Its not really related to whether the save is done in a service it should always attempt to carry out complex stuff and specifically db stuff in a service. so that is regardless. I tend to not use command object but have got hooked on helper classes aka beans that sit in src/main/groovy and do all of the validation and formatting. I just did a form and in it has feedback and reason.
Initially I thought I would get away with
def someAction(String feedback, String reason) {
someService.doSomething(feedback,reason)
}
But then I looked closed and my form was firstly a textarea then the selection objects were bytes so above was not working and to simply fix it without adding the complexity to my controller/service I did this:
packe some.package
import grails.validation.Validateable
class SomeBean implements Validateable {
User user
byte reason
String feedback
static constraints = {
user(nullable: true)
reason(nullable:true, inList:UsersRemoved.REASONS)
feedback(nullable:true)
}
void setReason(String t) {
reason=t as byte
}
void setFeedback(String t) {
feedback=t?.trim()
}
}
Now my controller
class SomeController {
def userService
def someService
def doSomething(SomeBean bean){
bean.user = userService.currentUser
if (!bean.validate()) {
flash.message=bean.errors.allErrors.collect{g.message([error : it])}
render view: '/someTemplate', model: [instance: bean,template:'/some/template']
return
}
someService.doSomeThing(bean)
}
}
Now my service
Class SomeService {
def doSomeThing(SomeBean bean) {
if (bean.user=='A') {
.....
}
}
All of that validation would have still had to have been done somewhere, you say no validation but in a good model you should do validation and set things to be stored in proper structures to reduce overloading your db over time. difficult to explain but in short i am talking about your domain class objects and ensuring you are not setting up String something string somethingelse and then not even defining their lenghts etc. be strict and validate
if you have a text area this will be stored in the back end - so you will need to trim it like above - you will need to ensure the input does not exceed the max character of the actual db structure which if not defined will probably be 255
and by doing
static constraints = {
user(nullable: true)
reason(min:1, max:255, nullable:true, inList:UsersRemoved.REASONS)
Has already invalidated it through the bean.validate() in the controller if the user exceeded somehow my front end checks and put in more than 255.
This stuff takes time be patient
Edited to finally add in that example byte - is one to be careful of -
When adding any String or what ever I have started to define the specific like this and in the case of byte if it is a boolean true false - fine if not then define it as a tinyint
static mapping = {
//since there is more than 1 type in this case
reason(sqlType:'tinyint(1)')
feedback(sqlType:'varchar(1000)')
// name(sqlType:'varchar(70)')
}
If you then look at your tables created in the db you should find they have been created as per definition rather than standard 255 varchar which I think is the default for a declared String.

GORM : Relationship table not updated in many-to-many mapping

I have two domain classes (simplified) which are related by a Many-to-Many relationship.
A Team can have many Players and a Player can belong to many Teams.
When i call the 'save' action of the Team controller :
A player gets saved in the table. (as expected)
A Team gets saved in the table. (as expected)
When i print team.players and player.teams, i see the correct output (See code below)
Nothing is saved in the relationship table (TEAM_PLAYERS). Why is this happening? Do i need to make entries in the join table myself? If this table is not getting updated, how am i seeing the correct output for point 3. ?
Team.groovy is :
class Team {
static hasMany = [players : Player]
String name;
String size;
}
Player.groovy is :
class Player {
static hasMany = [teams : Team]
static belongsTo = Team
String fullName;
String age;
}
TeamController.groovy is :
class TeamController {
def save() {
def player = new Player(fullName : "John Doe", age : "21").save()
def team = new Team(name : "LocalXI", size : "1").addToPlayers(player).save();
println "The players in the team are : " + team.players
println "The teams this player belongs to are : " + player.teams
}
}
Output for the above (when i call 'save' action ) :
The players in the team are : [John Doe]
The teams this player belongs to are : [LocalXI]
I am new to Grails and Groovy and have spent a lot of time trying to figure this out.
It should work. Take into consideration that the save method is transactional by default and it won't actually persist the data until the method is finished. Within the transaction everything looks correct, that is why the println output is correct.
If you are testing this controller via integration test, your data won't be persisted as the transaction is rolled back automatically for each test.
If you are testing this manually via browser make sure your are not using an in-memory DB in your current runtime environment.

Basic problems to use Vaadin 7 + Grails 2.3 (Persistence, domain class design, get Hibernate Session)

I'm using Vaadin 7 + Grails 2.3, there is some questions
My domain classes
class Base {
private static final Date NULL_DATE = new Date(0)
Date createdAt = NULL_DATE;
Date updatedAt = NULL_DATE;
def beforeInsert(){
createdAt = new Date();
updatedAt = new Date();
}
def beforeUpdate(){
updatedAt = new Date();
}
static mapping = {
autoTimestamp true
}
}
abstract class Person extends Base{
String name;
String name2;
String phone1;
String phone2;
static constraints = {
name2 nullable:true
phone1 nullable:true
phone2 nullable:true
}
}
class Customer extends Person {
double credit;
}
THE PROBLEMS
PROBLEM 1
In my Vaadin class UI, if I try this
class MyUI extends UI {
#Override
protected void init(VaadinRequest vaadinRequest) {
Customer customer = new Customer()
customer.name="RODRIGO"
customer.save()
}
}
Show this error
Servlet.service() for servlet [VaadinServlet 0] in context with path [/AgileWeb] threw exception [com.vaadin.server.ServiceException: groovy.lang.MissingPropertyException: No such property: name for class: agileweb.Customer
Possible solutions: all] with root cause
Message: No such property: name for class: agileweb.Customer
Possible solutions: all
there is no "name" property? The class Customer extends Person that has this property.
PROBLEM 2
If I try this
Customer customer = new Customer()
Customer.setName("RODRIGO")
Customer.save()
Show thos error : Message: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
I have seach about this error but I didn't understand to fix it, maybe I'm new with Grails
PROBLEM 3 - ACTUALLY IS A QUESTION
I know that I can use BeanItemContainer, just from List object, that is possible with no problem, but BeanItemContainer is not lazy load, I'd like to use HbnContainer addon (https://vaadin.com/directory#addon/hbncontainer) becase it just need a hibernate session, so How Can I get the "Hibernante session", is there any example and how to do it?
PROBLEM 4 - ACTUALLY IS A QUESTION (AGAIN)
Following this tutorial https://vaadin.com/wiki/-/wiki/Main/Vaadin%20on%20Grails%20-%20Database%20access
It works to save object in the database, but the questions
- Is it really necessary to create a service for each domain class? I have read that it's recomend to put domain logical in the services, I agree with this, but what about simple domain that no need any logical?
so, is there possible to create something like DAO for services? is there any service design to avoid repeted code just to save objects?
I know that are many questions but I think these questions are the same of others, I really want to use Vaadin + Grails to enjoy the better of both, but is not easy to me at the moment!
Before I start answering your question, let me question you domain model. Base class is generally fine, but I want to talk about Person and Customer. You may have good reasons why you picked up inheritance, but please think of composition. Maybe you could have Person that contains a reference to an enum, that states type of the person. You could start here with that: http://en.wikipedia.org/wiki/Composition_over_inheritance
I think you have a typo there. Call save() method on 'customer' not 'Customer', which is a class
When there is a request coming to Grails application, it opens a session and the session is available during that request. There is not this kind of behavior like that in Vaadin. You need to put it into a Service. Yes, you can make generic service to save an object
class GenericService { def save(def domain) { domain.save(failOnError:true) }}
You can get the session factory like this
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
def ctx = AH.application.mainContext
def sessionFactory = ctx.sessionFactory
or
ApplicationContext applicationContext = grailsApplication.mainContext
ConfigurableLocalSessionFactoryBean factory = applicationContext.getBean('&sessionFactory')
As I wrote, you could create GenericService or a service per domain object. Just to keep in mind that GenericService should only save an object and contain no other logic that would be specific for a domain object.

grails removeFrom not working in Integration Test

All of this is on Grails 2.2.3.
I have two classes in a One-to-many relationship, and a service which removes a list of ids
class Box {
String name
static hasMany = [items:ItemDomain]
static constraints = {
items(nullable:true)
}
}
and
class ItemDomain { String name Box box
static belongsTo = Box
static constraints = {
name(blank:false,unique:['box'], maxSize:127)
box(nullable:false) } }
In the service, here's the problem section:
def itemsToDelete = params.itemsToDelete //List of DB ids
List<ItemDomain> items= []
items.addAll(box.items) //Copy the list to avoid concurrent mod exception
for(ItemDomain item : items)
{
if(itemsToDelete.contains(item.id))
{
box.removeFromItems(item)
item.delete()
}
box.save(flush: true)
}
This works fine when running the application, but from integration testing it fails with
InvalidDataAccessApiUsageException: deleted object would be re-saved by cascade (remove deleted object from associations)
If I take out the flush, and eventually it will fail with:
Field error in object 'mypackage.ItemDomain' on field 'box': rejected value [null];
Adding logging, I see the size of box.items before entering the loop is the same as it is after exiting the loop, printing the items in the loop before and after shows that the item.box field for the deleted items changes to null. I've tried messing with the cascade mapping in the parent class... I'm at a loss as to whether I'm doing something wrong or if this is an issue with integration testing. The only similar issues I found were against grails 1.1 and had no resolution in the threads that I found.
I appreciate any feedback.
So, not surprisingly, I was doing something wrong. It turns out that my equals() and hashCode() implementations on the ItemDomain class were including a field that was never supposed to change, but due to requirements creep, was now changing and the methods never got updated properly.

my own id in GORM

I tried to change the standard 'id' in grails:
calls Book {
String id
String title
static mapping {
id generator:'assigned'
}
}
unfortunately, I soon noticed that this breaks my bootstrap. Instead of
new Book (id:'some ISBN', title:'great book').save(flush:true, failOnError:true)
I had to use
def b = new Book(title:'great book')
b.id = 'some ISBN'
b.save(flush:true, failOnError:true)
otherwise I get an 'ids for this class must be manually assigned before calling save()' error.
but that's ok so far.
I then encountered the same problem in the save action of my bookController. But this time, the workaround didn't do the trick.
Any suggestions?
I known, I can rename the id, but then I will have to change all scaffolded views...
That's a feature of databinding. You don't want submitted data to be able to change managed fields like id and version, so the Map constructor that you're using binds all available properties except those two (it also ignores any value for class, metaClass, and a few others).
So there's a bit of a mismatch here since the value isn't managed by Hibernate/GORM but by you. As you saw the workaround is that you need to create the object in two steps instead of just one.
I can't replicate this problem (used Grails 2.0.RC1). I think it might be as simple as a missing equal sign on your static mapping = { (you just have static mapping {)
Here's the code for a domain object:
class Book {
String id
String name
static mapping = {
id generator:'assigned'
}
}
And inside BootStrap.groovy:
def init = { servletContext ->
new Book(name:"test",id:"123abc").save(failOnError:true)
}
And it works fine for me. I see the id as 123abc.
You need to set the bindable constraint to true for your id prop, e.g.
class Employee {
Long id
String name
static constraints = {
id bindable: true
}
}

Resources