Java/Hibernate using interfaces over the entities - java

I am using annoted Hibernate, and I'm wondering whether the following is possible.
I have to set up a series of interfaces representing the objects that can be persisted, and an interface for the main database class containing several operations for persisting these objects (... an API for the database).
Below that, I have to implement these interfaces, and persist them with Hibernate.
So I'll have, for example:
public interface Data {
public String getSomeString();
public void setSomeString(String someString);
}
#Entity
public class HbnData implements Data, Serializable {
#Column(name = "some_string")
private String someString;
public String getSomeString() {
return this.someString;
}
public void setSomeString(String someString) {
this.someString = someString;
}
}
Now, this works fine, sort of.
The trouble comes when I want nested entities.
The interface of what I'd want is easy enough:
public interface HasData {
public Data getSomeData();
public void setSomeData(Data someData);
}
But when I implement the class, I can follow the interface, as below, and get an error from Hibernate saying it doesn't know the class "Data".
#Entity
public class HbnHasData implements HasData, Serializable {
#OneToOne(cascade = CascadeType.ALL)
private Data someData;
public Data getSomeData() {
return this.someData;
}
public void setSomeData(Data someData) {
this.someData = someData;
}
}
The simple change would be to change the type from "Data" to "HbnData", but that would obviously break the interface implementation, and thus make the abstraction impossible.
Can anyone explain to me how to implement this in a way that it will work with Hibernate?

Maybe OneToOne.targetEntity?:
#OneToOne(targetEntity = HbnData.class, cascade = CascadeType.ALL)
private Data someData;

The interface that I usually use is Data Access Object, or DAO. Using Java generics, I can write it just once; Hibernate makes it possible to write the implementation just once, too:
package persistence;
import java.io.Serializable;
import java.util.List;
public interface GenericDao<T, K extends Serializable>
{
T find(K id);
List<T> find();
List<T> find(T example);
List<T> find(String queryName, String [] paramNames, Object [] bindValues);
K save(T instance);
void update(T instance);
void delete(T instance);
}

Related

Java generics both methods have same erasure error

In my project, I have multiple services performing three basic operations - create, edit and search. For this, I am trying to create a generic service. Below is what I have come up with so far.
Search method will take a list of objects at runtime.
public interface GenericService<T> {
void update(T t);
void create(T t);
T search(List<?> t);
}
Also, I have created an abstract class where the common code for all services will be placed.
public abstract class AbstractService<T> implements GenericService<T> {
}
Here is my implementation
public class AccountService extends AbstractService<Account> implements GenericService<Account> {
#Override
public void update(Account account) { }
#Override
public void create(Account account) { }
#Override
public Account search(List<SearchCriteria> t) { return null; }
}
Here are my Account and SearchCriteria classes
public class Account {
private String accountNumber;
private Date openingDate;
// more fields
// getter setter removed for brevity
}
Search criteria class
public class SearchCriteria {
private String key;
private String value;
// getter setter removed for brevity
}
Problem: on line public Account search(List t) { return null; }, getting compilation error saying
'search(List)' in
'com.test.AccountService' clashes with
'search(List)' in 'com.test.GenericService';
both methods have same erasure, yet neither overrides the other
In order for
public Account search(List<SearchCriteria> t) { ...}
to override
T search(List<?> t);
The arguments must be the same after type parameter substitution, but ? is not SearchCriteria.
Therefore, if you want to keep these methods (the inheritance looks a bit wild to me), you'll need to parameterise the types further.
public interface GenericService<T, C> {
// ...
T search(List<C> t); // probably change that parameter name
}
public abstract class AbstractService<T, C>
implements GenericService<T, C>
{
}
public class AccountService
extends AbstractService<Account, SearchCriteria>
implements GenericService<Account, SearchCriteria> // unnecessary
{
// ...
#Override
public Account search(List<SearchCriteria> t) { /* ... */ }
}
Changing List<?> to List<SearchCriteria> in GenericService will solve the error. There is no benefit in using a wildcard if the search method will always take a list of SearchCriteria objects in every service implementation.
If, however, you want to make this generic as well, you can introduce a second type parameter.

Generics in Hierarchical Model Issue

