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

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

Related

Grails query on a domain with a property that is a set of strings

I am trying to query a domain that looks like this:
class MyDomain {
String something
String somethingElse
Set someStrings
static hasMany = [someStrings: String]
static mapping = {
//... etc.
someStrings: cascade: 'all'
//... etc.
}
}
The domain is in a dependency I didn't write and can't modify.
I would like to find all MyDomains where the Set someStrings contains, say, 'xyz'.
Please show me how, dynamically, with a criteria, or whatever you consider the best practice, I can do this in Grails. My project and the dependency are using Grails 2.44.
In my opinion, using a Collection as a property in a grails Domain is already an anti-pattern, so asking for a "best practice" on top of that is kind of ironic.
FWIW, here's my attempt.
Grails builds a table for your Set of strings, so you can use a classic SQL query, and bind the results to the domain class, like this:
import myapp.MyDomain
class MyDomainService {
List<MyDomain> findByKeyword(String keyword) {
MyDomain.withSession {session ->
def query = session.createSQLQuery("select distinct main.* from MY_DOMAIN as main inner join MY_DOMAIN_SOME_STRINGS as detail on main.id=detail.MY_DOMAIN_ID where detail.SOME_STRINGS_STRING = :keyword")
query.addEntity(MyDomain.class)
query.setString("keyword", keyword)
query.list()
}
}
}
I could not test the code, so there may be typos. I believe that my table and column names match what grails would generate for your example. In any case the principle of binding a domain class to a resultset works in my code.
UPDATE:
Not sure if it will work, but you could try this:
MyDomain.findAll {
someStrings.contains "xyz"
}
It is theoretically possible within the DSL of where queries, but I haven't tried it. I'd be impressed if they thought about this.

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

GORM get domain object list by property

I'm trying to get a list of domain objects back, but only those that the user is allowed to see. This is based on what granted role that user has from spring security.
What I'd like to be able to do in my controller is something along the lines of
[reportInstanceList: Report.list(params).sort{it.name}]
but only get the reports where
Report.role = SecurityContextHolder.getContext().getAuthentication().getAuthorities()
I have the Spring security stuff in another service class, but for simplicity's sake, its inline here.
Is there a way to direct GORM to only pull the records where the roles match?
Something like:
def reports = Report.createCriteria().list( params ) {
'in'( "role", SpringSecurityUtils.principalAuthorities )
}
[ reportInstanceList: reports, totalCount: reports.totalCount ]
Should work...
This is what dynamic finders are for. You can use the findAllBy finder to get all the objects with an attribute.
Report.findAllByRoleInList(SecurityContextHolder.getContext().getAuthentication().getAuthorities())
You can also sort at the db level by passing a map of params.
Report.findAllByRoleInList(roles, [sort: 'name'])

Querying associations with enum collections in Grails

I'm trying a query in Grails 1.2.1, find all products by tenant type.
My solution works but is very inefficient, first I retrieve all products and then find all matching results for the given tenant.
I found a related bug in JIRA: Enum as collection
class Product {
Set<TenantType> tenants
static hasMany = [tenants: TenantType]
}
enum TenantType {
BICYCLE,
MOTORCYCLE
}
def tenant = TenantType.BICYCLE
Product.list().findAll { product -> tenant in product.tenants }
Is there a more efficient way of querying for this data?
A similar question was asked here and as pointed out in the answer, it looks like Hibernate doesn't support criteria queries over collections of value types like enums. One option is to use an hql query instead:
Product.executeQuery('from Product p inner join p.tenants tenants
where tenants = :tenant', [tenant: TenantType.BICYCLE])
Can be executed without join:
Product.executeQuery('from Product where :tenant in elements(tenants)', [tenant: TenantType.BICYCLE])
Use named query in Product class something like
findByTenantType { tenantType->
tenants{ eq 'value', tenantType}
}
And then access this named query like this -
def product = Product .findByTenantType(TenantType.BICYCLE).get()
see similar blog - http://www.grailsbrains.com/search-parent-through-child-in-aggregation-relationship/

Grails select domain objects based on an enum value in an enum list property

I'm having trouble selecting items from a list of domain objects based on a value in an enum list.
My domain object looks like this:
class Truck {
static hasMany = [ makes: Make ]
}
where a Make looks like this:
enum Make {
KENWORTH, MACK, VOLVO
}
I'm not really sure how do something like Truck.findByMake(Make.MACK) to give me all of the Trucks that have this Make in their list of Makes. That call gives me this error:
No property found for name [make] for class [class Truck]
Any ideas? Grails 1.2.2.
This one's tricky and not supported by the dynamic finders. I also don't know how to do this with Criteria queries, but the HQL would be
def mackTrucks = Truck.executeQuery(
'select t from Truck t left join t.makes make where make=:make',
[make: Make.MACK])
You can make ist with criteria query the answer is her in the forum but you have to customize it. Maybe like this:
Truck.createCriteria.list ={makes{eq('name', Make.MACK)}
}
I think each Enum has the attribute name.

Resources