Grails: querying hasMany association - grails

I know there are several questions on this subject but none of them seem to work for me. I have a Grails app with the following Domain objects:
class Tag {
String name
}
class SystemTag extends Tag {
// Will have additional properties here...just placeholder for now
}
class Location {
String name
Set<Tag> tags = []
static hasMany = [tags: Tag]
}
I am trying to query for all Location objects that have been tagged by 1 or more tags:
class LocationQueryTests {
#Test
public void testTagsQuery() {
def tag = new SystemTag(name: "My Locations").save(failOnError: true)
def locationNames = ["L1","L2","L3","L4","L5"]
def locations = []
locationNames.each {
locations << new Location(name: it).save(failOnError: true)
}
(2..4).each {
locations[it].tags << tag
locations[it].save(failOnError: true)
}
def results = Location.withCriteria {
tags {
'in'('name', [tag.name])
}
}
assertEquals(3, results.size()) // Returning 0 results
}
}
I have validated that the data is being created/setup correctly...5 Location objects created and the last 3 of them are tagged.
I don't see what's wrong with the above query. I would really like to stay away from HQL and I believe that should be possible here.

Welcome to hibernate.
The save method informs the persistence context that an instance should be saved or updated. The object will not be persisted immediately unless the flush argument is used
if you do not use flush it does the saves in batches so when you setup your query right after the save it appears that the data is not there.
you need to add
locations[it].save(failOnError: true, flush:true)

You should use addTo* for adds a domain class relationship for one-to-many or many-to-many relationship.
(2..4).each {
locations[it].addToTags(tag)
}

Related

Grails - List of Long not getting saved

I have a domain class similar to the following:
class Record {
Long id
List numbers = []
String description
void recordNumber(Long number) {
//requirements, validations, etc.
numbers << number
}
}
Then I defined a Web service similar to the code below:
class RecordController extends RestfulController {
def recordNumber(Record record) {
def number = getNumberFromRequest() //request.JSON, request.XML, etc.
if (record) {
record.recordNumber(number)
record.save(flush: true, failOnError: true)
}
}
}
However, the numbers on the list don't seem to get saved, because when I retrieve a Record, the list empty. I have test for the code and it seems ok. Could it also be that the list is lazily loaded?
You are saving a new record instance each time the action is called. You should load it out of the DB instead:
def recordNumber( Long id ){
def record = Record.get id
def number = getNumberFromRequest() //request.JSON, request.XML, etc.
//....
}
So based on this answer from a previous StackOverflow question, I updated the code as follows:
class Record {
static hasMany = [numbers: Long]
Long id
String description
void recordNumber(Long number) {
//requirements, validations, etc.
addToNumbers number
}
}
It would seem that if a collection is meant to be persistent, it has to be declared this way, or be mapped in some other methods; I'm just not sure what those other methods are.

How to write constraints for hasMany properties in grails

I have three classes in grails application
class Category {
String name
}
class Application {
String name
static hasMany =[specialCategoryies:SpecialCategory]
}
class SpecialCategory {
Category category
Integer points
static belongsTo =[application:Application]
}
Here while I am saving the applicationInstance I don't want save duplicate
specialCategories values like ..specialCategories does not have same
category value again ..
application.addToSpecialCategoryies(newSpecialCategory(category:Category.get(1),points:2))
application.addToSpecialCategoryies(newSpecialCategory(category:Category.get(1),points:3))
here i application instance should rise error that category value repeated..
so how define constraints for hasMany properties in domain class......?
suggest how to write constraints to avoid duplicate values of category
You might try using a custom validator in your Application constraints section. For example, one way to check for duplicate property values is to collect the values into an array and compare them to the corresponding unique array (with duplicated elements removed):
class Application {
String name
static hasMany =[specialCategoryies:SpecialCategory]
static constraints = {
specialCategoryies validator: { specialCategories, obj ->
def specialCategoriesIdArray = specialCategories.collect {it?.category?.getId()}
return (specialCategoriesIdArray.size() == specialCategoriesIdArray.unique().size())?true:'application.validator.specialcategoryduplicate.error'
}
}
}
When trying to save a special category with an existing category, it will throw a validation error when saving. You can test with the following:
def cat1 = new Category(name:"Cat 1").save(flush:true)
def cat2 = new Category(name:"Cat 2").save(flush:true)
def app = new Application()
app.name = "Test"
app.addToSpecialCategoryies(new SpecialCategory(category: Category.get(1), points:2))
app.addToSpecialCategoryies(new SpecialCategory(category: Category.get(2), points:2))
app.addToSpecialCategoryies(new SpecialCategory(category: Category.get(1), points:3))
if ( app.save(flush:true) ){
log.info "Saved!"
} else {
log.error "NOT Saved. Error:"
app.errors.each {
log.error it
}
}

grails gorm cascading save best practice

