Grails - Find by non-native types - grails

I'm very new to grails (day 2).
First, I find it hard to find easily browsable resources (the documentation is very raw, and the tutorials are spread out and only show 'hello world' types of examples ).
I've set up my domain class with a relationship to other data types.
class ShopCategoryPage{
//some stuff
ProductProcedure procedure;
ProductCategory category;
//some stuff
}
In my controller, I am getting a category id and a procedure id as parameters, and I am trying to get the ShopCategoryPage associated with those parameters.
How do I "find" them? I tried passing the ids as procedureId or procedure_id, I tried passing a ProductProcedure object generated by findById ...
I'm not sure how to find by a property that is not of native type.

First, I find it hard to find easily browsable resources (the documentation is very raw,
and the tutorials are spread out and only show 'hello world' types of examples ).
In my opinion the documentation is great, perhaps we're using different docs. I use:
Grails Reference Documentation
Grails JavaDoc
GDK, i.e. methods Groovy adds to Java classes
If that still isn't satisfactory, I highly recommend the book "The Definitive Guide to Grails". I believe "Grails in Action" is also very good, but haven't read it. For learning Groovy, "Programming Groovy" is a great book (albeit a little out of date).
In my controller, I am getting a category id and a procedure id as parameters, and I am
trying to get the ShopCategoryPage associated with those parameters.
The easiest way (though not the most efficient) is to use the dynamic finders.
// First of all load the ProductProcedure and ProductCategory
// I'm assuming here the request params are named 'procedureId' and 'categoryId'
ProductProcedure productProcedure = ProductProcedure.get(params.procedureId.toLong())
ProductCategory productCategory = ProductCategory .get(params.categoryId.toLong())
// Now get the ShopCategoryPage associated with these. Replace 'find' with 'findAll'
// if there could be multiple associated ShopCategoryPages
ShopCategoryPage shopCategoryPage = ShopCategoryPage.findByProcedureAndCategory(productProcedure, productCategory)
A shortcoming of this approach is that it will cause 3 SELECT statements to be executed. If you're only interested in the shopCategoryPage returned by the last query, you could load this in "one shot" using HQL or a criteria query instead.

You should never use findById since it'll bypass the id-based 2nd-level cache and only use the query cache, which is a lot more volatile. Use get() instead.
Having said that, there's two ways to do this. One is to use get():
def shopCategoryPage = ShopCategoryPage.findByProcedureAndCategory(
ProductProcedure.get(params.procedureId),
ProductCategory.get(params.categoryId))
(use the appropriate param name for the two ids)
or using an HQL query (or a criteria query) to fetch the instance in one query instead of 3:
def shopCategoryPage = ShopCategoryPage.executeQuery(
'from ShopCategoryPage p where p.procedure.id=:procedureId and p.category=:categoryId',
[procedureId: params.procedureId.toLong(), categoryId: params.categoryId.toLong()])[0]

I agree, the easiest way is to use an dynamic finder.
In addition to 'The Definitive Guide to Grails' by Rocher and Brown, I suggest the IBM developerWorks track 'Mastering Grails' by Scott Davis.
http://www.ibm.com/developerworks/views/java/libraryview.jsp?site_id=1&contentarea_by=Java&sort_by=Date&sort_order=1&start=1&end=19&topic_by=&product_by=&type_by=All%20Types&show_abstract=true&search_by=mastering%20grails

Related

GRAILS / GORM: Dynamic multiple joins

I've got a complicated query which I can solve using SQL but I can't find a proper GORM way to do the same.
I have Story objects. Each Story object refers to many Tag objects. It's a unidirectional OneToMany relation:
class Story {
...
Collection tags
static hasMany = [
tags: Tag
]
...
}
Several Tags are considered as being "aliased" (technically using a separate "AliasGroup" table, which is not the problem here).
Now, I want to search for multiple tags. Returned stories have to be tagged with ALL of these tags ("Match All tags"). But also, for each of those tags, an 'aliased' tag should also be accepted.
In SQL I solve that by using a dynmically composed sequence of joins. For example, if there are two tags given, both having several "aliases", the resulting SQL statement looks like this (using the actual SQL row names):
select s.id from story
join story_tag st1 on s.id = st1.story_id
join story_tag st2 on s.id = st2.story_id
where
st1.tag_id in (<list of ids for tag1 and all its aliases>) and
st2.tag_id in (<list of ids for tag2 and all its aliases>)
It's important to understand that this cannot be written as a single join, because then the SAME tag would have to be in both groups of tag lists, which is not what I want.
So, this statement is working fine. But I want to achieve the same thing using grails directly. The GORM documentation on joins is rather terse, but I figured that the feature using an association query and 'and' them together would give the correct result. So I tried:
def c = Story.createCriteria()
def stories = c.list {
and {
srchTags.each { tag ->
def ids = []
tag.aliasGroup.aliases.each { alias ->
ids << alias.id
}
tags {
inList('id', ids)
}
}
}
}
This works fine if I just use one single tag as input. I.e. the list of aliased tags ids is properly resolved and the statement is working in principle.
But if I use two or more tags, it doesn't work correctly. The code runs, but GORM seems to just create the wrong SQL query, it seems that it again tries to match each tag with all of the match-lists which cannot work.
I realize that this problem is not easy to understand, and it's even more difficult to describe properly.
Does anyone have a solution how to create a proper Grails query for this problem?
In a project at work now I had several sql queries that I could not translate to gorm(criteria, where). With the need to move forward I used native sql queries in grails and everything goes well.
Please have a look at the article Using Hibernate Native SQL Queries for an explanation of the implementation method.
I know you can not always decide which method to use but having this facility at hand is always useful.
I hope it's useful for you. regards

