I need to write java generic method that gets no parameter and returns a List.
This generic method is used hibernate:
public <T> List list() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
List result = session.createQuery("from " + T.class.getName()).list();
return result;
}
I am tring to invoke this method. I tried the following but it creates compilation errors:
mgr.list<User>();
mgr.list()<User>;
mgr.list(<User>);
How can I call this method?
You forgot this one:
mgr.<User>list()
I believe what you're trying to accomplish requires some refactoring. It looks like you're trying to create a generic Dao class that you can reuse to query any Model object. The problem is that without passing the class to the method, you can't get the type of the <T> generic at runtime.
One way to accomplish what you want is to create a base dao, which is extended by specific implementations that know the class they're dealing with at compile time.
public abstract class AbstractDao<T>{
private Class<T> clazz;
public AbstractDao(Class<T> clazz){
this.clazz = clazz;
}
public List<T> list() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
List result = session.createQuery("from " + clazz.getName()).list();
return (List<T>) result;
}
}
Then extend the class:
class UserDao extends AbstractDao<User>{
public UserDao(){
super(User.class);
}
}
Then call the method:
List<User> users = userDao.list();
It may have something to do with "type erasure", because of using generics. When you call
T.class.getName()
that T type is erased before run time. It is not available at runtime, it is only used at compile time to make sure type safety. You probably need to write some code to get thy type of the persitence class at runtime, and then use that in mgr class. An exmaple is
public abstract class GenericHibernateDAO<T, ID extends Serializable>
implements GenericDAO<T, ID> {
private Class<T> persistentClass;
private Session session;
public GenericHibernateDAO() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
// more methods
}
from https://community.jboss.org/wiki/GenericDataAccessObjects?_sscc=t
Here, it has persistentClass which is set to parameter type at construction time, then that persistentClass is in the class, whenever needed.
Related
I need help with Java Generics.
My model is: I have some classes that extends a Dto (Data Transfer Object) and some classes that extends Entity (The model of my object to DB).
I have
interface Mapper<D extends Dto, E extends Entity>{
//Convert a Entity to Dto.
D toDto(E entity);
And I have some classes that implements this interface (i.e PersonMapper, BookMapper and so far and so on).
#Component
public class PersonMapper implements Mapper<PersonDto, PersonEntity> {
//implementation
}
#Component
public class BookMapper implements Mapper<BookDto, BookEntity> {
//implementation
}
What I want to do is to use Factory Pattern in order to select at runtime my Mapper, that depends from a String that I pass in input.
#Autowired
private PersonMapper personMapper;
#Autowired
private BookMapper bookMapper;
public <D extends Dto, E extends Entity> Mapper<D, E> selectMapper(String entity){
if ("Person".equalsIgnoreCase(entity))
return personMapper;
if("Book".equalsIgnoreCase(entity))
return bookMapper;
...
}
With this situation I have the following compile error:
Type mismatch: cannot convert from PersonMapper to Mapper<D,E>
My solutions:
1)
return (Mapper<D, E>) personMapper;
but I have a Warning:
Type Safety: `Unchecked class from personMapper to Mapper<D,H>`
2)
Using WildCard and castingb
public Mapper<Dto, Entity> selectMapper(String entity){
Mapper<? extends Dto, ? extends Entity> toReturn = null;
if ("Person".equalsIgnoreCase(entity))
toReturn = personMapper;
else if("Book".equalsIgnoreCase(entity))
toReturn = bookMapper;
...
return (Mapper<Dto, Entity>) toReturn;
}
But in this case but I have another time a Warning:
Type safety: Unchecked cast from Mapper<capture#29-of ? extends Dto,capture#30-of ? extends Entity> to Mapper<Dto,Entity>
It works but it doesn't seems to be a clean solution
3) Using wildcard as return type:
public Mapper<? extends Dto, ? extends HistoryEntity> selectMapper(String entity)
but you know, using wildcard as return type is not recommended at all and also doesn't help me because I would like to use this mapper and call mapper.toDto ensuring that the return type is an something that extends Dto.
====================================================================
I don't explain why If I write a class constructor like that
public Service<D extends Dto, E extends Entity>{
public Service(Mapper<D,E> mapper){
this.mapper = mapper;
}
}
and than I inject (for example) bookMapper it works.
If, instead, the Mapper<D,E> is in return type I cannot do such a kind of operation.
====================================================================
The help that I ask to you is:
how can I write a solution using clean code principles (avoiding compile warnings, sonarlint issue etc.) in order to implement this kind of logic?
Thank you very much, I appreciate a lot if you dedicate a little bit of your time helping me to solve my problem.
Those vars (D and E) about the caller and not about your code. The D and E are decided by the caller, so there is absolutely no way to guarantee that PersonDTO fits.
Make that Mapper<? extends DTO, ? extends Entity> (and no variables), and given that those are already the lower bounds, just Mapper<?, ?> - that'll work, you can write your return statements without any casts and without compiler errors or warnings.
Of course, it means the caller has a mostly useless type.
Generics are entirely 'compile time / write time' based. The JVM (java.exe) has no idea what generics are, and in fact most of them don't survive the compilation process. The one and only purpose of generics is to make the compiler flag incorrect code and avoid some casting, that is all.
The nature of turning that string into a Mapper is entirely runtime.
Ergo, if Mapper<?, ?> isn't sufficient, what you want isn't possible. You'd need to write compile/write-time checkable stuff, so the moment you use a String, it's impossible. For example, a method getPersonMapper() can of course return a Mapper<PersonDTO, PersonEntity>, no problem.
More generally (heh) it sounds like you're badly reinventing various wheels here. Look at tutorials of JDBI, JOOQ, and Hibernate to get some ideas about how java code is commonly written to interact with databases.
Factory Pattern is pattern that assemble or create something by factory methods, in you case what you need is just to get corresponding mapper by name, so there is a simple way to do that since the mapper beans are autowired, adding String getName() to Mapper interface then implements it for earch implementation, e.g. in BookMapper
#Override
public String getName() { return "Book"; }
use mapper name as key and mapper bean as value to store mapper beans in a map, then you can retrieve it by its name:
#Service
public class SimpleService {
private BookMapper bookMapper;
private PersonMapper personMapper;
private Map<String, Mapper<? extends DTO, ? extends Entity>> mappers = new HashMap<>();
public SimpleService(BookMapper bookMapper, PersonMapper personMapper) {
this.bookMapper = bookMapper;
this.personMapper = personMapper;
mappers.put(bookMapper.getName(), bookMapper);
mappers.put(personMapper.getName(), personMapper);
}
public Mapper<? extends DTO, ? extends Entity> getMapperByName(String mapperName) {
return mappers.get(mapperName);
}
}
and you can cast it to corresponding mapper without warning.
PersonMapper p = (PersonMapper) simpleService.getMapperByName("Person");
or you can put different mapper in their service and use the service to handle you biz likes codes below, after all, you need specified mappers to do specified operations:
if(personThings){
personService.doSomeThing();
}
if(bookThings){
bookService.doSomething();
}
Can I use generics and JPA together?
I am trying to persist objects of four classes to my db. Here's my PersistService class:
public class PersistService<T> {
private static EntityManagerFactory emf = Persistence.createEntityManagerFactory("fileUploadProject");
public static EntityManager getEntityManager() {
return emf.createEntityManager();
}
// Write Client to Database
public static <T> void persist(T obj) {
EntityManager em = getEntityManager();
EntityTransaction et = em.getTransaction();
et.begin();
em.persist(obj);
et.commit();
em.close();
}
}
But then I get into a problem with removing the object. I have the following method in the PersistService class in addition to the above:
// Remove an object from the Database if they exist
public static <T> void remove(Long id) {
EntityManager em = getEntityManager();
EntityTransaction et = em.getTransaction();
<T> obj = em.find(<T>.class, id);
}
The final line is giving me a compile time error. I've tried <T>.class T Class<T> and T.class as well, but it still gives me a compile time error. Just learning about Type Erasure, is this error because of that? How do I resolve this issue?
You have started using a good pattern. The next step is to create a subclass of PersistService for each of your entity types. I will also mention that in the long run you probably want to have a common base class or interface for each of your entities. For example, I will call it Entity. This base class (if it is a class rather than interface) can be abstract and can define common methods for all of your entities.
public interface Entity {
long getId();
}
You can use the methods defined by Entity in your implementation of PersistService (which you may find handy as you add more generic entity-related business logic in this base service or elsewhere in your code).
Your entity A looks like
public class A extends Entity {
}
Your PersistService becomes
public abstract class PersistService<T extends Entity> {
// Your common methods (persist, remove, etc.).
public abstract Class<T> getEntityClass();
}
Your entity-specific services look like this
public class APersistService extends PersistService<A> {
public Class<A> getEntityClass() {
return A.class;
}
}
You then use the getEntityClass() method when you implement PersistService.remove().
While the entity-specific subclasses solve the problem of getting the specific class object in the face of type erasure, you will find that you end up wanting the subclass to support entity-specific queries as well.
I may have the answer you are searching for, well, to have generic type during compile time isn't something that easy. Since java don't allow you to do that directly.
I have a hack myself, can you try something like this ?
Be sure to handle your exceptions.
static <T> Class getGenericType(T t){
return getType(t);
}
static Class<?> getType(Object o){
return o.getClass();
}
I have looked for an answer on Stack for a while. All the answers look like they say I already have the right answer, but I still keep getting a class cast exception for the first line in the constructor below.
SEVERE: Exception while loading the app : EJB Container initialization error
java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
at com.domain.security.logging.ElsAbstractCrudClass.<init>(ElsAbstractCrudClass.java:54)
Here's the code. After looking at the documentation I still can't figure it out. I'm relatively new to generics and reflection so need some help. TIA.
public abstract class ElsAbstractCrudClass<T> {
Class<T> entity;
public ElsAbstractCrudClass() {
[line 54] ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
Type type = genericSuperclass.getActualTypeArguments()[0];
this.entity = (Class<T>) type;
}
}
Here is a subclass of the abstract crud class (SessionLog is a JPA entity):
#Stateless
public class SessionLogger extends ElsAbstractCrudClass<SessionLog> {
#PersistenceContext(unitName = "ELS_Soulard_PU")
private EntityManager em;
#EJB
DozerInstantiator di;
//SessionLog entity;
//SessionLog sessionLog = new SessionLog();
static final Logger logger = Logger.getLogger(SessionLogger.class.getSimpleName());
public SessionLogger() {
}
...
getGenericSuperclass returns an instance of ParameterizedType if the super class is generic, and an instance of Class if it is not. Presumably you have something like:
class A extends B { ... }
class B extends ElsAbstractCrudClass<Person> { ... }
Now, getClass() return A.class with superclass B.class, which is not generic ...
You could generalize your code snippet to work as long as the runtime class is not generic (recursively walking the type hierarchy, replacing type parameters by their definitions as you go). However, unless you have dozens of crud classes, requiring the subclass to pass the proper class object is easier:
public abstract class ElsAbstractCrudClass<T> {
final Class<T> entityClass;
public ElsAbstractCrudClass(Class<T> entityClass) {
this.entityClass = entityClass;
}
}
You got this error because EJB Container extend your Stateless bean to wrap EJB specific logic on method invocation. So at deployment time you have somthing like this:
ContainerSubclass extends SessionLogger {}
Solutions:
1) In your constructor first call
... = getClass().getSuperClass();
...
2) or code against interfaces so EJB container would create Dynamic Proxy through reflection.
Another way to run into trouble is if your extending class uses the raw type, not the generic type. In other words, this subclass will generate your exception, because its supertype is simply the raw type ElsAbstractCrudClass.
public class EE extends ElsAbstractCrudClass { ... }
But this one will not because its supertype is the generic type ElsAbstractCrudClass
public class EE extends ElsAbstractCrudClass<String> { ... }
Using the generic dao pattern, I define the generic interface:
public interface GenericDao<T extends DataObject, ID extends Serializable> {
T save(T t);
void delete(ID id);
T findById(ID id);
Class<T> getPersistentClass();
}
I then implemented an default GenericDaoImpl implementation to perform these functions with the following constructor:
public GenericDaoImpl(Class<T> clazz) {
this.persistentClass = clazz;
DaoRegistry.getInstance().register(clazz, this);
}
The point of the DaoRegistry is to look up a Dao by the class associating to it. This allows me to extend GenericDaoImpl and overwrite methods for objects that requires special handling:
DaoRegistry.getInstance().getDao(someClass.getClass()).save(someClass);
While it works, there are a few things that I don't like about it:
DaoRegistry is an singleton
The logic of calling save is complicated
Is there a better way to do this?
Edit
I am not looking to debate whether Singleton is an anti-pattern or not.
First of all, what is your problem with DaoRegistry being singleton?
Anyway, you could have an abstract base class for your entities that'd implement save like this
public T save(){
DaoRegistry.getInstance().getDao(this.getClass()).save(this);
}
then you could simply call someEntity.save()
Or it may be more straightforward if the entity classes itself implemented the whole GenericDao interface (save, delete and find methods), so the contents of your GenericDaoImpl would be in the base class of your entities.
It could be better to use instance of DaoRegistry instead of static methods. It would make it more manageable for test configurations. You could implement it as
#Component("daoRegistry")
public class DaoRegistry {
#Autowired
private List<GenericDao> customDaos;
private GenericDao defaultDao = new GenericDaoImpl();
public <T> T getDao(Class<T> clazz) {
// search customDaos for matching clazz, return default dao otherwise
}
}
Also you could add save method to it and rename accordingly. All customised daos should be available as beans.
I have class:
public class GenericDAO<T, ID extends Serializable> {
private final EntityManager em;
private final Class<T> entityClass;
public GenericDAO(EntityManager em) {
this.em = em;
ParameterizedType genericSuperClass = (ParameterizedType) getClass().getGenericSuperclass();
this.entityClass = (Class<T>) genericSuperClass.getActualTypeArguments()[0];
}
}
If I extend this class all works fine. Now I want to use this class directly (see code belove, CRUDBean is implementation of CRUDService) - it is necessary to rewrite constructor to get particular class.
#Remote(CRUDService.class)
#Stateless
public class CRUDBean<T extends EntityBase> implements CRUDService<T> {
#PersistenceContext
private EntityManager entityManager;
#Override
public long size(String whereClause, Map<String, Object> whereParameters) {
return new GenericDAO<T, Long>(entityManager).size(whereClause, whereParameters);
}
}
How to write such generics service?
Yes, you would need to create a separate constructor.
Your current constructor assumes that this is an instance of a subclass of GenericDAO, and it uses that fact to get the type parameter for you through getClass().getGenericSuperclass().getActualTypeArguments().
To use GenericDAO directly, you should create a GenericDAO constructor which takes the entity class (whatever type T really is) as an argument. Then provide the entity class in CRUDBean.size() or wherever you need to instantiate your GenericDAO.
If you don't have the actual class available in CRUDBean, have three choices:
Create a CRUDBean constructor which takes the entity class as an argument.
Add a parameter to size() which takes the entity class as an argument.
Use the same trick as in the current GenericDAO constructor to get it, but with getGenericInterfaces() instead.
I would suggest using an explicit "type token", rather than reusing a class.
The usual, and easiest, way around this to pass an instance of the class to the constructor:
public GenericDAO(EntityManager em, Class<T> entityClass) {
this.em = em;
this.entityClass = entityClass;
}
Then call it like this:
public long size(String whereClause, Map<String, Object> whereParameters, Class<T> entityClass) {
return new GenericDAO<T, Long>(entityManager, entityClass).size(whereClause, whereParameters);
}