Joins in Grails Criteria - grails

I'm trying to write a criteria query in grails, for the following domains:
class Data {
Long createdById // this is user id
// other fields
}
class User {
Company company
// other fields
}
Now data, as the name suggest stores some data. It is not directly related to User but has a field createdById which is the id of User. (I cannot give direct reference of User in Data because these belongs to different projects, where Data is from a custom reusable plugin.)
Now I want to write criteria on Data and list all records where it was created by users of a given company. that is data.id == User.id and user.company == givenCompany

In order to use any GORM/Hibernate query method (criteria, where, or HQL) you need an association between the domain classes. Since you can't make an association from Data to User, you cannot use GORM to write your query with your domain classes are they are. In other words, you need to use SQL; HQL (DomainClass.executeQuery() will not work). But...
With HQL
If you're willing to modify User and maintain some redundant data, you can use HQL.
class User {
Company company
// other fields
/* Add uni-directional one-to-many */
static hasMany = [data: Data]
}
def data = AnyDomainClass.executeQuery('SELECT d FROM User as u INNER JOIN u.data AS d WHERE u.company = :company', [company: givenCompany])
The uni-directional one-to-many association creates a join table which would need to be maintained to reflect Data.createdById. The HQL join syntax hints at the fact that associations are required. If you must use a criteria query...
With criteria query
To use a criteria query you'll need to modify User and add an additional domain class. The reason is that a criteria query cannot project an association like HQL can. For example, this will not work:
def data = User.withCriteria {
eq('company', givenCompany)
projections {
property('data')
}
}
Due to not being able to modify Data, this --which is the simplest solution-- will also not work:
def data = Data.withCriteria {
user {
eq('company', givenCompany)
}
}
So, you need a middle-man in order to project Data instances:
class User {
Company company
// other fields
static hasMany = [userData: UserData]
}
class UserData {
Data data
static belongsTo = [user: User]
}
def data = User.withCriteria {
eq('company', givenCompany)
projections {
userData {
property('data')
}
}
}
As you can imagine, this method also requires some data maintenance.
Using SQL
Lastly, you have the option of using SQL and having the query return domain class instances. It boils down to something like this:
AnyDomainClass.withNewSession { session ->
def data = session.createSQLQuery('SQL GOES HERE').addEntity(Data).list()
}
More info
For more information, check out my following articles:
Domain class associations and how they work on the DB level
Joining domain classes with GORM queries
Using SQL and returning domain class instances. Available on Jan 22nd.

If you are able to create sql query for this and not able to create criteria for this then you can look for executeQuery
I hope it should work

Related

How to model stored procedure records in Grails?

