Grails createCriteria many-to-many - grails

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)
}

Related

Joins in Grails Criteria

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

How to make a "deep" transformation in Grails withCriteria contains projection?

I am using Grails 2.2.4 to build a web application, and now I am having a complex problem with Grails withCriteria operation. I have 3 classes as below:
class Person {
int id
String name
Set<Car> cars = [] // One-to-many relationship
Company currentCompany // One-to-one relationship
}
class Car {
int id
String manufacturer
String brand
double price
Person owner
}
class Company {
int id
String name
Set<Person> employees = []
}
And now I want to query data from Person as root class along with associated data like this:
List result = Person.withCriteria {
projections {
property('id')
property('name')
createAlias('cars', 'c', CriteriaSpecification.LEFT_JOIN)
property('c.brand')
createAlias('currentCompany', 'com', CriteriaSpecification.LEFT_JOIN)
property('com.name')
}
lt('id', 10L)
resultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
}
And the problem is, I don't know how to transform deeply the result data to a List of persons to make sure every single element contains its data as the class structure. I tried many methods like
resultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP)
resultTransformer(CriteriaSpecification.aliasToBean(Person.class))
but nothing worked as I expected.
Does Grails 2.2.4 support this? If yes so what is the correct syntax?
Thank you so much.
Actually, after researching many articles, Grails documentation and even its source code, I think the best way to do this is implementing a custom transformer for my own purpose. The most difficult thing here is how to transform data to association objects and gather them to collection inside the root entity. And I have created one here:
http://www.mycodestock.com/code/10333/
Hope it helps you guys who may need something like mine.

Can't avoid n+1 selects with many to many relationship in Grails

I'm creating a toy Q&A site with Grails in order to learn the platform.
I have two domains, Posts and Tags, with a many to many relationship between them. I want to print a list of posts with their tags.
I can't use lazy fetching, because I would run into the N+1 selects problem
I can't use eager fetching either because it uses a left join and I wouldn't be able to page the results properly.
Therefore I decided to manually fetch the tags with the following code:
static def getList(params) {
ArrayList questions = Question.list(params)
def questionMap = [:]
questions.each {
questionMap.put(it.id, it)
}
if(questions.size()>0) {
Tag.executeQuery('SELECT q.id, t FROM Tag t JOIN t.questions q \
WHERE q.id in ( :list ) ', [ list:questions.collect{ it.id } ] ).each { questionMap.get(it[0]).tags.add(it[1]) }
}
return questions
}
however when I print the tags in my view:
<g:each in="${questions}" var="question">
${question.title}
<g:each in="${question.tags}" var="tag">
${tag?.text}
</g:each>
</g:each>
A query is executed for each question anyway!
What is the recommended approach here?
The problem with your code is that you don't do anything with the result of your Tag query. Also, it's a better approach to have a join class for many-to-many relationships. If you see the Spring Security Core plugin, for example, you have the User, the Role and a join class called UserRole. Here is the example class.
So my suggestion to you is:
class Tag {
...
}
class Question{
...
}
class QuestionTag implements Serializable {
Tag tag
Question question
static mapping = {
id composite: ['tag','question']
...
}
//need to override equals and hashCode
}
To store the result of tags, you can add a transient field for your class:
class Question {
def tags
static transients = ['tags']
//remove the hasMany.
}
You can now execute your HQL, look for the instance of question in your questions list and set the tags attribute. And since you are using one HQL that don't return a single class, the result is not mapped as a Tag object, so the access is a little different.
HQL queries can return domain class instances, or Arrays of specified
data when the query selects individual fields or calculated values
You are saying
"I can't use eager fetching either because it uses a left join and I
wouldn't be able to page the results properly."
You can do paging for an association by using session.createFilter.
This example (copyright Burt Beckwith) is from Burt Beckwith's book Programming Grails from "Chapter 5, Hibernate , session.createFilter"
// example from Burt Beckwith's book "Programming Grails", (c) Burt Beckwith
class Branch {
String name
List visits
static hasMany = [visits: Visit]
List<Visit> getVisitsByPage(int pageSize, int pageNumber) {
Branch.withSession { session ->
session.createFilter(visits, '')
.setMaxResults(pageSize)
.setFirstResult(pageSize * pageNumber)
.list()
}
}
}
I recommend buying this book

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.

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