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.
Related
I am working with Grails 3.2.8. I would like to allow both options when generating an id. Is there a way to use assigned and falling back to sequence if unassigned? I tried getting the next id within a constructor and setting id there but running into issues. Any help/guidance would be most appreciated.
class Foo {
static mapping = {
id generator:'assigned'
}
}
vs
class Foo {
static mapping = {
id generator:'sequence'
}
}
Ive tried using mapping set to assigned and setting the id within the domain constructor of beforeValidate function. Neither are working for me. Examples below.
class Foo{
Foo(){
def id = Foo.find("from Foo order by id desc")
id = id ? id : 0
this.id = id
}
static mapping = {
id generator:'assigned'
}
}
class Foo{
def beforeValidate() {
def id = Foo.find("from Foo order by id desc")
id = id ? id : 0
this.id = id
}
static mapping = {
id generator:'assigned'
}
}
Thanks in advance for your help.
Is there a way to use assigned and falling back to sequence if
unassigned?
No, not directly anyway. You could use assigned and when you wanted to fall back on the sequence you could send a query to the database to retrieve the next sequence value and then assign it yourself. I think that is probably as close as you can probably get.
First of all, you need to understand HQL; in your examples you are returning Foo not id, so it's clearly wrong.
Second, you shouldn't depend on the table for next value of id, because it's not guaranteed to be correct -- you might face id is not unique kinda errors.
Third, you aren't incrementing the id, this way you will end up getting the same, most likely 0, every time.
Here is what you can do, use database sequence -- depends on the database.
"select id_seq.nextval from dual" // for Oracle
Finally, it's a very bad idea -- unless you are having a very good reason for it.
When using hasMany and belongsTo, i can navigate from the source but not from target backwards to source of relationship.
Example Grails code:
class School {
hasMany [students : Student]
}
class Student {
belongsTo [school : school]
}
// Following works
School scl = new School()
scl.addToStudents(new Student("firstStudent"))
scl.addToStudents(new Student("secondStudent"))
scl.save()
assertEquals(2, scl.students.size())
// Following does not work
School scl = new School()
scl.save() // so that it generated ID and persisted
Student std = new Student(school: scl)
std.save()
assertEquals(2, std.school.students) // This FAILS!
Why is that when we lookup from Student it fails? My understanding is that it should work.
The last line should be:
assertEquals(1, std.school.students.size())
instead of
assertEquals(2, std.school.students)
Try also to re-read objects state before assertion.
By Burt Beckwith
Re-reading instances is typically a no-op due to confusion about Hibernate. If you get() an instance or re-query for multiple and they're already associated with the session you'll just get the same instances back. You need to clear the session (and flush() for good measure) for this to be valid. This is pretty simple to do, e.g. AnyDomainClass.withSession { it.flush(); it.clear() } – Burt Beckwith 6 hours ago
This solution works!
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.
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.
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