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();
}
Related
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.
This question already has answers here:
Generic Repository in Spring JPA
(2 answers)
Closed 4 years ago.
I'm beginning to work with Spring Data JPA repositories. We have an application already using Spring MVC (No spring boot or spring data JPA), where we have written a Generic DAO class that handles basic CRUD operations for virtually all entities that we have. Any other special operations can be handled by writing custom DAOs.
Now, Spring data JPA has made things very easy by requiring us to write only an interface and the rest is taken care of.
public interface PersonRepository extends JpaRepository<Person, Long> {
}
This is cool, but I was wondering if I can introduce generics here.
The reason is, my application has a number of entities for which we need to perform only basic CRUD operations and nothing more. Which means, for every entity, we need to write an interface. Though the code is minimal, it results in one file for each entity, which I guess can be avoided (true?).
My question is, can I write a generic Repository class like
public interface GenericRepository<T> extends JpaRepository<T, Long> {
}
so that my service class can look like this
#Autowired
private GenericRepository<Person> personRepository;
public List<Person> findAll() {
return this.personRepository.findAll();
}
This will be a much cleaner approach for basic operations, as one Repository interface handles a number of entities.
EDIT
It turns out that I can indeed create a repository interface as I illustrated above, but when the application starts, I get an error which says
Error creating bean with name 'genericRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class java.lang.Object
This is probably because of the Generic Type
I have to say that my entities are separate classes in themselves and do not implement or extend a super entity/entities. Would it help if they did?
Please guide me in the right direction.
Thanks!
I think you could do it like this:
#NoRepositoryBean
public interface GenericRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
//added custom common functionality for all the GenericRepository implementations
public List<T> findByAttributeContainsText(String attributeName, String text);
}
public class GenericRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
implements GenericRepository<T, ID> {
private EntityManager entityManager;
public GenericRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityManager = entityManager;
}
#Transactional
public List<T> findByAttributeContainsText(String attributeName, String text) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<T> cQuery = builder.createQuery(getDomainClass());
Root<T> root = cQuery.from(getDomainClass());
cQuery.select(root).where(builder.like(root.<String>get(attributeName), "%" + text + "%"));
TypedQuery<T> query = entityManager.createQuery(cQuery);
return query.getResultList();
}
}
public interface MyOtherRepository extends GenericRepository<Role, Long> {
}
And in your config class:
#Configuration
#EnableJpaRepositories(basePackages="com.myProject", repositoryBaseClass =
GenericRepositoryImpl.class)
public class JpaConfig {
}
I have some entity type that needs additional logic on saving (to be precise, I want to save position at the moment of saving). I don't want to do it with any DB-specific features, like triggers, because I'm not sure what will be the data storage used in future.
So I would like to override save() method.
In Spring Data JPA documentation I can see two ways of providing own implementation for repository classes:
Extend base repository class and tell Spring Data to use it.
Defining an interface (in my case I assume PositionedRepository) with an implementation class (PositionedRepositoryImpl).
Problem with first way - I don't want to implement it for all repositories, only two entity types are positioned.
Problem with second way - I don't have access to base repository methods, so apart from position calculation I would need to somehow build all of the queries, normally provided by base repository.
Any way to extend base repository class just for specific repository types?
Don't do that logic in the repository itself. Think about repositories as a dumb layer between java and the database. It just passes data from end to the other.
Instead you should handle that case in a different layer. A more intelligent one. The business logic layer.
See this example:
#Service
public class MyEntityService{
private final MyEntityRepository myEntityRepository;
private final OtherEntityRepository otherEntityRepository;
#Autowired
public MyEntityService(MyEntityRepository myEntityRepository,
OtherEntityRepository otherEntityRepository){
this.myEntityRepository = myEntityRepository;
this.otherEntityRepository = otherEntityRepository;
}
public void save(MyEntity myEntity){
// do stuff with otherEntityRepository
myEntitiyRepository.save(myEntity);
}
}
you can :
public class CustomJpaRepository<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> {
private final JpaEntityInformation<T, ?> entityInformationWrap;
private final EntityManager emWrap;
public CustomJpaRepository(JpaEntityInformation entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
entityInformationWrap=entityInformation;
emWrap=entityManager;
}
#Override
public <S extends T> S save(S entity) {
//doing
}
}
then main class add:
#EnableJpaRepositories(repositoryBaseClass = CustomJpaRepository.class)
As third option you can extend SimpleJpaRepository that implements JpaRepository and JpaSpecificationExecutor.
In this way, you could benefit from the default implementation of JpaRepository while being the ability to override these methods.
For example :
#Repository
public class PositionedRepository extends SimpleJpaRepository<Positioned, Long> {
#Override
public Positioned save(Positioned positioned) {
...
}
}
As fourth option you can also define your own savePositioned() method that uses under the hood the JpaRepository.save().
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();
}
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.