Can Hibernate Criteria query for subclasses using Restrictions.in? - java

I have a number of classes mapped as an inheritance hierarchy into a single table.
I would like to use the following Criteria query but this results in a ClassCastException since Class cannot be cast to String.
Set<Class> childClasses = new HashSet<>();
childClasses.add(Child1.class);
childClasses.add(Child2.class);
session.createCriteria(Parent.class)
.add(Restrictions.in("class", childClasses)
.list();
I note that Hibernate does support specifying a single class using Restrictions.eq("class", childClass) so I can workaround by using a Disjunction'. I also know that this would work if my resriction was based on the discriminator strings for each subclass but I would prefer not to use these.
It is possible to use Criteria in this manner? The accepted answer to this question suggests that it works when the class is a property of the class you are basing your Criteria on but it doesn't seem to work in the case I've shown above.

Looking at the source code, I would say that this is a Hibernate bug.
For example, SimpleExpression (returned by Restrictions.eq) calls criteriaQuery.getTypedValue which handles conversion of Class value to the corresponding discriminator value.
InExpression (returned by Restrictions.in) adds values as they are, without conversion. That's why you get ClassCastException, because later an attempt to cast Class to String is made (obviously the type of your discriminator value is String).
You could avoid using this form until it's fixed (you already suggested the proper workarounds), or, if you would really like to stick to using Class objects directly, then you could implement custom InExpression in your project. For example, something like:
public class ClassInExpression extends InExpression {
private static final String CLASS = "class";
private final Collection<Class> values;
public ClassInExpression(Collection<Class> values) {
super(CLASS, values.toArray(new Object[values.size()]));
this.values = values;
}
#Override
public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) {
if (criteriaQuery.getTypeUsingProjection(criteria, CLASS).isComponentType()) {
return super.getTypedValues(criteria, criteriaQuery);
}
return convertToDiscriminatorValues(criteria, criteriaQuery);
}
private TypedValue[] convertToDiscriminatorValues(Criteria criteria, CriteriaQuery criteriaQuery) {
List<TypedValue> resultList = new ArrayList<TypedValue>();
for (Object value : values) {
resultList.add(criteriaQuery.getTypedValue(criteria, CLASS, value));
}
return resultList.toArray(new TypedValue[resultList.size()]);
}
}
Then you would use it instead of Restrictions.in:
session.createCriteria(Parent.class)
.add(new ClassInExpression(childClasses))
.list()

Related

Create a generic enumeration converter for JHipster

