jpa join query on a subclass - sql

I have the following relationships in JPA (hibernate).
Object X has two subclasses, Y and Z.
Object A has a manyToOne relationship to object X. (Note, this is a one-sided relationship so object X cannot see object A).
Now, I want to get the max value of a column in object A, but only where the relationship is of a specific subtype, ie...Y.
So, that equates to...get the max value of column1 in object A, across all instances of A where they have a relationship with Y. Is this possible? I'm a bit lost as how to query it.
I was thinking of something like:
String query = "SELECT MAX(a.columnName) FROM A a join a.x;
Query query = super.entityManager.createQuery(query);
query.execute();
However that doesn't take account of the subclass of X...so I'm a bit lost.
Any help would be much appreciated.

JPA 2.0 allows queries to restrict the classes returned from a polymorphic query with the TYPE operator. From the JPA 2.0 specification:
4.6.17.4 Entity Type Expressions
An entity type expression can be used
to restrict query polymorphism. The
TYPE operator returns the exact type
of the argument.
The syntax of an entity type
expression is as follows:
entity_type_expression ::=
type_discriminator |
entity_type_literal |
input_parameter
type_discriminator ::=
TYPE(identification_variable |
single_valued_object_path_expression |
input_parameter )
An entity_type_literal is designated
by the entity name.
The Java class of the entity is used
as an input parameter to specify the
entity type.
Examples:
SELECT e
FROM Employee e
WHERE TYPE(e) IN (Exempt, Contractor)
SELECT e
FROM Employee e
WHERE TYPE(e) IN (:empType1, :empType2)
SELECT e
FROM Employee e
WHERE TYPE(e) IN :empTypes
SELECT TYPE(e)
FROM Employee e
WHERE TYPE(e) <> Exempt
JPA 1.0 doesn't offer such a mechanism so if you're using JPA 1.0, this won't be possible using standard JPQL.
If you don't mind using proprietary HQL, then you can use the special property class:
Likewise, the special property class accesses the discriminator value of an instance in the case of polymorphic persistence. A Java class name embedded in the where clause will be translated to its discriminator value.
from Cat cat where cat.class = DomesticCat
So in your case, I'd try something like that:
SELECT MAX(a.columnName) FROM A a where a.x.class = Y

I have not tested it, but try something like this:
SELECT MAX(a.columnName) FROM A a join a.x WHERE a.x.class = y.class
Hope it helps,
Anton

Related

How do I get tuples of primary keys of models related to each other using the django orm?

Assuming I have four models, where two models define types and two models are instances of these types, which have a 1-to-n relationship:
class SuperTypeA(Model):
pass
class InstanceofA(Model):
definition = ForeignKey(SuperTypeA, null=False)
class SubTypeB(Model):
supertype = ForeignKey(SuperTypeA, null=False)
class InstanceofB(Model):
definition = ForeignKey(SubTypeB, null=False)
related = ForeignKey(InstanceofA, null=False)
In plain English, a SuperTypeA has n SubTypeB, and n InstanceofA. A SubTypeB has n InstanceofB. A InstanceofA has n InstanceofB.
I would like to get the tuples of the primary keys of SuperTypeA, SubTypeB, InstanceofB given some constraints on InstanceofA. In SQL:
SELECT SuperTypeA.id, SubTypeB.id, InstanceofB.id
FROM SuperTypeA, SubTypeB, InstanceofB, InstanceofA
WHERE InstanceofA.id IN (1, 2, 3, 4)
How do I do that using the Django ORM, without using raw queries?
I think what you want to do is this: InstanceofB.objects.all().values_list('definition', 'related')
You can change the all method for the filter one if you want to set some kind of condition.

Subqueries for each row

Using these tables:
*student {'s_id','s_name,'...} , class {'c_id','c_name',...} student2class {'s_id','c_id'}, grades {'s_id','c_id','grade'}*
Is it possible to perform a query (nested query?) put class name as subtitle and then all students (of that class) and grades, next class name as subtitle ...
The result I need is:
Maths
John .... C
Anna .... B
[...]
Biology
Anna .... C
Jack .... A
[...]
For each row from class I'll have a subquery fetching all data related with this class
No need of any sub-query. You can get your data this way:
SELECT c_name, s_name_, grade
FROM student, class, grades
WHERE student.s_id = grades.s_id and class.c_id = grades.c_id
ORDER BY c_name
The presentation of results depends on your system/tools, as others already said. This is a link to the solution for Microsoft Access:
http://office.microsoft.com/en-001/access-help/create-a-grouped-or-summary-report-HA001159160.aspx
The solution should be implemented in your client side code and not in the database. From database you should just get a simple table formatted data (subject, student, grade)
Then convert the above recordset to the format you want:
For an example in C# you could convert the recordset into lookup
var Lookup = DataSet.Tables[0].Rows.ToLookup(x => x["subjectColumn"]);
now you can loop through the lookup and format your result
foreach (var grade in Lookup)
{
subject = grade.Key;
...
}