I have some working code but I'm a little fuzzy on GORM and cascading saves. Here is my object model:
class Profile {
PhotoAlbum photoAlbum
static constraints = {
photoAlbum(nullable:true)
}
}
class PhotoAlbum {
static hasMany = [photos:Photo]
static belongsTo = [profile:Profile]
}
class Photo {
static belongsTo = PhotoAlbum
}
And here is my working code to save a new photo object:
Photo photo = new Photo()
if (!profile.photoAlbum) { profile.photoAlbum = new PhotoAlbum(profile:profile) }
profile.photoAlbum.addToPhotos(photo)
if (!photo.save()) {
def json = [
status:'fail',
message:messageSource.getMessage('label.photo.validate.failed.message', [photo.errorStrings].toArray(), LocaleContextHolder.locale)
]
return json
}
if (!profile.photoAlbum.save()) {
def json = [
status:'fail',
message:messageSource.getMessage('label.photo.validate.failed.message', [profile.photoAlbum.errorStrings].toArray(), LocaleContextHolder.locale)
]
return json
}
if (!profile.save()) {
def json = [
status:'fail',
message:messageSource.getMessage('label.photo.validate.failed.message', [profile.errorStrings].toArray(), LocaleContextHolder.locale)
]
return json
}
else {
def json = [
status:'success',
message:messageSource.getMessage('label.photo.insert.success.message', null, LocaleContextHolder.locale)
]
return json
}
It seems like a lot of code and error checking to save a new photo object. When I look at grails examples on websites and in books, I don't see a lot of error checking. In my case if a photo cannot be saved, the service has to return a json error string to the client. I have read the GORM Gotchas 2 post, but I'm still not clear on cascading saves.
I did find that if I don't call photo.save() and profile.photoAlbum.save() and just call profile.save() I get the transient exception. So I added in photo.save() and profile.photoAlbum.save() to get everything working.
As #Cregg mentioned you should move all your logic to service, in order to handle all calls to DB in one transaction. To not get the transient exception try to use following:
profile.save(flush: true, failOnError:true)
You should create ErrorController to handle all your exceptions. See example.

grails unknown number of model objects in the view

Working in Grails 2.2
I have a situation where I need to be able to handle an unknown number of CommitteeMembers in the view. These need to be both created and displayed.
Each one has the usual attributes - name, address, contact information, userid.
I understand that if I name form fields the same name, Grails will return a collection for me to iterate over. In this case, however, I am faced with this situation:
cm_firstname
cm_lastname
cm_address
cm_email
cm_userid
So does this mean I will be given collections of each of these fields? That is not as useful as there is no way to corelate the various firstnames with the correct lastnames, etc.
I am enjoying Grails and am looking forward to your feedback.
You can use Grails Command objects to do this work for you. Here's an example in a SO question. Basically you will have a single collection of CommitteeMembers that will be populated in your controller thorugh data binding.
As #Gregg says, in the view you need the fields to have an index.
class MyDomain {
String name
}
class MyDomainCommand {
List<MyDomain> instances = ListUtils.lazyList([], FactoryUtils.instantiateFactory(MyDomain))
}
class MyController {
def save() {
MyDomainCommand command = new MyDomainCommand()
bindData(command, params, [include: 'instances'])
}
}
I'll tell you what I do, which may or may not be the best option. I do this mainly because I don't like data binding.
For your case as an example, I would name my fields: "cm.firstName, cm.lastName, cm.address, cm.email, cm.userId".
If you are in a service:
GrailsWebRequest webUtils = WebUtils.retrieveGrailsWebRequest()
List committeeMembers = [].withDefault {new GrailsParameterMap([:], webUtils.getCurrentRequest())}
In a controller:
List committeeMembers = [].withDefault {new GrailsParameterMap([:], request)}
Then
params.cm.each { k, v ->
if (v instanceof String[]) {
v.eachWithIndex { val, idx ->
committeeMembers[idx]."$k" = val
}
}
else {
committeeMembers[0]."$k" = v
}
}
Then you can do:
committeeMembers.each {
<Create from it.firstName, it.lastName, etc>
}

Grails GORM Query with Multiple Objects?

I am trying to write a query in Grails to return a set of results from a domain class, but within those return the relevant results of a separate class whom have the parentId of the main class.
def query = Cars.where {
(colour == 'red')
}
And then within each list item include the set of parts relating to that CAR ID (as an example of what I'm trying to achieve, I know the code is incorrect though....
query.each{
this car. add(Parts.whereCarID{it.id})
}
If you define your domain model properly, you should get it without a criteria involved.
As far as I understand you need to add static hasMany = [parts: Parts] in your Cars domain class, and static belongsTo = [car:Cars] in your Parts class.
So for example, here how it might look:
class Cars {
string colour
static hasMany = [parts:Parts]
// ... rest of your properties
}
class Parts {
static belongsTo = [car:Cars]
// ... rest of your properties
}
And to get your result just do this:
def cars = Cars.findAllByColour('red')
Then you can do:
cars.each { car->
println car.parts // <-- all the parts for each car is here
}

Resources