So far all the examples of queries I am coming across are geared towards a domain class such as:
Account.where, Account.withCriteria, Account.findxxxx but what if I want to query an instance's properties' properties? For example what if I have c a company instance that has a department d and I want to get a list of all the departments of this company instance that have 12 employees (a property of department) or less? What would be the code for such a query?
Something like:
c.findAllD's(such that d.numberOfEmployees <= 12)
Also, can anyone point me to literature on such instance based queries? I haven't been able to come across it.
The easiest approach would be to make the association bidirectional, i.e.
class Company {
static hasMany = [departments:Department]
}
class Department {
Company company
int numberOfEmployees
static belongsTo = [company:Company]
}
Then you can simply start your queries from the Department end, such as
def c = Company.get(...) // or however you obtain your Company instance
def departments = Department.findAllByCompanyAndNumberOfEmployeesLessThanEquals(c, 12)
looks like you want to use the Named Queries
Related
I'm a Grails newbie and I found the following obstacle:
I have 2 domains: Course and Student, they have a many-to-many relationship (a Course can have several students, a student can enroll in several courses) and the student belongs to the course.
So, when I add a student to a course, I want to be able to find what Courses have added a specific student.
I tried to use:
def s = Student.get(id)
def c = Course.findAllByStudents(s)
But grails keeps telling me "No value specified for parameter 1".
Can you guys throw some light into this?
Course.findAllByStudents expects as parameter Set of Students but you are supplying it with single instance of Student, that's why you are getting "No value specified for parameter 1".
To find in what Courses is Student. If you created domain classes like this:
class Course {
//some Course attributes
static hasMany = [students: Student]
}
class Student {
//some Student attributes
static hasMany = [courses: Course]
static belongsTo = Course
}
then you can simply use s.courses.
If you are not two-way mapping that relationship. You can create criteria like this:
Course.withCriteria {
createAlias 'students', 's'
eq 's.elements', s
}
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
I have one many-to-many relationship in my app which looks like this: class
App {
String type
String name
static hasMany = [users: User]
}
class User {
String email
String regid
static belongsTo = App
static hasMany = [apps:App]
}
Now i want to access all Users of an app, by having its id. I prefer using plain SQL as It is working way faster and I database will have ~one million records. So i need a way to access all users of an app where app id in (array of given apps).
What I am trying is doing something like this:
def result = sql.eachRow('SELECT App_Users from App where id in(?)' [id:params.app_checkbox])
But this throws me exception as column App_users is not available.
I am trying to do this for 4-5 hours and bearing in mind I am not a web developer, it is already driving me crazy, so could you guys give me advice, how to access members of many-to-many relationship with sql?
At last i managed to achieve it with simple criteria:
def results = App.createCriteria().list() {
createAlias('users', 'user')
'in'("id", goodIds)
projections {
count('users')
}
}
Say we have three Grails domain classes as follows:
class Person {
String name
static searchable = true
}
class Boss extends Person { }
class Employee extends Person { }
We then create/persist one Boss and one Employee instance:
def myBoss = new Boss(name:"Boss")
myBoss.save()
def myEmployee = new Employee(name:"Employee")
myEmployee.save()
By default, it seems the ElasticSearch plugin will index these instances as type Boss and Employee, not as type Person.
Given that only one inherited field will be searchable, is there any way to change the ElasticSearch mapping to Person rather than Boss and Employee (without using the low level API)?
This would allow us to use one query to search for all Persons by name, rather than two separate queries on Boss and Employee.
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.