NHibernate many-to-many criteria creating multiple database calls

I have Events, which can have many EventTypes. I'm attempting to query the database by using the NHibernate mappings I have defined. The issue is that when defining criteria on child objects, NHibernate makes repeated calls to the database, rather than making one call for all the data it needs. This leads to an extremely slow search.
The database tables are as follows:
Event
-EventID
EventType
-EventTypeID
EventEventTypeID
-EventEventTypeID
-EventID
-EventTypeID
The Event class looks like this:
Id as Integer
Types as IList(Of EventType)
The EventType class looks like this:
Id as Integer
FullTitle as String
There is no EventEventType class.
The NHibernate mappings are as follows:
EventMapping
Table("event.Event")
Id(Function(x) x.Id).Column("EventID").GeneratedBy().Identity()
HasManyToMany(Of EventType)(Function(x) .Types).Table("event.EventEventType").ParentKeyColumn("EventID").ChildKeyColumn("EventTypeID").Cascade.All()
EventTypeMapping
Table("event.EventType")
Id(Function(x) x.Id).Column("EventTypeID").GeneratedBy().Identity()
Map(Function(x) x.FullTitle).Column("EventTypeFullTitle")
On the open of my form, the following function is called, which sets the FetchMode for the Types property of Event.
Public Function CreateListViewQuery(currentNHibernateSession As ISession) As NHibernate.ICriteria Implements IListViewQueryable.CreateListViewQuery
Return currentNHibernateSession.CreateCriteria(Of [Event])().SetFetchMode("Types", FetchMode.Join)
End Function
This is used to populate the listview, which happens extremely quickly, with just one call to the database for all the data:
SELECT TOP (50)
this_.EventID as EventID
, t3_.EventTypeID as EventTyp1_15_0_
, t3_.EventTypeFullTitle as EventTyp2_15_0_
FROM event.Event this_
inner join event.EventEventType types5_ on this_.EventID=types5_.EventID
inner join event.EventType t3_ on types5_.EventTypeID=t3_.EventTypeID
However, when I add a Criterion.Expression, like this (where QuickSearch is my user input):
.CreateAlias("Types", "t", NHibernate.SqlCommand.JoinType.InnerJoin)
.Add(NHibernate.Criterion.Expression.Like("t.FullTitle", QuickSearch))
I get 1 call which looks like this:
SELECT TOP 50
this_.EventID as EventID12_6_
, types8_.EventID as EventID
, t3_.EventTypeID as EventTyp2_
, t3_.EventTypeFullTitle as EventTyp2_15_0_
FROM
event.Event this_
inner join event.EventEventType types8_ on this_.EventID=types8_.EventID
inner join event.EventType t3_ on types8_.EventTypeID=t3_.EventTypeID
WHERE t3_.EventTypeFullTitle like #p1
And 50 more calls which look like this (50 as I have selected TOP 50):
SELECT
types0_.*
FROM
event.EventEventType types0_
left outer join event.EventType eventtype1_ on types0_.EventTypeID=eventtype1_.EventTypeID
WHERE types0_.EventID=#p0
(where #p0 is each of the TOP 50 Events which are returned by the search)
I feel like only the first call should be necessary for this.
Is it the nature of a many-to-many relationship which means NHibernate needs these additional calls? Is there something in my mapping which I've missed?
Perhaps importantly, I have used exactly the same technique for String properties of Event, and one-to-many relationships from Event and only one call is required for the search. The problem seems to only exist with many-to-many relationships.
Apologies for the long question, and thank you for getting this far. I've read many questions on a similar topic but couldn't find any addressing the issues of repeated database calls for many-to-many relationships.
What you are describing is an nHibernate n+1 issue (basically the number of queries executed is in direct proportion to the result set size), which can be difficult to resolve depending on how complex your query is.
Although not an obvious solution what has worked for me in the past is to change the join type to left outer join as below:
.CreateAlias("Types", "t", NHibernate.SqlCommand.JoinType.LeftOuterJoin)

Selecting a collection does not result in exception in JPA 2

I am having a bit of a trouble understanding this line of code from the book Pro JPA 2
According to the book on page 181.
The result type of a select query cannot be a collection; it must be a
single valued object such as an entity instance or persistent field
type. Expressions such as e.phones are illegal in the SELECT clause
because they would result in Collection instances (each occurrence of
e.phones is a collection, not an instance). Therefore, just as with
SQL and tables, if we want to navigate along a collection association
and return elements of that collection, we must join the two entities
together.
Please consider this entities below with relationship mapping
#Entity
public class Employee {
..
#OneToMany(mappedBy="employee", cascade=CascadeType.ALL, targetEntity=Phone.class)
private Collection<Phone> phones = new ArrayList<Phone>();
..
}
#Entity
public class Phone {
..
#OneToOne
private Employee employee;
..
}
Now in a test class i tried it with this test case
#Test
public void selectCollectionTest(){
TypedQuery<Object> query = em.createQuery("select e.phones from Employee e where e.id = 1", Object.class);
List<Object> empList = query.getResultList();
for(Object temp: empList){
System.out.println(temp);
}
}
I was expecting that an exception would be thrown but nothing is happening and I was able to select the collection?
Is this correct? Can somebody explain or clear out my understanding?
Eclipselink
EclipseLink allows this, it is an extension, the JPA spec does not support it.
It is the same as the query,
select p from Employee e join e.phones p where e.id = 1
Try to run your query with below code by removing the where clause:
select e.phones from Employee e, Object.class
The point i am trying to make is may be your result for emp id 1 contains only single phone object.

nHibernate collections and alias criteria

I have a simple test object model in which there are schools, and a school has a collection of students.
I would like to retrieve a school and all its students who are above a certain age.
I carry out the following query, which obtains a given school and the children which are above a certain age:
public School GetSchoolAndStudentsWithDOBAbove(int schoolid, DateTime dob)
{
var school = this.Session.CreateCriteria(typeof(School))
.CreateAlias("Students", "students")
.Add(Expression.And(Expression.Eq("SchoolId", schoolid), Expression.Gt("students.DOB", dob)))
.UniqueResult<School>();
return school;
}
This all works fine and I can see the query going to the database and returning the expected number of rows.
However, when I carry out either of the following, it gives me the total number of students in the given school (regardless of the preceding request) by running another query:
foreach (Student st in s.Students)
{
Console.WriteLine(st.FirstName);
}
Assert.AreEqual(s.Students.Count, 3);
Can anyone explain why?
You made your query on the School class and you restricted your results on it, not on the mapped related objects.
Now there are many ways to do this.
You can make a static filter as IanL said, however its not really flexible.
You can just iterate the collection like mxmissile but that is ugly and slow (especially considering lazy loading considerations)
I would provide 2 different solutions:
In the first you maintain the query you have and you fire a dynamic filter on the collection (maintaining a lazy-loaded collection) and doing a round-trip to the database:
var school = GetSchoolAndStudentsWithDOBAbove(5, dob);
IQuery qDob = nhSession.CreateFilter(school.Students, "where DOB > :dob").SetDateTime("dob", dob);
IList<Student> dobedSchoolStudents = qDob.List<Student>();
In the second solution just fetch both the school and the students in one shot:
object result = nhSession.CreateQuery(
"select ss, st from School ss, Student st
where ss.Id = st.School.Id and ss.Id = :schId and st.DOB > :dob")
.SetInt32("schId", 5).SetDateTime("dob", dob).List();
ss is a School object and st is a Student collection.
And this can definitely be done using the criteria query you use now (using Projections)
Unfortunately s.Students will not contain your "queried" results. You will have to create a separate query for Students to reach your goal.
foreach(var st in s.Students.Where(x => x.DOB > dob))
Console.WriteLine(st.FirstName);
Warning: That will still make second trip to the db depending on your mapping, and it will still retrieve all students.
I'm not sure but you could possibly use Projections to do all this in one query, but I am by no means an expert on that.
You do have the option of filtering data. If it there is a single instance of the query mxmissle option would be the better choice.
Nhibernate Filter Documentation
Filters do have there uses, but depending on the version you are using there can be issues where filtered collections are not cached correctly.