I have an entity with an element of type enum:
#Column(name = "COL_NAME")
#Convert(converter = EnumConverter.class)
private COLNAME colname;
I need a generic converter (I don't want to write one new converter for each enum I'll have in my entities)
import java.lang.reflect.*;
#Converter(autoApply = false)
public class EnumConverter implements AttributeConverter<Object, String>{
#Override
public String convertToDatabaseColumn(Object attribute) {
String valuetoconvert = attribute.toString();
//do something on valuetoconvert
return valueconverted;
}
#Override
public Object convertToEntityAttribute(String dbData) {
// Object to return with dbData read from DB and modified
return objectconverted
}
}
In convertToEntityAttribute I try with Enumeration.valueOf, but this method need the class of enum. How can I try to find this?... if this is the correct way to do so.
Thanks
P.S. I find, googling, some approach tending to minimize the code written, but in every case I must write one class for each enum. And I don't want this. Is it possible?
So, in essence, you're asking how to inject the type of the annotated property into the AttributeConverter. I'm afraid that's impossible with vanilla JPA.
If you're using Hibernate, you could use a composite user type instead. See here, specifically section 4.4. 'Type Parameterization'. You'd end up with something like:
#Type(type = "com.example.ConvertibleEnumType", parameters = #Parameter(name = "lookup", value = MyEnum.class))
private MyEnumClass property;
and you'd still have to rely on reflection inside your custom ConvertibleEnumType definition heavily, but it would work - you would be able to read the value of lookup inside setParameterValues.
(TBH personally, I'd still consider a separate converter per each enum, using e.g. the approach described here, to be the cleaner solution)

How to get MongoRepository to return correct type when there are multiple types in the collection [duplicate]

Having two types of entities, that are mapped to two Java classes in the single MongoDB collection:
#Document
public class Superclass { ... }
#Document(collection = "superclass")
public class Subclass extends Superclass { ... }
and two repositories for those entities:
public interface SuperclassRepository extends MongoRepository<Superclass, String> {}
public interface SubclassRepository extends MongoRepository<Subclass, String> {}
MongoRepositories don't handle the inheritance of the entities correctly. While querying for all Subclass objects (e.g. SubclassRepository.findAll()) the result set contains Superclass objects, that are instantiated (or at least had been tried to be instantiated) with null values for fields that are part of the Subclass, but are not part of the Superclass.
The expected result would be that SubclassRepository should return only Subclass objects, while SuperclassRepository should return Superclass and Subclass objects. It works this way in Spring Data JPA.
Has anyone encountered this bug and has any solution on how to fix it?
I encounter the same issue.
Take a look at the source code and at my surprise it is kind of not implemented. It adds the Collection name and the entity class but not inserted in the final query the _class property.
And after taking a look at it I realized that how Mongo would know that SubClass1 or Subclass2 derived from SuperClass.
So I just override the SimpleMongoRepository Class and create my own Factory that put that class instead of the default SimpleMongoRepository
Here what i added:
public MySimpleMongoRepository(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) {
Assert.notNull(mongoOperations);
Assert.notNull(metadata);
this.entityInformation = metadata;
this.mongoOperations = mongoOperations;
Reflections reflections = new Reflections("com.cre8techlabs.entity");
Set<String> subTypes = reflections.getSubTypesOf(entityInformation.getJavaType()).stream().map(Class::getName).collect(Collectors.toSet());
subTypes.add(entityInformation.getJavaType().getName());
this.baseClassQuery = Criteria.where("_class").in(subTypes.toArray());
}
And here an example of implementation of a find
public T findOne(ID id) {
Assert.notNull(id, "The given id must not be null!");
Query q = getIdQuery(id).addCriteria(baseClassQuery);
return mongoOperations.findOne(q, entityInformation.getJavaType(), entityInformation.getCollectionName());
}
It works for me, I am just afraid that it take a little longer

How do Generics and Fields assist DAO Pattern in Standalone Java applications

//Interface DAO
public abstract class BaseDAO<T extends BaseDTO> {
public void update(T t) throws DBException {
Field[] fieldsToInsert = t.getClass().getDeclaredFields();
//code to update database object academic or event
}
public Integer create(T t) throws DBException {
Field[] fieldsToInsert = t.getClass().getDeclaredFields();
//code to create academic or event in database
}
}
//Concrete DAOs
public class AcademicDAO extends BaseDAO<AcademicDTO> {
//provide implementation
}
public class EventDAO extends BaseDAO<EventDTO> {
//provide implementation
}
//Transfer object
public class AcademicDTO extends BaseDTO {
String title;
String surname;
//getters and setters
}
public class BaseDTO {
protected Integer ID;
public Integer getID() {
return ID;
}
public void setID(Integer ID) {
this.ID = ID;
}
}
Hello Guys, I have a sample code on me that follows the above structure to create a small java application to manage academics and events. It is leniently following this pattern
1- You experts are familiar with this pattern more than me. I would like to understand why generics are used in this case so DAOs can extend and implement a generic base class. It would be great if one can show how generics here may be advantageous using an example.
2 - I have also witnessed the use of java Fields. Is there a link between generics and Fields?
I would like to document DAO pattern in an academic report, but I am finding difficult to understand how Generics and Reflect Field play a part here. Do they support flexibility and loose coupling?
The code you've provided is reusable set of logic to load and persist entities. Many times, in an application of non-trivial size, you'll wind up persisting many different types of objects. In this example, you can define as many objects as necessary, but only define the logic to actually save and load once. By asking the DTO what Field objects are there, it can get at the data to help construct queries for loading and saving.
Generics allow you to use this pattern while maintaining type safety. AcademicDAO can only handle AcadmeicDTO. You can't use AcademicDAO to store EventDTO. Generics allow the instance of the class to rely on a more specific type when dealing with the Field objects. If you didn't have generics, the BaseDAO would take Object, and you wouldn't be able to access any methods except those that Object provides because the JVM wouldn't know what class is provided, so it has to limit it's knowledge to that of Object. Using getClass().getDeclaredFields() bypasses that limitation because getClass() returns the actual class of the Object parameter.
Field is just a way to use reflection to access the values of the properties in each DTO. If you had to access the fields directly, with getTitle(), you couldn't reuse a generic base class to do your persistence. What would happen when you needed to access EventDTO? You would have to provide logic for that. Field allows you to skip that logic.
Edit:
To explain what I mean by accessing getID, you could do the following within BaseDAO because T is known to be a BaseDTO with a getID() method defined:
public abstract class BaseDAO<T extends BaseDTO> {
public boolean update(T t) throws DBException {
Integer id = t.getID();
Field[] fields = t.getClass().getDeclaredFields();
// Assuming you have a db object to execute queries using bind variables:
boolean success = db.execute("UPDATE table SET ... WHERE id = ?", id.intValue());
return success;
}
}
If you had this instead (in a non-generic class):
public boolean update(Object o) throws DBException {
// This line doesn't work, since Object doesn't have a getID() method.
Integer id = t.getID();
Field[] fields = o.getClass().getDeclaredFields();
boolean success = db.execute("UPDATE table SET ... WHERE id = ?", id.intValue());
return success;
}
You'd have to look through those Field objects, or ask for the ID field and assume it existed.
For question 1. The use of generics allows the same implementations of update and create to be used regardless of the type of the DTO. Consider if you didn't use generics. Then the best you could do for the parameter type of update would be BaseDTO, but then you could call
academicDAO.update( eventDTO )
which doesn't make sense. With the code as you have it, this would be a type error. So the main advantage is: better type checking.
For question 2. The use of Fields allows a single implementation of update and create to work on DTO object of various concrete types.

How to map custom collection in JPA?

I have problems in mapping custom collection with JPA (Hiberante provider). For example when I am using object with attribute
List<Match> matches;
with
<one-to-many name="matches">
<cascade>
<cascade-all />
</cascade>
</one-to-many>
in my ORM file, it is allright; But if I replace "List matches;" by
private Matches matches;
,where "Matches" is defined like:
public class Matches extends ArrayList<Match> {
private static final long serialVersionUID = 1L;
}
It produces following error:
Caused by: org.hibernate.AnnotationException: Illegal attempt to map a non collection as a #OneToMany, #ManyToMany or #CollectionOfElements: by.sokol.labs.jpa.MatchBox.matches
Thanks for your attention!
You can, but you have to refer to it as one of the common collections - List or Set.
so:
private List matches = new Matches();
Why? Because Hibernate makes proxies to your collections to enable lazy loading, for example. So it creates PersistentList, PersistentSet and PersistentBag, which are List but aren't Matches. So, if you want to add additional methods to that collection - well, you can't.
Check this article for more details.
You have a solution, however. Don't use inheritance, use composition. You can, for example, add a method to your entity called getMatchesCollection() (in addition to the traditional getter), which looks like:
public Matches getMatchesCollection() {
return new Matches(matches);
}
And your Matches class would look like (using google-collections' ForwardingList):
public class Matches extends ForwardingList {
private List<Match> matches;
public Matches(List<Match> matches) { this.matches = matches; }
public List<Match> delegate() { return matches; }
// define your additional methods
}
If you can't use google collections, simply define the ForwardingList yourself - it's calling all the methods of the underlying List
If you don't need any additional methods to operate on the structure, then don't define a custom collection.
Hibernate requires persistent collection-valued fields to be declared as an interface type (because they will be replaced with Hibernate's implementation for lazy loading purposes). From the reference documentation:
6.1. Persistent collections
Hibernate requires that persistent collection-valued fields be declared as an interface type. For example:
public class Product {
private String serialNumber;
private Set parts = new HashSet();
public Set getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
public String getSerialNumber() { return serialNumber; }
void setSerialNumber(String sn) { serialNumber = sn; }
}
The actual interface might be
java.util.Set, java.util.Collection,
java.util.List, java.util.Map,
java.util.SortedSet,
java.util.SortedMap or anything you
like ("anything you like" means you
will have to write an implementation
of
org.hibernate.usertype.UserCollectionType.)
Notice how the instance variable was
initialized with an instance of
HashSet. This is the best way to
initialize collection valued
properties of newly instantiated
(non-persistent) instances. When you
make the instance persistent, by
calling persist() for example,
Hibernate will actually replace the
HashSet with an instance of
Hibernate's own implementation of Set.
So your second approach is not possible, at least not the way you declared it. But to be honest, I don't really see the point.

Java: Trouble with Generics & Collection type detection

I have a class called DataSet with various constructors, each specifying a different type of variable. It might look a bit like this:
public class DataSet
{
private HashSet Data;
public DataSet( DataObject obj )
{
Data = new <DataObject>HashSet();
Data.add( obj );
}
public DataSet( ObjectRelationship rel )
{
Data = new <ObjectRelationship>HashSet();
Data.add( rel );
}
// etc.
Note: I haven't yet gotten to test that code due to incomplete parts (which I'm building right now).
In a function that I'm currently building, getDataObjects(), I need to return all DataObject objects that this set represents. In the case of constructors that initiate the class's HashSet, Data with types other than DataObject (such as the above ObjectRelationship), there obviously won't be any DataObjects stored within. In this case, I need to be able to detect the type that the HashSet 'Data' was initiated with (like, to tell if it's 'ObjectRelationship' or not, I mean). How do I do this?
tl;dr: How do I tell the type that a Collection (in this case, a HashSet) was initiated with in my code (like with an 'if' or 'switch' statement or something)?
Sounds like you want to make the entire class generic- add a template parameter to the declaration for the class and define your HashSet and retrieval functions using that template parameter for the types.
I'm a .Net guy at the moment, though, so I couldn't give you the Java syntax, but using C# syntax it would look something like this:
public class DataSet<T>
{
private Set<T> Data;
public DataSet( T obj )
{
Data = new HashSet<T>();
Data.add( obj );
}
public Iterator<T> getDataObjects()
{
return Data.iterator;
}
}
You could fetch an object from the set and verify its type.
Or you could have multiple sets to contain different types.
Or you could have an instance variable of type Class to act as a discriminator as an instance variable.
Or you could create a proxy object for HashSet using the last technique.
You could use a map to the set
HashMap <Class<?>, HashSet<Object>> data;
HashSet temp = data.get(DataObject.class);
if(temp == null)
{
temp = new HashSet();
data.put(DataObject.class, temp);
}
temp.add(obj);
Then you will get the best of both worlds.
Sounds like your design needs to be re-thought.
Also, to be clear on Generics; you cannot access the type at runtime. The type parameter is only for compile-time checking and is completely gone (type erasure) at runtime.
What does this class offer that CachedRowSet does not?
Sorry, I don't consider this to be a very good abstraction. If I were a member of your team, I wouldn't use it.
Your syntax doesn't look correct to me, either. IntelliJ agrees with me: it won't compile.
This does:
import java.util.HashSet;
import java.util.Set;
import java.util.Arrays;
public class DataSet
{
private Set<DataObject> data;
public DataSet(DataObject obj)
{
this.data = new HashSet<DataObject>();
data.add(obj);
}
public DataSet(DataObject[] objs)
{
data = new HashSet<DataObject>();
data.addAll(Arrays.asList(objs));
}
// etc.
}
Still a poor abstraction. Rethink it.
You could add an property to your dataset class (an enumerated value, boolean or type) that specifies which type was used to initialize the hashset.
Set the property in the appropriate constructor. This allows you to bypass getting an element out of the collection to check its type.
pseudo-code:
public class DataSet
{
private HashSet Data;
private Type _iw = null;
public Type InitializedWith { return _iw; }
public DataSet(DataObject)
{
...
_iw = typeof(DataObject);
}
public DataSet(ObjectRelationship)
{
...
_iw = typeof(ObjectRelationship)
}
I'm going to follow duffymo's advice and just use better abstraction. I'm going to make multiple classes for each specific type I plan to use (each implementing a common interface) so that I can just bypass this dumb problem.
It'll add a minuscule bit of overhead during the process of creating each DataSet object of correct type, but I suppose that's just how it goes.
I don't know what DataObject gives you over and above an Object.
I think an object-oriented approach to your problem would use classes that reflected your domain of interest (e.g., Invoice, Customer, etc.). The persistence layer would hide the persistence details.
A common way to accomplish this is to use the Data Access Object, which might look like this in Java:
public interface GenericDao<T>
{
T find(Serializable id);
List<T> find();
void save(T obj);
void update(T obj);
void delete(T obj);
}
Now you're dealing with objects instead of things that smack of relational databases. All the CRUD details are hidden behind the DAO interface.

Categories