Grails createCriteria on abstract domain - grails

I am quite curious how one would use the criteria builder to access fields of an inherited class.
Let's assume we have the following class:
class A {
String fieldOne
String fieldTwo
static hasMany = [childs: AbstractChildClass]
}
and the abstract child class would look like this:
abstract class AbstractChildClass {
Integer valueOne
Integer valueTwo
A a
static mapping
tablePerHierarchy false
}
}
of course there are several extending classes such as:
class ExtendingClassOne extends AbstractChildClass {
DesiredObject desiredObject
static mapping = {
tablePerHierarchy false
}
}
let's also assume there is the class DesiredObject which looks like this:
class DesiredObject {
String infoA
String infoB
}
The question is how one would get the fields infoA and infoB by creating a criteria for class A. My approach so far is this:
A.createCriteria().list {
childs {
desiredObject {
ilike('infoA', '%something%')
}
}
}
This of course does not work because in the table ExtendingClassOne is only the id of the desiredObject and I have no clue how to join the object with the criteria builder to get its fields.
Thank you for reading.
Marco

don't expect 100% match between your domain model and the DB schema it's related to.
In your case the polymorphism would work only with tablePerHierarchy true for AbstractChildClass.
If you do want to stick with tablePerHierarchy false, you can define your class like:
class A {
static hasMany = [ childrenOne:ExtendingClassOne, childrenTwo:ExtendingClassTwo ]
}
thus your query would be as simple as:
A.withCriteria{
for( String c in [ 'childrenOne', 'childrenTwo' ] ){
"$c"{
desiredObject {
ilike('infoA', '%something%')
}
}
}
}

Related

grails: sort by nested attributes

Is it possible to sort by nested attributes using where queries?
I have 2 domain classes:
class Parent {
String name
Child child
}
and
class Child {
String name
static belongsTo = [parent: Parent]
}
This works:
Parent.where {}.list(sort: 'name')
and this doesn't:
Parent.where {}.list(sort: 'child.name')
I have an error:
could not resolve property: child.name of: Parent
I am using grails 2.3.x
See this: Grails - sort by the domain relation attribute (using createCriteria())
Solution 1:
def criteria = Child.createCriteria();
println criteria.list{
createAlias("parent","_parent")
order( "_parent.name")
}
Solution 2:
def criteria = Child.createCriteria();
println criteria.list{
parent {
order("name")
}
}
Solution 3:
class Child {
String name
static belongsTo = [parent: Parent]
public String getParentName(){
return parent.getName()
}
}
println Child.listOrderByParentName()
Hope it helps.

inheritance in Grails domain model

My Grails app's domain model has the following requirements:
a user belong to zero or one organisations
an organisation is either a charity or a company
charities and companies have some some common fields and also some (non-nullable) fields that are unique to each organisation type
I put the common organisation fields into an abstract Organisation class which Charity and Company both extend. I can't store this hierarchy in a single table because there are non-nullable fields that are specific to each organisation type. The relevant parts of the domain model are shown below:
class User {
String name
static belongsTo = [organization: Organization]
static constraints = {
organization nullable: true
}
}
abstract class Organization {
String name
static hasMany = [users: User]
static mapping = {
tablePerHierarchy false
}
}
class Charity extends Organization {
// charity-specific fields go here
}
class Company extends Organization {
// company-specific fields go here
}
When I look at the MySQL schema generated from this model, the inheritance relationship between organisation-company and organisation-charity seems to have been completely ignored. Although there is an organisation table with a name column, it has no primary-foreign key relationship with either company or charity
I see the same result as IanRoberts for both MySQL and H2. In other words: no join table generated, but the expected organization_id FK in the users table.
With "Table per subclass" mapping (tablePerHierarchy false), you end up with an implied one-to-one relationship in the database. Primary Keys for Charity and Company will have the same value as the PK for the parent Organization. The schema generated by GORM/Hibernate3 doesn't appear to enforce this with referential integrity constraints. It's pure Hibernate magic. A bit more detail here
Solved!
Add the class below to src/java (this class cannot be written in Groovy)
package org.example;
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration;
import org.hibernate.MappingException;
import org.hibernate.mapping.JoinedSubclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
import java.util.Iterator;
public class TablePerSubclassConfiguration extends GrailsAnnotationConfiguration {
private static final long serialVersionUID = 1;
private boolean alreadyProcessed = false;
#Override
protected void secondPassCompile() throws MappingException {
super.secondPassCompile();
if (alreadyProcessed) {
return;
}
for (PersistentClass persistentClass : classes.values()) {
if (persistentClass instanceof RootClass) {
RootClass rootClass = (RootClass) persistentClass;
if (rootClass.hasSubclasses()) {
Iterator subclasses = rootClass.getSubclassIterator();
while (subclasses.hasNext()) {
Object subclass = subclasses.next();
// This test ensures that foreign keys will only be created for subclasses that are
// mapped using "table per subclass"
if (subclass instanceof JoinedSubclass) {
JoinedSubclass joinedSubclass = (JoinedSubclass) subclass;
joinedSubclass.createForeignKey();
}
}
}
}
}
alreadyProcessed = true;
}
}
Then in DataSource.groovy set this as the configuration class
dataSource {
configClass = 'org.example.TablePerSubclassConfiguration'
pooled = true
driverClassName = "org.h2.Driver"
username = "sa"
password = ""
dbCreate = "update"
url = "jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
}
I've submitted a pull request to Grails that fixes this. The fix was be included in Grails 2.3.9.
ORM is not RDBS.
tablePerHierarchy false
so You Have three tables: Organization, Charity, Company. User belongs to only Organization (not Charity or Company). How are you going to get value of specific fields?
There is USER. We know ORGANIZATION, but we don't know Charity or Company. I think you underst...
I can suggest you three solutions:
1. tablePerHierarchy true (But you need to have nullable charity\Company -specific fields )
2.
class User {
static belongsTo = [charity: Charity, company: Company]
}
class Charity {
String name
static hasMany = [users: User]
// charity-specific fields go here
}
class Company {
String name
static hasMany = [users: User]
// company-specific fields go here
}
3.
class User {
static belongsTo = [organization: Organization]
}
class Organization {
String name
Charity charity //nullable
Company company //nullable
static hasMany = [users: User]
}
class Charity {
static belongsTo = [organization: Organization]
// charity-specific fields go here
}
class Company {
static belongsTo = [organization: Organization]
// company-specific fields go here
}

Grails GORM: list all with nested property

My (simplified) domain model looks like this:
class Student {
static hasMany = [professions:StudentProfession];
}
class StudentProfession {
static belongsTo = [student:Student];
Profession profession;
}
class Profession {
String name;
}
What is the most efficient way to:
List all students that are taught "Programmer" and "Manager" professions
Am I forced to filter them out after querying the database?
students = students.findAll { student ->
student.professions.find { professionNames.contains(it.profession.name) } != null
}
You can do this using a GORM query:
def studends = Student.where {
professions {
profession.name == "Programmer" || profession.name == "Manager"
}
}
Lots of ways to skin this cat - here's one:
StudentProfession.findAllByProfessionInList(Profession.findAllByNameInList(["Programmer","Manager"])*.student.unique()

How to map Domain Object Properties to Columns

I have this domain class.
class Book {
String code
String description
static mapping = {
table 'Book'
version false
}
}
and I have table BookStore with columns COD and DSC.
I need map to this table.
How can I achieve this?
If I understand your question correct, the sections within Mapping in the documentation should help you
For your example, the following should work:
class Book {
String code
String description
static mapping = {
table 'BookStore'
version false
code column: 'COD'
description column: 'DSC'
}
}
Also, within DataSource.groovy, make dbCreate = "update" under the appropriate environment that you are using. Refer the documentation on DataSource for this.
Hope this helps.
class Book implements Serializable {
String code
String description
static mapping = {
table 'BookStore'
version false
id composite: ['code']
code column: 'COD'
description column: 'DSC'
}
boolean equals(other) {
if (!(other instanceof Book)) {
return false
}
other.code == code
}
int hashCode() {
def builder = new HashCodeBuilder()
builder.append code
builder.toHashCode()
}
}

One's more about grails searchable plugin

I have two simple domains:
public class Hotel {
static searchable = true
Source source
City city
HotelType type
long sourceid
float lat
float lon
static hasMany = [hotelTexts:HotelText]
static mapping = {
hotelTexts batchSize:10
}
}
public class HotelText {
static searchable = true
static belongsTo = [hotel:Hotel]
String lang
String name
String description
String address
static mapping = {
batchSize:10
description type:"text"
}
}
I'm totally new in searchable plugin but i believe that it could help me with my problem.
So, the task is to find Hotels by city and then sort result by name. Without sorting it could be easily done with dynamic finders help but...
Summary:
Find hotels by city.
Sort result by hotel name(for given language).
Support pagination.
public class Hotel {
static searchable = {
hotelTexts component: true
}
...
}
public class HotelText {
static searchable = {
name boost: 2.0
}
...
}

Resources