I have following hierarchy of classes:
public interface Table<V> {
Map<String, Object> getNamedParams(SearchQuery<V> query);
}
public class TopicTable implements Table<Topic> {
#Override
public Map<String, Object> getNamedParams(TopicSearchQuery query) {
// TODO to be implemented
return null;
}
}
#Data
public abstract class SearchQuery<E> {
private Integer pageSize;
private Integer pageNumber;
}
public class TopicSearchQuery extends SearchQuery<Topic> {
private String orgId;
private Boolean isShared;
private String name;
private String description;
}
My IDE is complaining that the method getNamedParams from interface Table is not properly implemented in class TopicTable. I know it has something to do with inheritance and my usage of generics.
What I am trying to accomplish: tie entity, search query and table together, so that table operates only with proper entities and search queries. I want this to be cohesive and robust. Any idea on how I can achieve it best, so that the design is not cluttered or cumbersome, ideally, with no explicit casting.
Thanks in advance.
This method signature:
getNamedParams(TopicSearchQuery query)
Does not override the method. It would have to be:
getNamedParams(SearchQuery<Topic> query)
You can't change the type of the parameter to be any more specific (because this would violate Liskov substitutability) or less specific (because Java doesn't allow it when overriding) than required by the interface.
If you want to be able to require a more specific parameter type, add another type variable:
public interface Table<V, SQ extends SearchQuery<V>> {
Map<String, Object> getNamedParams(SQ query);
}
Then you can declare your implementing class to use that type:
public class TopicTable implements Table<Topic, TopicSearchQuery> {
#Override
public Map<String, Object> getNamedParams(TopicSearchQuery query)

How to create a generic class that has a generic class as type?

In my project I have a Model abstract class, which defined a getId() method and a different classes that extends Model.
In order to be enable each class to define the type of the ID field as it needs, I made the Model class generic:
public abstract class Model<T> {
public abstract T getId();
}
So I can extend it now:
public class Person extends Model<String> {
#Override
public String getId() {
return getName();
}
}
That is ok.
Now I would like to create a class that act as a change set, where I can manage updated and deleted models. In this class, I would like to manage the updated Models using a map, being the model id the key and the model itself the value. (A changeset will have only one type, for instance ChangeSet will only contain person, ChangeSet will only contain cars and so on).
I would like to instantiate it like this:
ChangeSet<Person> changeSet = new ChangeSet<Person>();
It makes sense to me that it is possible to infer that the ID will be a String because Person extends Model.
But it doesn't seem to be possible. The nearest I could get were these two alternatives:
public class ChangeSet<M extends Model, T>{
private final Map<T, M> updated = new HashMap<>();
...
}
so I can store in a set the models that were updated. But Eclipse is compaining that I should specify the generic type of the Model, creating a signature like this:
public class ChangeSet<M extends Model<T>, T>{
private final Map<T, M> updated = new HashMap<>();
...
}
Eclipse is fine with it, but now I need this code to instantiate an object.
ChangeSet<String, Person> changeSet = new ChangeSet<Person<String>, String>();
It is not so elegant and having duplicated "String" is awkward.
Is there any way I can get (near) to my desired declaration?
A good combination of interface usage and generics would definitely result in a simple cleaner implementation that is also extensible.
Here is what I propose - I actually implemented this and it works fine :
Update based on comments from the OP
The Model interface can be "generified" to constrain the return types:
package org.example;
/**
* Created by prahaladd on 08/07/15.
*/
public interface Model<T extends Identifier>
{
T getIdentifier();
}
Implement the model class that uses a particular type of identifier :
package org.example;
/**
* Created by prahaladd on 08/07/15.
*/
public class Person implements Model<StringIdentifier>
{
private final String name;
private final String id;
public Person(String id, String name)
{
this.id = id;
this.name = name;
}
#Override
public StringIdentifier getIdentifier()
{
return new StringIdentifier(id);
}
public String getName()
{
return name;
}
}
ChangeSet implementation now changes a bit to mimic the Map interface as below. It in-fact now takes in the type of the identifiers that would be stored as keys:
package org.example;
import java.util.Map;
/**
* Created by prahaladd on 08/07/15.
*/
public class ChangeSet<T extends Identifier, M extends Model<T>>
{
//Refer to PECS - http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs
private Map<? super Identifier, M> changeMap;
public void addChange(M element)
{
changeMap.put(element.getIdentifier(),element);
}
public M getChangedElementForId(T id)
{
return changeMap.get(id);
}
}
All these changes are not so bad - you can instantiate the ChangeSet pretty much easily as below :
package org.example;
public class Main {
public static void main(String[] args)
{
Person p1 = new Person("1", "Tom");
Person p2 = new Person("2", "Jerry");
//change set is instantiated without any redundant generic parameters
ChangeSet<StringIdentifier, Person> changes = new ChangeSet<StringIdentifier,Person>();
//assume that there were some changes and you want to add them to the changeset.
changes.addChange(p1);
changes.addChange(p2);
//retrieve element from the changeset for an id
p1= changes.getChangedElementForId(new StringIdentifier("1"));
p2 = changes.getChangedElementForId(new StringIdentifier("2"));
}
}
Alternate Solution
Firstly - define an Interface that would encapsulate your ID. This is not an overkill; given that you have different types of IDs using an Interface to define the contract for an identifier would go a long way to make your code clean and extensible:
package org.example;
/**
* Created by prahaladd on 08/07/15.
*/
public interface Identifier<T>
{
T getIdentifier();
}
Now that you have defined an Identifier interface, you can define different implementations for it corresponding to your various ID types. For e.g. below I have provided an implementation for the StringIdentifier which generates IDs of type string:
package org.example;
/**
* Created by prahaladd on 08/07/15.
*/
public class StringIdentifier implements Identifier<String>
{
private final String identifier;
public StringIdentifier(String id)
{
identifier = id;
}
#Override
public String getIdentifier()
{
return "someId";
}
}
Now define the Model interface. Ideally, the Model interface should not deal with any of the ID types, it should just know that it has to return an Identifier (as is your use case).
package org.example;
/**
* Created by prahaladd on 08/07/15.
*/
public interface Model
{
Identifier getIdentifier();
}
Now provide an implementation of the Model interface. For e.g. below is the Person class that has been mentioned in your query:
package org.example;
/**
* Created by prahaladd on 08/07/15.
*/
public class Person implements Model
{
private final String name;
private final String id;
public Person(String id, String name)
{
this.id = id;
this.name = name;
}
#Override
public Identifier getIdentifier()
{
return new StringIdentifier(id);
}
public String getName()
{
return name;
}
}
Now define the ChangeSet. The ChangeSet should only know that it stores mapping between ID objects and the corresponding Model. It does not really know about the type of the ID objects. This makes the ChangeSet class extremely flexible to even support heterogenous collection in addition to the homogenous ones that you want.
package org.example;
import java.util.Map;
/**
* Created by prahaladd on 08/07/15.
*/
public class ChangeSet<M extends Model>
{
//Refer to PECS - http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs
private Map<? super Identifier, M> changeMap;
private Class identifierType;
public void addChange(M element)
{
//prahaladd - update : save the identifier type for a later check.
if(identifierType != null)
{
identifierType = element.getIdentifier.getClass();
}
changeMap.put(element.getIdentifier(),element);
}
public M getChangedElementForId(Identifier id)
{
//prahaladd updated - verify that the type of the passed in id
//is the same as that of the changeset identifier type.
if(!id.getClass().equals(identifierType))
{
throw new IllegalArgumentException();
}
return changeMap.get(id);
}
}
Now the hard work pays off. Take a look at the below client implementation:
package org.example;
public class Main {
public static void main(String[] args)
{
Person p1 = new Person("1", "Tom");
Person p2 = new Person("2", "Jerry");
ChangeSet<Person> changes = new ChangeSet<Person>();
//assume that there were some changes and you want to add them to the changeset.
changes.addChange(p1);
changes.addChange(p2);
//retrieve element from the changeset for an id
p1= changes.getChangedElementForId(new StringIdentifier("1"));
p2 = changes.getChangedElementForId(new StringIdentifier("2"));
}
}
Exactly as you envisioned!! As you can see, there is nothing fancy that has been done here. Plain Object oriented concepts and a thoughtful combination of interface and generics.
Hope this helps!!
If the updated map is an implementation detail of ChangeSet, you could simply write:
public class ChangeSet<M extends Model<?>> {
private final Map<Object,M> updated = new HashMap<>();
public void add(M model) {
updates.put(model.getId(), model);
}
}
but this is not a good solution if ChangeSet should have methods which rely on the id type T of the Model.

Is it possible to write a generic enum converter for JPA?

I wanted to write a Converter for JPA that stores any enum as UPPERCASE. Some enums we encounter do not follow yet the convention to use only Uppercase letters so until they are refactored I still store the future value.
What I got so far:
package student;
public enum StudentState {
Started,
Mentoring,
Repeating,
STUPID,
GENIUS;
}
I want "Started" to be stored as "STARTED" and so on.
package student;
import jpa.EnumUppercaseConverter;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
#Entity
#Table(name = "STUDENTS")
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long mId;
#Column(name = "LAST_NAME", length = 35)
private String mLastName;
#Column(name = "FIRST_NAME", nullable = false, length = 35)
private String mFirstName;
#Column(name = "BIRTH_DATE", nullable = false)
#Temporal(TemporalType.DATE)
private Date mBirthDate;
#Column(name = "STUDENT_STATE")
#Enumerated(EnumType.STRING)
#Convert(converter = EnumUppercaseConverter.class)
private StudentState studentState;
}
the converter currently looks like this:
package jpa;
import javax.persistence.AttributeConverter;
import java.util.EnumSet;
public class EnumUppercaseConverter<E extends Enum<E>> implements AttributeConverter<E, String> {
private Class<E> enumClass;
#Override
public String convertToDatabaseColumn(E e) {
return e.name().toUpperCase();
}
#Override
public E convertToEntityAttribute(String s) {
// which enum is it?
for (E en : EnumSet.allOf(enumClass)) {
if (en.name().equalsIgnoreCase(s)) {
return en;
}
}
return null;
}
}
what will not work is that I do not know what enumClass will be at runtime. And I could not figure out a way to pass this information to the converter in the #Converter annotation.
So is there a way to add parameters to the converter or cheat a bit? Or is there another way?
I'm using EclipseLink 2.4.2
Thanks!
Based on #scottb solution I made this, tested against hibernate 4.3: (no hibernate classes, should run on JPA just fine)
Interface enum must implement:
public interface PersistableEnum<T> {
public T getValue();
}
Base abstract converter:
#Converter
public abstract class AbstractEnumConverter<T extends Enum<T> & PersistableEnum<E>, E> implements AttributeConverter<T, E> {
private final Class<T> clazz;
public AbstractEnumConverter(Class<T> clazz) {
this.clazz = clazz;
}
#Override
public E convertToDatabaseColumn(T attribute) {
return attribute != null ? attribute.getValue() : null;
}
#Override
public T convertToEntityAttribute(E dbData) {
T[] enums = clazz.getEnumConstants();
for (T e : enums) {
if (e.getValue().equals(dbData)) {
return e;
}
}
throw new UnsupportedOperationException();
}
}
You must create a converter class for each enum, I find it easier to create static class inside the enum: (jpa/hibernate could just provide the interface for the enum, oh well...)
public enum IndOrientation implements PersistableEnum<String> {
LANDSCAPE("L"), PORTRAIT("P");
private final String value;
#Override
public String getValue() {
return value;
}
private IndOrientation(String value) {
this.value= value;
}
public static class Converter extends AbstractEnumConverter<IndOrientation, String> {
public Converter() {
super(IndOrientation.class);
}
}
}
And mapping example with annotation:
...
#Convert(converter = IndOrientation.Converter.class)
private IndOrientation indOrientation;
...
With some changes you can create a IntegerEnum interface and generify for that.
What you need to do is write a generic base class and then extend that for each enum type you want to persist. Then use the extended type in the #Converter annotation:
public abstract class GenericEnumUppercaseConverter<E extends Enum<E>> implements AttributeConverter<E, String> {
...
}
public FooConverter
extends GenericEnumUppercaseConverter<Foo>
implements AttributeConverter<Foo, String> // See Bug HHH-8854
{
public FooConverter() {
super(Foo.class);
}
}
where Foo is the enum you want to handle.
The alternative would be to define a custom annotation, patch the JPA provider to recognize this annotation. That way, you could examine the field type as you build the mapping information and feed the necessary enum type into a purely generic converter.
Related:
https://hibernate.atlassian.net/browse/HHH-8854
This answer has been modified to take advantage of default interface methods in Java 8.
The number of components of the facility (enumerated below) remains at four, but the amount of required boilerplate is much less. The erstwhile AbstractEnumConverter class has been replaced by an interface named JpaEnumConverter which now extends the JPA AttributeConverter interface. Moreover, each placeholder JPA #Converter class now only requires the implementation of a single abstract method that returns the Class<E> object for the enum (for even less boilerplate).
This solution is similar to others and also makes use of the JPA Converter facility introduced in JPA 2.1. As generic types in Java 8 are not reified, there does not appear to be an easy way to avoid writing a separate placeholder class for each Java enum that you want to be able to convert to/from a database format.
You can however reduce the process of writing an enum converter class to pure boilerplate. The components of this solution are:
Encodable interface; the contract for an enum class that grants access to a String token for each enum constant. This is written only once and is implemented by all enum classes that are to be persisted via JPA. This interface also contains a static factory method for getting back the enum constant for its matching token.
JpaEnumConverter interface; provides the common code for translating tokens to/from enum constants. This is also only written once and is implemented by all the placeholder #Converter classes in the project.
Each Java enum class in the project implements the Encodable interface.
Each JPA placeholder #Converter class implements the JpaEnumConverter interface.
The Encodable interface is simple and contains a static factory method, forToken(), for obtaining enum constants:
public interface Encodable{
String token();
public static <E extends Enum<E> & Encodable> E forToken(Class<E> cls, String tok) {
final String t = tok.trim();
return Stream.of(cls.getEnumConstants())
.filter(e -> e.token().equalsIgnoreCase(t))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("Unknown token '" +
tok + "' for enum " + cls.getName()));
}
}
The JpaEnumConverter interface is a generic interface that is also simple. It extends the JPA 2.1 AttributeConverter interface and implements its methods for translating back and forth between entity and database. These are then inherited by each of the JPA #Converter classes. The only abstract method that each placeholder class must implement, is the one that returns the Class<E> object for the enum.
public interface JpaEnumConverter<E extends Enum<E> & Encodable>
extends AttributeConverter<E, String> {
public abstract Class<E> getEnumClass();
#Override
public default String convertToDatabaseColumn(E attribute) {
return (attribute == null)
? null
: attribute.token();
}
#Override
public default E convertToEntityAttribute(String dbData) {
return (dbData == null)
? null
: Encodeable.forToken(getEnumClass(), dbData);
}
}
An example of a concrete enum class that could now be persisted to a database with the JPA 2.1 Converter facility is shown below (note that it implements Encodable, and that the token for each enum constant is defined as a private field):
public enum GenderCode implements Encodable{
MALE ("M"),
FEMALE ("F"),
OTHER ("O");
final String e_token;
GenderCode(String v) {
this.e_token = v;
}
#Override
public String token() { // the only abstract method of Encodable
return this.e_token;
}
}
The boilerplate for every placeholder JPA 2.1 #Converter class would now look like the code below. Note that every such converter will need to implement JpaEnumConverter and provide the implementation for getEnumClass() ... and that's all! The implementations for the JPA AttributeConverter interface methods are inherited.
#Converter
public class GenderCodeConverter
implements JpaEnumConverter<GenderCode> {
#Override
public Class<GenderCode> getEnumClass() { // sole abstract method
return GenderCode.class;
}
}
These placeholder #Converter classes can be readily nested as static member classes of their associated enum classes.
The above solutions are really fine. My small additions here.
I also added the following to enforce when implementing the interface writing a converter class. When you forget jpa starts using default mechanisms which are really fuzzy solutions (especially when mapping to some number value, which I always do).
The interface class looks like this:
public interface PersistedEnum<E extends Enum<E> & PersistedEnum<E>> {
int getCode();
Class<? extends PersistedEnumConverter<E>> getConverterClass();
}
With the PersistedEnumConverter similar to previous posts. However when the implementing this interface you have to deal with the getConverterClass implementation, which is, besides being an enforcement to provide the specific converter class, completely useless.
Here is an example implementation:
public enum Status implements PersistedEnum<Status> {
...
#javax.persistence.Converter(autoApply = true)
static class Converter extends PersistedEnumConverter<Status> {
public Converter() {
super(Status.class);
}
}
#Override
public Class<? extends PersistedEnumConverter<Status>> getConverterClass() {
return Converter.class;
}
...
}
And what I do in the database is always make a companion table per enum with a row per enum value
create table e_status
(
id int
constraint pk_status primary key,
label varchar(100)
);
insert into e_status
values (0, 'Status1');
insert into e_status
values (1, 'Status2');
insert into e_status
values (5, 'Status3');
and put a fk constraint from wherever the enum type is used. Like this the usage of correct enum values is always guaranteed. I especially put values 0, 1 and 5 here to show how flexible it is, and still solid.
create table using_table
(
...
status int not null
constraint using_table_status_fk references e_status,
...
);
I found a way to do this without using java.lang.Class, default methods or reflection. I did this by using a Function that is passed to the Convertor in the constructor from the enum, using method reference. Also, the Convertos from the enum should be private, no need for them outside.
Interface that Enums should implement in order to be persisted
public interface PersistableEnum<T> {
/** A mapping from an enum value to a type T (usually a String, Integer etc).*/
T getCode();
}
The abstract converter will use a Function in order to cover convertToEntityAttribute transformation
#Converter
public abstract class AbstractEnumConverter<E extends Enum<E> & PersistableEnum<T>, T> implements AttributeConverter<E, T> {
private Function<T, E> fromCodeToEnum;
protected AbstractEnumConverter(Function<T, E> fromCodeToEnum) {
this.fromCodeToEnum = fromCodeToEnum;
}
#Override
public T convertToDatabaseColumn(E persistableEnum) {
return persistableEnum == null ? null : persistableEnum.getCode();
}
#Override
public E convertToEntityAttribute(T code) {
return code == null ? null : fromCodeToEnum.apply(code);
}
}
The enum will implement the interface (I am using lombok for the getter) and create the converted by using a constructor
that receives a Function, I pass the ofCode using method reference. I prefer this instead of working with java.lang.Class or using reflection, I have more freedom in the enums.
#Getter
public enum CarType implements PersistableEnum<String> {
DACIA("dacia"),
FORD("ford"),
BMW("bmw");
public static CarType ofCode(String code) {
return Arrays.stream(values())
.filter(carType -> carType.code.equalsIgnoreCase(code))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Invalid car type code."));
}
private final String code;
CarType(String code) {
this.code = code;
}
#Converter(autoApply = true)
private static class CarTypeConverter extends AbstractEnumConverter<CarType, String> {
protected CarTypeConverter () {
super(CarType::ofCode);
}
}
}
4.In the entity you just have to use the enum type and it will save it's String code.
#Column(name = "CAR_TYPE")
private CarType workflowType;
If you don't mind reflection, this works. Credit to another SO answer inline.
abstract class EnumTypeConverter<EnumType,ValueType> implements AttributeConverter<EnumType, ValueType> {
private EnumType[] values
#Override
ValueType convertToDatabaseColumn(EnumType enumInstance) {
return enumInstance ? enumInstance.getProperty(getValueColumnName()) : null
}
#Override
EnumType convertToEntityAttribute(ValueType dbData) {
if(dbData == null){
return null
}
EnumType[] values = getValues()
EnumType rtn = values.find {
it.getProperty(getValueColumnName()).equals(dbData)
}
if(!rtn) {
throw new IllegalArgumentException("Unknown ${values.first().class.name} value: ${dbData}")
}
rtn
}
private EnumType[] getValues() {
if(values == null){
Class cls = getTypeParameterType(getClass(), EnumTypeConverter.class, 0)
Method m = cls.getMethod("values")
values = m.invoke(null) as EnumType[]
}
values
}
abstract String getValueColumnName()
// https://stackoverflow.com/a/59205754/3307720
private static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
return getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex])
}
private static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
Map<TypeVariable<?>, Type> subMap = new HashMap<>()
Class<?> superClass
while ((superClass = subClass.getSuperclass()) != null) {
Map<TypeVariable<?>, Type> superMap = new HashMap<>()
Type superGeneric = subClass.getGenericSuperclass()
if (superGeneric instanceof ParameterizedType) {
TypeVariable<?>[] typeParams = superClass.getTypeParameters()
Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments()
for (int i = 0; i < typeParams.length; i++) {
Type actualType = actualTypeArgs[i]
if (actualType instanceof TypeVariable) {
actualType = subMap.get(actualType)
}
if (typeVariable == typeParams[i]) return (Class<?>) actualType
superMap.put(typeParams[i], actualType)
}
}
subClass = superClass
subMap = superMap
}
return null
}
}
Then in the entity class:
enum Type {
ATYPE("A"), ANOTHER_TYPE("B")
final String name
private Type(String nm) {
name = nm
}
}
...
#Column
Type type
...
#Converter(autoApply = true)
static class TypeConverter extends EnumTypeConverter<Type,String> {
String getValueColumnName(){
"name"
}
}
This is written in groovy, so you'll need some adjustments for Java.

