How to perform a non-polymorphic HQL query in Hibernate? - java

I'm using Hibernate 3.1.1, and in particular, I'm using HQL queries.
According to the documentation, Hibernate's queries are polymorphic:
A query like: from Cat as cat returns instances not only of Cat, but also of subclasses like DomesticCat.
How can I query for instances of Cat, but not of any of its subclasses?
I'd like to be able to do it without having to explicitly mention each subclass.
I'm aware of the following options, and don't find them satisfactory:
Manually filtering the instances after the query, OR:
Manually adding a WHERE clause on the discriminator column.
It would make sense for Hibernate to allow the user to decide whether a query should be polymorphic or not, but I can't find such an option.
Thanks in advance!

Use polymorphism="explicit" in the class mapping. This will cause queries to return only instances of the named class and not its subclasses.
Implicit polymorphism means that
instances of the class will be
returned by a query that names any
superclass or implemented interface or
class, and that instances of any
subclass of the class will be returned
by a query that names the class
itself. Explicit polymorphism means
that class instances will be returned
only by queries that explicitly name
that class.

SELECT cat FROM Cat cat WHERE cat.class='cat'
where the value 'cat' is the discriminator value of the Cat class.
If you are using TABLE_PER_CLASS, then try cat.class='Cat') (the name of the class)
This is not exactly a where clause on the discriminator column, because such a query will fail (the discriminator column is available only in native queries).

JPA 2 (Hibernate 3.5) adds support for non-polymorphic queries, this is very similar to Hibernates .class property (as Bozho answered above) but it is not Hibernate specific. This is done using the TYPE operator. As in
Select b from Book b where TYPE(b) = Book
You can read more about here it in my blog
Eyal