I need to call some stored procedures that return their own kinds of records, that are not directly mapped to tables or views.
I have used stored procedures in the past with groovy.sql.Sql and plain (unmodelled) Maps, but for this application I would like to model those records with something akin to domain classes, in order to define data types, data binding, validation, associations to other entities, and so on.
What is the best way to do so?
Should I model the stored procedure records as proper domain classes (entities) and then try to disable (or redefine) their database persistence? How?
Should I use non-domain POJOs where I selectively enable the features I need? (such as validation with #Validatable) How can I handle associations then? (Associations arise when the record returned from the SP contains a foreign key to some other persisted entity.)
If you want data binding, validation and associations to be maintained in an easy way then you should go with the Domain approach.
To disable database persistence for a domain class you can add static mapWith = "none" to a domain class and a table won't be created for that domain class.
Sample Entity class:
#ToString
public class SPTest {
Long idField
User user
GroupIntegrationKey integrationKey
static constraints = {
}
static mapWith = "none"
}
Stored Procedure statement:
SELECT id AS idField, user_id AS user, key AS integrationKey FROM
my_domain;
In order to map the result of the SP to the entity you can use result transformers.
Query query = session.createSQLQuery("CALL getSPData()");
List<Map> results = query.with {
resultTransformer = AliasToEntityMapResultTransformer.INSTANCE
list()
}
Now iterate over list and create a new entity object
List<MyDomain> list = results.collect {
new MyDomain(it)
}
System.err.println(list)
Drawbacks:
You can not map identifier field
You would have to iterate over result again to create entity objects
You can not map hasMany relationships
If you want to go with pojo, then in that case you would have to create your own version of getters to get associated objects.

Gorm not doing join

I'm trying to build a hierarchy of a class I've called group. The domain is fairly simple:
class SubGroup implements Serializable {
Group child
Group parent
static mapping = {
id composite: ['child', 'parent']
}
}
class Group implements Serializable {
int groupId
String key
String title
static mapping = {
id name: 'groupId'
}
}
Basically I'd like to build up a map of group parents and their associated children. So I'm looping through each record (if there's a cleaner way to query for a Map I'm happy to hear it) and creating the map entry for it.
Map hierarchy = [:]
SubGroup.list().each { relation ->
if (!hierarchy[relation.parent]) {
hierarchy[relation.parent] = new HashSet()
}
hierarchy[relation.parent] << relation.child
}
I would think hibernate would use some simple query that does this something like this:
select * from sub_group s, group c, group p
where s.child_id = c.group_id and s.parent_id = p.group_id
But it is not doing the join. It is doing a query of the subgroup followed by n queries of the group table (N+1 select problem). Ugh. I heard in 2.0 there's a problem with the hibernate query cache so I disabled it. I've tried adding lazy: false, and fetch: join to my SubGroup domain class for both the parent and the child columns with no luck. I've tried adding (fetch: [child: 'eager']) as a parameter to the list method. It will not do the join. Am I missing something? To be honest, it shouldn't really even need to do a join as I am only accessing the groupId foreign key property although later on I will need both the key and title properties.
I could, of course, make the child and parent properties ints and just do my own querying when I need the rest of the data or I could use HQL and some other method for limiting this to a single query, but it seems like GORM should be doing this for me. Thanks for your help.
Regards,
Jim
It seems the only way I could get this to work without hibernate making a select call for each child and parent was to instead do a HQL query rather than using the list method as shown below:
Map hierarchy = [:]
def subGroups = SubGroup.executeQuery("SELECT s.parent, s.child FROM SubGroup s")
subGroups.each { relation ->
if (!hierarchy[relation[0]]) {
hierarchy[relation[0]] = new HashSet()
}
hierarchy[relation[0]] << relation[1]
}
Thus I'm really only using the SubGroup domain class to define the relationships. I would think I should be able to do this via some kind of mapping but I couldn't figure out how.

Grails: find by one-to-many association with String

I have a following domain class:
class User {
static hasMany = [roles:String]
}
I would like to find every user which has role ROLE_ADMIN. Is there any possibility to do that with dynamic finders? user.findAllByRoles('ROLE_ADMIN') seems to give me an error.
UPDATE: it is quite easy to query association where Class A has a list of class B instances and both A and B are domain classes. But here class A is a domain class and class B is a simple Java string.
The code for querying association containing list of another domain objects would look like this:
`User.findAll { roles { role_name=='ROLE_ADMIN' } }`
What i am looking for is a way to specify the value of a String, for example:
`User.findAll { roles {THIS_VALUE=='ROLE_ADMIN' }}`
UPDATE 2: as far as i have found it is not possible to use criteria with collections of primitive types. It is possible to use HQL though:
User.findAll("from User a where :roles in elements(roles)",[roles:'ROLE_ADMIN'])
However it is not as usefull as a findAll or where query. I cannot chain findAll methods so defining other methods that for example: get ROLE_ADMIN users with username like 'xxx' requires rewriting whole HQL query. Maybe it is possible to express above HQL condition in form of a where expression?
Maybe you can do something like:
if you have already a user list (userList)
def list = userList.findAll { user -> user.roles =~ 'ROLE_ADMIN' }
Hope this help!
I have the same problem How to find records by value in their association property via DetachedCriteria
I made some investigation and, as I found, it's impossible.
The GORM DSL itself doesn't have any method to check that value contains in association.
It conains oly that criterias that are in SQL: AND, OR, IN.
But! You can join association as table in criteria Querying by Association Redux

Grails createCriteria many-to-many

Imagine i have the following (this is a search mechanism for my website)
class Supermarket {
String sp_name
String sp_street
}
class Products {
String p_name
String p_price
}
class products_supermarket{
Supermarket sp
Products pro
}
Now i want to create a criteria:
def c = Supermarket.createCriteria()
def results = c.list {
like("sp_street", params.street)
and {
************ ... params.product
}
maxResults(10)
}
Where i have the * i want to be able to find products whithin that supermaked searching on products_supermarket class. How to do that?
PS. If criteria works as an each() method, iterating over all supermarkets, i could use an if-else statment to search for products, and if found i could use: idEq(it), where it is the supermarket id. This way i would make it. My problem is, i dont know how to get current'sm supermarket id. Any help?
and is applied to criterias inside it, so there's no point applying it to a single statement. Top-level criterias are and-ed by defauilt.
You usually better go without connector class, just by using hasMany: Supermarket and hasMany: Product in domain classes. Connector table will be auto-generated by Hibernate.
If you stick with ProductsSupermarket connector class, do add belongsTo: Supermarket and belongsTo: Product to it class, and add 'hasMany: ProductsSupermarket' to other two, or you're losing Grails' GORM advantage.
There's a section "Querying Associations" in the doc.
Object's id is as simple as that: mySupermarket.id, or mySupermarket.ident() if key field is named differently. id field is auto-added to class and table by default.
So the query is:
List<Supermarket> results = Supermarket.withCriteria {
like("sp_street", params.street)
productSupermarket {
product {
idEq(params.product)
}
// or just eq('product', someProduct)
}
************ ... params.product
maxResults(10)
}

Legacy mapping in Grails/GORM: One domain class and two tables in a 1:N-relationship

Let's say I have two tables employee and salary with a 1:N relationship (one salary can be associated with many employees).
In plain SQL the tables would be joined with:
SELECT e.id, e.name, s.salary FROM employee e, salary s WHERE s.id = e.salary_id AND e.id = 12345;
Assuming the following GORM-powered domain class how do I map the legacy database structure to the class?
class Employee {
String name
int salary
}
Clarification #1: I want only one domain class containing data from both tables. Adding another class is hence not an option.
Clarification #2: The question I'm trying to find an answer to is simply "how do I map two tables to one class using Grails/GORM"? If you believe that it is impossible to do so, then please state that clearly in your answer rather than trying to restate the question.
IMO it is not possible with plain Grails/GORM to join multiple tables and map them to one Domain class. As a workaround you could use a legacy XML hibernate mapping and leverage the join feature to achieve your desired goal. Of course you would loose a lot of the GORM goodies.
Your SQL example indicates there are two tables, Employee and Salary. This should also be reflected in your classes. So instead of one, you need two classes. The GORM mapping would then look like this.
class Employee {
String name
Salary salary
}
class Salary {
static hasMany = [ employees : Employee ]
int salary
}
See http://www.grails.org/GORM+-+Defining+relationships
You could, instead of having salary and name as properties, have them as get* methods that actually run a query on both these tables.
granted, that isnt the grails way, and its strongly recommended that you do follow the grails way.
I don't fully understand the limitation on not being able to add another class if there are 2 tables in the database, but if you're looking to have a unified interface, would it work to delegate the methods to the Salary class?
Something like:
class Salary {
int amount
}
class Employee {
Salary _salary
String name
String toString() { name }
public Integer getSalary() {
return _salary?.amount
}
public void setSalary(Integer amount) {
// not quite sure of your business logic here, this is a guess
_salary = Salary.findByAmount(amount)
if (!_salary) {
_salary = new Salary(amount: amount)
_salary.save()
}
}
}
def e = new Employee(name:"willy loman", salary: 100)
e.save()
assert e.salary == 100
It's also possible that you might be able to make what you're asking for work with a custom hibernate mapping file, but I'm not familiar enough with contorting hibernate in that manner to say for sure.
See the Custom User Type section of this page.

Resources