我有一个BaseRepository泛型抽象类,其中包含一些可以与JPA一起使用的有趣方法。最近,我已经养成了针对预定义接口编写DAO和服务的习惯。
有没有办法将这两者结合起来?
换句话说,我怎样才能在实现接口的同时扩展抽象类?
经过实验,我得出以下结论:
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import org.hibernate.mapping.PersistentClass;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Transactional
@Service
@SuppressWarnings("unchecked")
public abstract class BaseRepository<E extends Identifiable<PK>, PK extends Serializable> {
/**
* Class corresponding to E. For example, if you define class BankRepository
* extends BaseRepository<Bank, Long>, then after BankRepository bankRep =
* new BankRepository(); bankRep.entityClass will be Bank.class. Assigned in
* the constructor.
*/
private Class<E> entityClass;
@PersistenceContext
protected EntityManager em;
// => 6.1.5.3. Introspection With Spring
// This constructor will probably be executed TWICE by Spring (for each
// repository):
// once for the real Repository, and once for instantiating a proxy.
public BaseRepository() {
// // We try to know, at runtime, the class associated to E, and put it
// in this.entityClass.
// 1. find someEntityRepositoryClass (== BankRepository.class)
Class someEntityRepositoryClass; // Your repository class, i.e.
// BankRepository (extending
// BaseRepository<Bank,Long>)
if (this.getClass().getSuperclass() == BaseRepository.class) { // We are
// instantiated
// without
// CGLIB:
// new
// BankRepository()
someEntityRepositoryClass = this.getClass();
} else { // Spring instantiates as CGLIB class
// BankRepository$$EnhancedByCGLIB$$de100650 extends
// BankRepository:
// new BankRepository$$EnhancedByCGLIB$$de100650()
Class cglibRepositoryClass = this.getClass();
someEntityRepositoryClass = cglibRepositoryClass.getSuperclass();
}
// 2. find the ancestor of BankRepository.class, which is
// BaseRepository<E, PK>.class
ParameterizedType baseRepositoryType = (ParameterizedType) someEntityRepositoryClass
.getGenericSuperclass();
// 3. Extract the type of E (from BaseRepository<E, PK>.class)
Type entityTypeOne = (baseRepositoryType).getActualTypeArguments()[0];
entityClass = (Class<E>) entityTypeOne;
}
public E find(final PK id) {
// TODO Validate.notNull(id, "The id cannot be null");
return em.find(entityClass, id);
}
public E persist(final E entity) {
// TODO Validate.notNull(entity, "The entity cannot be null");
em.persist(entity);
return entity;
}
public E merge(final E object) {
// TODO Validate.notNull(object, "The object cannot be null");
return em.merge(object);
}
public List<E> findAll() {
return em.createQuery(
"Select distinct e from " + getEntityName() + " e")
.getResultList();
}
public List<E> findAll(final int first, final int max) {
return em.createQuery("Select e from " + getEntityName() + " e")
.setFirstResult(first).setMaxResults(max).getResultList();
}
public List<E> findAll(final int max) {
return em.createQuery("Select e from " + getEntityName() + " e")
.setMaxResults(max).getResultList();
}
public void remove(final PK id) {
em.remove(this.find(id));
}
public void remove(final E entity) {
em.remove(entity);
}
public Class<E> getEntityClass() {
return entityClass;
}
/**
* Returns the name of the entity which is probably the same as the class
* name. But if on, the entity we used @Entity(name="..."), then the name of
* the entity is different. Useful for building query strings:
* "select w from Worker" where "Worker" is the entity name. CURRENT
* IMPLEMENTATION JUST RETURNS THE CLASSNAME!!!
*/
public String getEntityName() {
// how to get Configuration configuration? I'm afraid we'll have to know
// how JPA is initialized,
// and this class (BaseRepositoty) does not want to know that.
// for (PersistentClass pc : configuration.getClassMappings()) {
// if (pc.getMappedClass().equals(entityClass)) {
// return pc.getEntityName();
// }
// }
// throw new
// IllegalStateException("entityClass not found in Hibernate configuration. EntityClas=["+entityClass+"]");
// Simplistic result in the meanwhile ;-)
return entityClass.getSimpleName();
}
protected EntityManager getEntityManager() {
return em;
}
public void setEntityManager(final EntityManager entityManager) {
this.em = entityManager;
}
}
public interface Identifiable<K> {
K getId();
}
答案 0 :(得分:1)
如上所述,没有问题。实际上,这种模式在几个框架中使用(如Spring)。您应该将名称从BaseRepository更改为类似AbstractSingleIdRepository的名称。前缀“Abstract”表示模板类。
此外,在公共接口上公开常用方法并使抽象类实现该接口可能是个好主意。另外,子类可以实现自己的自定义接口。
其他一些评论: