Imagine I have two classes in Groovy that look like that:
class Person {
int id;
String name;
}
class Item {
int id;
int price;
}
Now it would be simple to create a JasperReport listing all persons' names using the following SQL:
SELECT name FROM Person
Also, it would be easy to pass a Model from which the list should be created:
def p = Person.withCriteria {
eq('name','SomeGuy')
}
chain(controller:'jasper',action:'index',model:[data:p],params:params)
But what I want to do is to use the following query:
SELECT name, (SELECT sum(i.price) FROM Item f WHERE f.id=p.id) as priceSum FROM Person p
And now this is the part where I don't know how to go on: How can I pass any Model to the jasperReport? I can't just use
def p = Person.withCriteria {
eq('name','SomeGuy')
}
because then, the priceSum attribute would be missing.
What I would like to do is something like this:
def l = ['name':'SomeGuy','priceSum':'5000']
chain(controller:'jasper',action:'index',model:[data:l],params:params)
But this doesn't work either, it gives me:
Message: Cannot get property 'name' on null object
Caused by: java.lang.NullPointerException: Cannot get property 'name' on null object
Is there anything simliar to this that would work?
Related
In Groovy to set a bean you just need to give the GroovyRowResult while creating object.
Consider below People.groovy bean:
class People {
String name
int age
}
My sql query:
select * from People -- returns name and age of people
the GroovyRowResult is returned with column names (keys) in capitals like it:[NAME:"Alex", AGE: 21].
So when I try to set the bean like below:
le.rows(sqlQuery).each {
People p = new People(it)
}
I receive the Exception:
groovy.lang.MissingPropertyException: No such property: NAME for class: People. Possible solutions: name
I guess I can modify sql query to include double quotes on the alias, but have you guys handled it any different?
The rows() method returns a List<GroovyRowResult> where GroovyRowResult implements Map and then you can apply collectEntries to transform it, so that the keys are lowercase and you can use the resulting map:
sql.rows('select * from people').each {
People p = new People(it.collectEntries { k,v -> [k.toLowerCase(), v] })
println p.name
println p.age
}
I can't find this anywhere, but it seems pretty trivial. So, please excuse if this is a duplicate.
I have something like:
public class Doctor : Entity
{
...some other properties here...
public virtual string Email { get; set; }
}
public class Lawyer : Entity
{
...some other properties here...
public virtual string Email { get; set; }
}
I want to return all doctors where there is no email match in the Lawyers table like:
select * from Doctors d
where d.Email not in
(select l.Email from Lawyers l where l.Email is not null)
or using a join:
select d.* from Doctors d
left join Lawyers l on l.Email = d.Email
where l.Email is null
The problem is that the Email is of course not set up as a foreign key. I have no mapped property on the Doctor entity that maps to Lawyer.
What I've tried so far:
ICriteria criteria = Session.CreateCriteria(typeof(Doctor))
.CreateAlias("Lawyers.Email", "LawyerEmail", JoinType.LeftOuterJoin)
.Add(Restrictions.IsNull("LawyerEmail"));
return criteria.List<Doctor>();
But, I get a "cannot resolve property Lawyer of MyPlatform.MyNamespace.Doctor" error. Any ideas how to set up my DoctorMap and adjust the criteria tomfoolery to achieve this?
NHibernate for the loss........Entity Framework for the win....
We can achieve that with a feature called subquery:
// a inner SELECT to return all EMAILs from Lawyer table
var subQuery = DetachedCriteria.For<Lawyer>()
.SetProjection(Projections.Property("Email"));
// the root SELECT to get only these Doctors
var criteria = session.CreateCriteria<Doctor>();
// whos email is not in the sub SELECT
criteria.Add(Subqueries.PropertyNotIn("Email", subQuery));
// get first 10
var result = criteria
.SetMaxResults(10)
.SetFirstResult(0) // paging
.List<Doctor>();
In my grails project I create instances of an object called Patient, that has got a field city.
class Patient {
String name;
String surname;
String cf;
String address;
String cap;
String city;
String province;
String country;
String phone_1;
String phone_2;
String email;
String vat;
}
When I create a Patient, I store city by its id, but I want that, in show() and list() methods, I see the name of the related city.
The Cities domain class is linked to a table in my db as follows
class Cities {
String cityName;
String capOfCity;
String provinceOfCity;
static constraints = {
}
static mapping = {
table 's_cap'
id column: 'idcap'
cityName column: 'comune'
capOfCity column: 'cap'
provinceOfCity column: 'prov'
version false
}
}
I suppose that I must perform a query to db to get its name by id, but how can I do it in gsp?
With your current approach you can do
def show(){
// look up your patient object
def patient = Patient.get(123)
def patientCityObject = Cities.findByCityName(patient.city)
[patientCityObject: patientCityObject ]
}
GSP:
<p>${patientCityObject.cityName}</p>
However,
If you define the association between your domains then Grails will load your city when you are accessing it. To access you city object associated with Patient, you can define it as follow:
class Patient {
...
Cities city;
...
}
Then when you have the patient object, you can easily access its property city.
def patient = Patient.get(123)
patient.city.cityName
This will give you the city object associated with your patient. Then you can pass that into your GSP and use it.
To learn more about GORM and object relation you can read Object Relational Mapping
How do I handle this SQL query in grails in my ProductsController script? Notice its two tables with a join on the product id.
SELECT p.*,pd.*
FROM products p, products_description pd
WHERE p.products_id=pd.products_id
ORDER BY p.products_date_added DESC
Obviously I can't do this:
def all= {
def a
a = Products.find("FROM products p, products_description pd
WHERE p.products_id=pd.products_id ORDER BY p.products_date_added DESC")
render a as JSON
}
If you are adamant on using a custom sql query instead of any grails dynamic finder, you can use the following code:
def session = sessionFactory.getCurrentSession() // a reference to the sessionFactory is injected in all controllers and services that have a sessionFactory variable defined
Query query = session.createSQLQuery("SELECT p.*,pd.*
FROM products p, products_description pd
WHERE p.products_id=pd.products_id
ORDER BY p.products_date_added DESC");
def result = query.list()
You will have to add a variable named sessionFactory to your controller. Something like this:
class ProductsController = {
def sessionFactory
The result list would be a list of lists. Each element of the main list would be a list of size 2 with the first element as the product and the second as the product description.
You can use a Many-to-one and one-to-one relation and let GORM take care of the association for you.
class Products {
ProductsDescription description
...
static mapping = {
description column: 'products_id'
}
}
You can then access the description by reference:
a = products.listOrderByProductsDateAdded(order: 'desc')
a.description.productsDescription
I'm trying to write a query in NHibernate. I don't really care if I use the Criteria API or HQL, I just can't figure out how to write the query.
Here's my model:
public class LogEntry { public DateTime TimeCreated { get; set; } }
public class Note : LogEntry { public string Content { get; set; } }
public class Workflow { public IList<LogEntry> Log { get; set; } }
I want the query to return all Workflows that which contain a Note with specific words in the Content of the note.
In pseudo-SQL, I'd write this like:
select w.*
from Workflow w
join w.Log l where l is class:Note
where (Note)l.Content like '%keyword%'
I'm not sure about the Criteria API, but HQL seems to handle polymorphic queries quite well, even when searching on a property that only exists in a specific sub-class. I would expect the following to work:
from Workflow w join w.Log l where l.class = Note and l.Content like '%keyword%'
I don't know if there is a better way, but I use subqueries for this:
from Workflow w
join w.Log l
where l in (
select n
from Note n
where n.Content like '%keyword%'
)
(if this doesn't work, write l.id in (select n.id...)
In criteria, you can directly filter properties that are only available on a subclass, but you shouldn't, because it only filters for the first subtype it finds where this property is defined.
I use subqueries as well:
DetachedCriteria subquery = DetachedCriteria.For<Note>("n")
.Add(Expression.Like("n.Content", "%keyword%"))
.SetProjection(Projections.Property("n.id"));
IList<Workflow> workflows = session.CreateCriteria<Workflow>("w")
.CreateCriteria("w.Log", "l")
.Add(Subqueries.PropertyIn("l.id", subquery))
.List<Workflow>();