Hibernate using DetachedCriteria with interfaces - java

Using Hibernate 3.6.8.Final and Spring 3.0.5.RELEASE , I'm trying to add some Common DAO functionality for classes that have multiple implementations overridden higher up to implement the specific classes however it doesn't work for DetachedCriteria.
Example:
In base class:
public interface ICat {
public void setMeowSound(String meow);
public String getMeowSound();
}
Then each inherited project would define the hibernate annotations.
e.g.
#Entity
#Table(name="SQUAWKY_CATS")
public class SquawkyMeowingCat implements ICat, Serializable {
#Id
#Column(name="SQUAWK_NAME")
private String meow;
public String getMeowSound() {
return meow;
}
public void setMeowString(String meow) {
this.meow = meow;
}
}
This means I can use:
Criteria criteria = Session.createCriteria(ICat.class);
And Spring/Hibernate knows that it pulls the annotations for ICat from the concrete inheritance in the particular project.
However if I try to do:
DetachedCriteria subQuery = DetachedCriteria.forClass(ICat.class,"inner"); // etcetera
then I get an Unknown entity at runtime for ICat.
Now this makes sense as in the first instance is creating it off the Session so it has all the configuration that it needs whereas the DetachedCriteria is a static method however it errors when trying to do the
criteria.list()
by which time it has picked up the Session and should know that ICat is actually a SquawkyMeowingCat which has all the annotations.
So my questions are two part:
1) Is this known behaviour and will be like this forever more?
2) Can anyone think of a simple way around it without using an Interface and concrete ClassHolder which hands back the instance of the class it needs to create?

I'm not sure about the case of the DetachedCriteria, but one way to avoid explicit dependence on the concrete class might be to query Hibernate's metadata using the interface:
public <T> Class<? extends T> findEntityClassForEntityInterface(
SessionFactory sessionFactory,
Class<T> entityInterface
) {
for (ClassMetadata metadata : sessionFactory.getAllClassMetadata().values()) {
Class entityClass = metadata.getMappedClass(EntityMode.POJO);
if (entityInterface.isAssignableFrom(entityClass)) {
return entityClass;
}
}
return null;
}
With the usual caveats about the robustness of illustrative code spippets.

Related

Can i use generic method to acess data from different entities in hibernate

For example: i have User.class and Post.Class
I want to get all rows from this tables with hibernate.
It is done with
TypedQuery<Post> query = SessionFactory.getCurrentSession().createQuery("from Post");
Also i have dao layer, UserDao and PostDao. and i have abstract class CrudDao with methods like:
public abstract class CrudDao<T> {
#Transactional
public void save(T entity) {
SessionFactory.getCurrentSession().save(entity);
}
}
So my user and post dao just extend this crudDao and i don't need to write save method for them. When i want to get all rows i need to write "from MyEntitie". But i want to make this method also abstract, so i don't need to write it multiple times for each dao. But i can't write "from T" in abstract method.
I also tried to do like this:
List<T> getAll(Class<T> type){
CriteriaQuery<T> criteria = builder.createQuery(type);
criteria.from(type);
return
SessionFactory.getCurrentSession().createQuery(criteria).getResultList();
}
So in my service i call my dao like this:
PostDao.getAll(Post.class);
And i was told that my service shouldn't know about my entity and my dao call in service should be
PostDao.getAll();
And if i do like this ^ i need to write getAll method in every dao and it looks like a lot of copypasted code.
Can yoy give me some advice on how to do it or how you do it in your projects?
What i learned from deHaar reply:
You can create abstract methods with generic dao and call generic class if you create a variable of this generic type/ For example my generic dao:
public abstract class CrudDao<T> {
private Class<T> type;
public CrudDao(Class<T> type){
this.type = type;
}
#Transactional
public T getById(,int postId) {
return sessionFactory.getCurrentSession().get(type, postId);
}
}
So you need only to write a constructor that calls superclass constructor in your child Dao's that extend generic dao.
Like :
public class PostCommentDao extends CrudDao<PostComment> {
public PostCommentDao(){
super(PostComment.class);
}
}
And now everything works from generic dao!
As Tanos said: small price to pay for salvation.
In my opinion, generic DAO is an antipattern. Spring provides you with excellent three-tier architecture made of #Controller (for mvc and rest), #Service (for functionality reusability) and #Repository (for data access). It's okay to have a little bit more code just to leave it with a single responsibility.

How to force Spring Data to create query methods with entity runtime type?