The ORM mimics the Java Model: if an object is an instance of another type (if an instance of PersianCat is an instance of Cat also), any query on Cat will have to be polymorphic (imagine you querying a List and asking if the entries match instanceof Cat.
Even Bozho's solution is somewhat impure, since the 'class' column is supposedly opaque to your hibernate mapping, although I admit its a very good compromise. You can simply get the discriminator through the classe's simple name.
If you're comfy and are using table per class you can always do a native query to the Cat table to get the ids and then get the entries through hibernate.

Look at BaseQueryReturnFieldsCalculatorGC; it dynamically adds a condition to the 'where' which selects only where class=XXX; you can duplicate this logic to the HQLQueryTemplate and have the user define 'isNonPolymorphic'.
Note that it will only work with table-per-hierarchy, cos only then does the implicit class column exist and is selectable.

Related

Is it good practice to pass collection name in Morphia createQuery method?

I usually use this approach while instantiating the query object:
Query<Product> query = datastore.createQuery(Product.class);
But Morphia allows you to pass the collection name also while instantiating the query object, which would look like this:
Query<Product> query = datastore.createQuery(COLLECTION_NAME, Product.class);
I am unable to understand why do we have to pass the Collection name explicitly while creating the query object?
When we create the model, in this case in the Product class we are already binding the collection name under the Entity annotation:
#Entity(value = "product", noClassnameStored = true)
class Product {
// model attributes declared here
}
One reason I can think of is, if the same model is used across multiple collection then we might need to pass the collection name, but even if it is so is this a good practise and does it abide the ORM guidelines? Please help me understand.
That method is used for cases where one entity is stored in different collections depending on the application usage. It's "hidden" on the AdvancedDatastore interface so that all you should see on a Datastore reference is the one createQuery() that takes the type. Most users won't need the overridden form but it was added years ago as a convenience for those who needed to map to multiple locations.
It's a mild misfeature as it doesn't quite work well with #Reference fields. I'm not sure how long that feature will continue to exist as it complicates some of the implementation for very little benefit.

I can't downcast in JPQL/Hibernate - Unable to find domain.Scoreable with id 16

I have an abstract class called Scoreable and it's extended by two classes named Idea and Solution. That is, both subclasses may have a set of scores. Every Score has a field scoreable that is a FK.
What I want to get is a collection of Scores given, for example, an Idea. So I've tried the following query:
select s from Score s join s.scoreable idea where idea.id = 16;
But it returns Unable to find domain.Scoreable with id 16, maybe because the Scoreable table doesn't exist in the database. Also I tried a downcast with this query select s from Score s join TREAT(s.scoreable AS Idea) idea where idea.id = 16;. In this case, I get the same response.
Your problem may be because you mapped the Scorable with #MapedSuperclass and now you want to include the base type in a query. For this you need the base class to be an actual #Entity instead.
According to the JPA documentation:
A mapped superclass is a non-entity class that can define persistent state and mapping information for entity subclasses. Mapped
superclasses are usually abstract. Unlike true entities, you cannot
query a mapped superclass, pass a mapped superclass instance to any
EntityManager or Query methods, or declare a persistent relation with
a mapped superclass target. You denote a mapped superclass with the
MappedSuperclass marker annotation.
So you can have Scorable as both the base class and an #Entity, while its subclases idea dn Solution are #Entities too.For more about this inheritance model, check this JPA example from Oracle.
If you only need Idea's scores you can issue the following query:
select distinct s
from Idea i
join i.scores s
where i.id = ?

Querying Mapped Superclasses or Equivalent

I have three classes that are subclasses of the abstract superclass Automobile. I'm using the single table inheritance model and a #MappedSuperclass for the automobile class. The subclasses are Car, Truck, and Van.
I want to query the automobile class, but have the different subclasses returned. I've written a couple queries and done some research, but it seems like it is not possible to query against MappedSuperclasses. I have also tried to do NativeSqlQueries, but I can't seem to be able to figure out how to specifiy multiple ResultSetMappings.
Is there anyway to accomplish this?
You cannot use a mapped-superclass in the query. If you want to use Automobile in the query, don't mark it as mapped-superclass, instead mark it as an entity.
Following is from the JPA 2.0 Spec, second paragraph is what's relevant in your case -
2.11.2 Mapped Superclasses
An entity may inherit from a superclass that provides persistent entity state and mapping information, but which is not itself an entity. Typically, the purpose of such a mapped superclass is to define state and mapping information that is common to multiple entity classes.
A mapped superclass, unlike an entity, is not queryable and must not be passed as an argument to EntityManager or Query operations. Persistent relationships defined by a mapped superclass must
be unidirectional.

Is it possible to fetch all child entities from a parent abstract entity?

I'm trying to figure out if I can make a method that fetches or finds all the entries in a central database I have.
My entities are as follows:
Nomination(Abstract) -> Type1Nomination, Type2Nomination, Type3Nomination, so on.
Nomination has a table is mapped to the DB also, and has the common properties/columns of every type of nomination. The rest is self-explanatory.
The way I see it, I would have to cast the types of nominations to the base class? And this would be done in a Service class (because you cannot make a DAO of an abstract class), am I correct?
You can do this in JPA/Hibernate and JPA inheritance. It should be as simple as querying the Nomination entity.
Select n From Nomination n
should return a list of abstract nomination objects.
I've seen folks using a type column in conjunction with the in keyword to get certain subgroups in your case something like
Select n from nomination where n.type in :types
where types was a collection of "Type1Nomination" "Type2Nomination" but because these are sibling queries it can lead to some nasty LEFT OUTER JOIN queries

JPA Native Query for Entity with Inheritance

I have an entity class and a subclass based on that entity:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class A
and
#Entity
public class B extends A
I need to issue a native query that uses a stored procedure on the base class (A) only. If I attempt it as follows:
entityManager.createNativeQuery("select * from A a where procedure(f)",A.class).getResultList()
I get an error regarding "The column clazz_ was not found in the ResultSet". I assume that the JPA provider adds this column in order to discriminate between the base class and the extended class. I can work around this problem by explicitly adding the clazz column and all of the fields from the subclass:
entityManager.createNativeQuery("select *,1 as clazz_,null as prop1,null as prop2 from A a where procedure(f)",A.class).getResultList()
where "prop1" and "prop2" are properties of the subclass B. However, this seems like an unnecessary hack and is prone to maintenance problems if the subclass B changes.
My question is: How can I query using a stored procedure on an entity that has inheritance defined on it?
As you've probably seen, the Hibernate team hasn't put a lot of work into defining how you do this.. the documentation simply states:
16.1.6. Handling inheritance
Native SQL queries which query for
entities that are mapped as part of an
inheritance must include all
properties for the baseclass and all
its subclasses.
So if you want to use Native queries it looks like you're stuck doing something like this. Regarding the concern about the subclass B changing, perhaps a slightly less onerous way of implementing this would be to try using LEFT OUTER JOIN syntax on the shared ID property:
entityManager.createNativeQuery("select a.*, b*, 1 as clazz_, from A a LEFT OUTER JOIN B b on id = a.id where procedure(f)",A.class).getResultList()
That way you'll always get all of the properties from B if you add or remove some.
You need to distinguish instances of A from instances of B, using a discriminator.
Use one of the following annotations:
#DiscriminatorValue
http://docs.oracle.com/javaee/5/api/javax/persistence/DiscriminatorValue.html
#DiscriminatorColumn
https://docs.oracle.com/javaee/5/api/javax/persistence/DiscriminatorColumn.html
#DiscriminatorFormula
http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/entity.html#d0e1168

Categories

Resources