OGM Custom Query using Custom Label

I have a Neo4j/OGM Entity Person which I mapped to the Label User using
#NodeEntity(label="User).
I now want to write a custom Query MATCH (p:Person) where....
As far as I see, there is no way to use my Application-Side Type Person instead of the Graph-Side Label User like in Hibernate, right?
If there is a way, please explain how to do this, or tell my a key-word to google for.
Same question goes for entity properties.
Thank you.
Update:
Lets say I have a User class like so:
#NodeEntity(label="Person")
class User {
#Property(name="username")
private String name;
...
}
I've used the Mapping to obtain loose coupling so I can eg. rename the Person and won't affect the Neo4j.
And in the Neo4j there are for example Houses with Relationships to Users.
Now I want to load all Houses, referencing a User with the name "Sven", so the Statement would be MATCH (h:House)-[:HOLDS]->(p:Person {username:'Sven'}).
Given, that I might have a huge poject with all the entities in some submodule somewhere else, I might not know, that User is mapped to Person and the user.name is mapped to username, so in a Hibernate environment, I would query as MATCH (h:House)-[:HOLDS]->(u:User {name:'Sven'}). However in OGM this doesn't seem to work.
There might be a way to solve this architectually but in some projects you don't have this choise.
So the question in the End is: Is there some way to get this to work, or do I really need to know the mapping of every entity i use?
You can do this in several ways :
Make your User extend a Person class
if you need more dynamic labels, the class can contain an #Labelsannotated list of labels to apply. See the documentation for more details
About properties, I don't see how this would be useful. Interested to hear about the use case.

groovy, grails: high level questions on extraneous properties and command objects / data binding

Just a few high-level, hopefully very quick questions:
1) If I have a class A with a single field x, is constructing it
def A = new A(x:someVal, y:someVal)
totally fine?
2) Related, is the following a good way to copy relevant parts of a command object into a domain object?
def domainObject = new DomainObject(commandObject.properties).
Where command object has extra properties. Or should it be done instead:
def domainObject = new DomainObject()
domainObject.properties['prop1', 'prop2', ...] = commandObject.properties
or ?
Thanks
For the first question, it's important to distinguish between a vanilla groovy object, and a grails domain object. Groovy objects with throw a MissingPropertyException. Grails domain objects will silently ignore extra properties.
Regarding the second question, initializing grails domain objects with a command object is a common pattern, and generally ok. Params can be a little bit more dangerous. A malicious user can put anything into params so it's best to explicitly spell out what properties you want to assign. Otherwise, things like timestamps and users, or even non-mapped columns like injected spring beans could be affected.

Grails: how can I query domain class' hasMany-relationship against intersecting set of objects?

I have about the following:
class Object_1 {
static hasMany = [tags:Tag]
Set tags;
...
}
Now I have a set of tags and want to find all Object_1-instances with intersecting (!= matching) tags. I was thinking of something like
Object_1.findAllByTagsInList(tags);
But that does not work at all - I get a "nested exception is org.hibernate.exception.SQLGrammarException: could not execute query". I have the feeling I am missing something important. Help highly appreciated.
I actually found an elegant way to solve the problem. I redesigned the relationship to be many-to-many which allows for simply iterating over the tags list finding all the relevant objects.
... of course now I have to take care of that relationship a couple of times - but I am happy to have this working with few locs.
"in list" is not one of the operators recognized in dynamic finder methods - that won't work.
Instead, you'll have to use HQL or the criteria builder to formulate your query (and perhaps put it in a static finder method).

What is your convention for specifying complex SQL queries in Rails?

I am fairly new to Rails and I was curious as to some of the conventions experts are using when they need to construct a very complex SQL query that contains many conditions. Specifically, keeping the code readable and maintainable.
There are a couple of ways I can think of:
Single line, in the call to find():
#pitchers = Pitcher.find(:all, "<conditions>")
Use a predefined string and pass it in:
#pitchers = Pitcher.find(:all, #conditions)
Use a private member function to return a query
#pitchers = Pitcher.find(:all, conditionfunction)
I sort of lean towards the private member function convention, additionally because you could pass in parameters to customize the query.
Any thoughts on this?
I almost never pass conditions to find. Usually those conditions would be valuable to add to your object model in the form of a named_scope or even just a class method on the model. Named scopes are nice because you can chain them, which takes a little bit of the bite out of the complexity. They also allow you to pass parameters as well.
Also, you should almost never just pass a raw SQL condition string. You should either use the hash style (:conditions => { :name => 'Pat' }) or the array style (['name = ?', 'Pat']). This way, the SQL is escaped for you, offering some protection against SQL injection attacks.
Finally, I think that the approach you're considering, where you're trying to create conditions in whatever context you're calling find is a flawed approach. It is the job of the model to provide an interface through which the pertinent response is returned. Trying to determine conditions to pass into a find call is too close to the underlying implementation if you ask me. Plus it's harder to test.

Resources