I've got around 5 objects that I want to do similar things with.
I figured out that not to polute the code I will put a logic for those objects in one place.
public class MetaObjectController<T extends MetaObject> {
#Autowired
private final MetaObjectRepository<T> repository;
// generic logic
Here's how repository looks:
public interface MetaObjectRepository<T extends MetaObject> extends GraphRepository<T> {
T findByName(String name);
}
Now, I create concrete class which uses delegation:
public class ExperimentalController {
#Autowired
private final MetaObjectController<MetaCategory> metaController;
#RequestMapping(method = RequestMethod.POST)
public void add(#RequestBody MetaCategory toAdd) {
metaController.add(toAdd);
}
Now, when I look at the generated queries I see, that although instantiated correctly, repository puts MetaObject as an entity name instead of runtime type.
Is there a way to force the repository to use runtime type?
Please don't advise to put a #Query annnotation. That's not what I am looking for.
This is most probably due to type erasure: at runtime there is only the type constraint available which is MetaObject. If you want to use (via spring-data) the actually relevant subclass you will have to create explicit interfaces of the MetaObjectRepository like this:
public class Transmogrifier extends MetaObject
public interface MetaTransmogrifierRepository
extends MetaObjectRepository<Transmogrifier> {}

Using generics and jpa EntityManager methods

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();
}

Inheritance in HQL with Spring Data JPA

I have set of classes which inherit from a single super class:
Super
|
+------+-------+
Aaaa Bbbb Cccc
Each of the Aaaa,Bbbb,Cccc then should contain method findByTag. The problem is that I can't manage to define it generally. Following example defines specific findByTag for Aaaa.
public interface AaaaRepository extends SuperRepository<Aaaa> {
#Query("select distinct a from Aaaa a " +
"join a.tags t " +
"join fetch a.locale where t = ?1")
public List<Event> findByTag(Tag t);
}
Note that the Superclass is #MappedSuperclass and does not have its own table in database.
I would like to use some kind of "Super" in the query which would be replaced in each class by its name.
My second problem is that I don't know how to force #ElementCollection to be Eagerly fetched. I have to always explicitly say "join fetch" in the query. If it is not fetched, once the transaction is finished, I can't access those objects, which I did not explicitly fetched. (LazyFetch Exceptions...)
Thanks
Looking at the documentation, custom implementations section, what about this approach:
Create an interface that extends repository and has your findByTag method, without annotations.
Create an implementation of that class, and in the method implementation you use the JPA criteria. You also need a class field to hold the actual class for the domain object, because generics are erased at compilation time. Then you use that field to build the criteria.
Read the documentation to use this implementation as a base class for the repository factory, then Spring Data will build implementations for the other repositories based on this custom one.
public interface MyRepository<T, ID> extends JpaRepository<T, ID> {
public List<Event> findByTag(Tag t);
}
public class MyRepositoryImpl<T, ID> implements MyRepository<T, ID> {
private Class<T> actualClass; // initialized in the constructor
public List<Event> findByTag(Tag t) {
// here you build the criteria using actualClass field, and execute it.
}
}
public interface AaaaRepository extends MyRepository <Aaaa, Integer> {
// other methods...
}
Look at "Example 1.16. Custom repository factory bean" of the documentation to create the factory bean.
When Spring instantiates the implementation of AaaaRepository, it will use MyRepositoryImpl as base class.
Will this work for you?
Instead of writing it this way, I would create a data access object that resembles the pseudo java code below:
class DAO<T> {
private Class<T> clazz;
DAO( Class<T> class) { this.clazz = t; }
#PersistenceContext
private EntityManager em;
public List<T> findByTag(Tag t ) {
Query q = em.createQuery( "select from " + clazz.getSimpleName + "....";
...
return q.getResultList();
}
}
Hope it helps!
In the end, I was so unhappy about the behaviour and inflexibility of the Spring Data JPA, so that I wrote myself a small tool for building the queries in a simple way. An example of using is here:
https://github.com/knyttl/Maite/wiki/Maite-Persistence
There are two children classes and the parent class which define the functionality. But the trick is in a fluent interface of building the query.
It is just in the beggining, but it already works so that I have no duplicity and correct inheritance.
Small example of the parent class - check the link above for detail:
#Autowired
EntityManager em;
protected abstract String getName();
protected Clause select() {
return em
.select("DISTINCT i")
.from(this.getName(), "i")
.joinFetch("i.locale lf")
}
public List<T> findByTag(Tag tag) {
return (T) this.select()
.join("i.tags t")
.where("t = ?", tag)
.fetchAll();
}

Dao Registry refactoring

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.

Categories

Resources