Abstracting named queries in an abstract JPA DAO

I have an abstract DAO class which uses parameterized types E (Entity) and K (Primary Key). In every entity I have a #NamedQuery. I want to dynamically invoke this named query without knowing its exact name and parameter name.
As an example, imagine the following entity City
#Entity(name="CITY")
#NamedQuery(
name="findCityByname",
query="FROM CITY c WHERE name = :CityName"
)
public class City {
// ...
}
and this CityDao
public class CityDao extends AbstractDao<City, Long> {
public CityDao() {
super(City.class);
}
}
How should I implement the findByName() method in AbstractDao so that I don't need to know the exact name and parameter name?
public abstract class AbstractDao<E, K> implements Dao<E, K> {
#PersistenceContext
protected EntityManager entityManager;
protected Class<E> entityClass;
protected AbstractDao(Class<E> entityClass) {
this.entityClass = entityClass;
}
#Override
public E findByName(String name) {
try {
return (E) entityManager
.createNamedQuery("findCityByName")
.setParameter("CityName", name)
.getSingleResult();
} catch(Exception e) {
return null;
}
}
// ...
}
The naming convention for named queries is usually <Entity Name>.findBy<PropertyAndAnotherProperty>, "City.findByName" in your example, so I would try to change the named queries to follow this pattern. The parameter to this query should then also have the same name, or you could use positional parameters. Your find method would then turn into
#Override
public E findByName(String name) {
E entity = null;
try {
return (E)entityManager.createNamedQuery(myClass.getSimpleName() + ".findByName")
.setParameter("name", name)
.getSingleResult();
} catch (Exception ex) {
return null;
}
}
The simplest method is to pass the name of the query to the constructor of the abstract DAO:
public DaoAbstreact(Class myClass, String findByNameQueryName) {
this.myClass = myClass;
this.findByNameQueryName = findByNameQueryName;
}
Then define a public static final String in City to hold the name:
public class ConcreteCityDao<City,Long> extends DaoAbstreact {
ConcreteCityDao(){
super(City.class, City.FIND_BY_NAME_QUERY_NAME));
}
}
Alternatively you could declare DaoAbstreact as abstract and then have a method like this in it:
public abstract String getFindByNameQueryName();
And implement that in ConcreteCityDao.
Finally you could also introduce an enumeration:
public enum NamedEntityType {
CITY(City.class, "findCityByname"),
PERSON(Person.class, "findPersonByname");
private final Class<?> entityClass;
private final String findByNameQueryName;
private NamedEntityType(Class<?> entityClass, String findByNameQueryName) {
this.entityClass = entityClass;
this.findByNameQueryName = findByNameQueryName;
}
public Class<?> getEntityClass() {
return entityClass;
}
public String getFindByNameQueryName() {
return findByNameQueryName;
}
}
Then your DAO can determine the type from the class passed in. To ensure you don't forget to add an entity to the enumeration you can make each entity implement an interface with a getNamedEntityType() method. Then you can specify that your abstract generic DAO will only accept entities that implement that interface.
The obvious way would be to pass values from concrete classes to the abstract superclass using abstract method
public abstract class AbstractDao<E, K extends Serializable> implements Dao <E, K> {
...
protected abstract String getFindByNameQueryName();
#Override
public E findByName(String EntityStr) {
... entityManager.createNamedQuery(getFindByNameQueryName()) ...
}
}
#Override
public class ConcreteCityDao<City,Long> extends DaoAbstreact{
...
protected String getFindByNameQueryName() {
return "findCityByName";
}
}
or as a constructor argument:
public abstract class AbstractDao<E, K extends Serializable> implements Dao<E, K> {
public AbstractDao(Class<E> myClass, String findByNameQueryName) { ... }
...
}
#Override
public class ConcreteCityDao<City, Long> extends DaoAbstreact{
public ConcreteCityDao() {
super(City.class, "findCityByName");
}
}
Though this requires consistent naming of query parameters for different entities.
Also note the minor improvements in these snippets.
What you basically seem to want is to annotate the annotations that define the named queries, in such a way that you can programmatically discover what the "findByName" query is (and possible other queries).
Since this is not possible in Java, you could use the fact that #NamedQuery supports query hints, that are defined as being vendor specific. Unknown hints are ignored. You could add your own data here, that the generic DAO can read back from entityClass:
#NamedQuery(
name="findCityByname",
query="FROM CITY c WHERE name = :CityName",
hints=#QueryHint(name="genericDAO.type", value="findByName")
)

Categories

Resources