I try this generic code because i don't want to create a dao class for each entity i have in my database because i have 80 ones specially for those who i will excecute just CRUD query. because in most case i need just to persist or make a find by id.
public interface GenericDao<T, PK extends Serializable> {
T create(T t);
T read(PK id);
T update(T t);
void delete(T t);
}
the impl of the interface
@Component
public class GenericDaoJpaImpl<T, PK extends Serializable>
implements GenericDao<T, PK> {
protected Class<T> entityClass;
@PersistenceContext
protected EntityManager entityManager;
public GenericDaoJpaImpl() {
ParameterizedType genericSuperclass = (ParameterizedType) getClass()
.getGenericSuperclass();
this.entityClass = (Class<T>) genericSuperclass
.getActualTypeArguments()[0];
}
@Override
public T create(T t) {
this.entityManager.persist(t);
return t;
}
@Override
public T read(PK id) {
return this.entityManager.find(entityClass, id);
}
@Override
public T update(T t) {
return this.entityManager.merge(t);
}
@Override
public void delete(T t) {
t = this.entityManager.merge(t);
this.entityManager.remove(t);
}
@Override
public void delete(Set<T> ts) {
for( T t : ts){
t = this.entityManager.merge(t);
this.entityManager.remove(t);
}
}
}
the exception
Caused by:
org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [dao.GenericDaoJpaImpl]:
Constructor threw exception; nested exception is
java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
How to resolve this and what means this ParameterizedType
and why we have to use it in the constructor ?
and when i comment the constructor it works except for public T read(PK id)
i got null pointer exception
public GenericDaoJpaImpl() {
// ParameterizedType genericSuperclass = (ParameterizedType) getClass()
// .getGenericSuperclass();
// this.entityClass = (Class<T>) genericSuperclass
// .getActualTypeArguments()[0];
}
I use it like this :
@Autowired
private GenericDaoJpaImpl<AlerteAcheteur, Integer> acheteurAlerteDao;
i don't want to create a abstract
class and extend it like this :
public class AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer> ... {
}
@Autowired
private AlerteAcheteurGenericDaoJpaImpl<AlerteAcheteur, Integer> acheteurAlerteDao;
答案 0 :(得分:4)
不幸的是,没有办法让它完全按照您的意愿运作。
请注意GenericDaoJpaImpl
的确切声明 -
GenericDaoJpaImpl<T, PK extends Serializable> implements GenericDao<T, PK>
。
ClassCastException
被抛出,因为getClass().getGenericSuperclass()
返回Class<java.lang.Object>
的实例,Type
(java.lang.Class
实现java.lang.reflect.Type
),但不是ParameterizedType
{1}}。实际上,对于直接超类为Class<java.lang.Object>
的每个类,getClass().getGenericSuperclass()
都会返回java.lang.Object
的实例。因此,像
public GenericDaoJpaImpl() {
ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
this.entityClass = (Class<T>) genericSuperclass.getActualTypeArguments()[0];
}
适用于声明为AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer>
的类。但这正是你不想宣布你的DAO的方式。
小部件1,如果从您的GenericDaoJpaImpl
投放,则会打印T
和PK
(两者都是sun.reflect.generics.reflectiveObjects.TypeVariableImpl
的实例)。
Snippet 1
Type[] genericInterfaces = getClass().getGenericInterfaces();
ParameterizedType genericInterface = (ParameterizedType) genericInterfaces[0];
System.out.println(genericInterface.getActualTypeArguments()[0]);
System.out.println(genericInterface.getActualTypeArguments()[1]);
Snippet 2
@Bean(name = "alerteVendeurDao")
public GenericDao<AlerteVendeur, Long> alerteVendeurDao() {
return new GenericDaoJpaImpl<AlerteVendeur, Long>();
}
即使在@Configuration
- 带注释的类中有类似Snippet 2的内容,在运行时也无法知道由于type erasure已将参数化为GenericDaoJpaImpl
的内容。但是,如果Snippet 1是从AlerteAcheuteurDao implements GenericDao<AlerteAcheuteur, Long>
执行的,则会打印class somepackage.entity.AlerteAcheteur
和class java.lang.Long
(因为这些参数是明确的并且在编译时已知)。
最后,组件扫描在逻辑上甚至不适用于GenericDaoJpaImpl
。 @Component
- 注释类的Bean是“Singleton”-scoped。除了只创建一个实例的事实之外,我们怎么会知道这个单例DAO应该在哪个实体上运行呢?尽管如此,容器仍然能够实例化GenericDaoJpaImpl
,因为在运行时类型信息已被删除(type erasure!)。
此外,在相关案例中,建议使用更具体的@Repository
而不是@Component
来注释DAO。
在您的特定情况下,最好的办法是将实体类声明为构造函数参数。通过这种方式,可以通过将适当的构造函数参数传递给每个实例,在Spring配置中创建许多特定于实体的GenericDaoJpaImpl
实例。
GenericDaoJpaImpl.java
public class GenericDaoJpaImpl<T, PK extends Serializable>
implements GenericDao<T, PK> {
private final Class<T> entityClass;
@PersistenceContext
protected EntityManager entityManager;
public GenericDaoJpaImpl(Class<T> entityClass) {
this.entityClass = entityClass;
}
@Override
public T create(T t) {
this.entityManager.persist(t);
return t;
}
@Override
public T read(PK id) {
return this.entityManager.find(entityClass, id);
}
@Override
public T update(T t) {
return this.entityManager.merge(t);
}
@Override
public void delete(T t) {
t = this.entityManager.merge(t);
this.entityManager.remove(t);
}
@Override
public void delete(Set<T> ts) {
for( T t : ts){
t = this.entityManager.merge(t);
this.entityManager.remove(t);
}
}
}
AnnotationContextConfiguration.java
请注意,也可以通过constructor-based dependency injection以XML格式执行相同操作。
@Configuration
@ComponentScan("somepackage.service")// scan for services, but not for DAOs!
public class Config {
@Bean(autowire = Autowire.BY_NAME)
public GenericDaoJpaImpl<AlerteAcheteur, Long> alerteAcheteurDao() {
return new GenericDaoJpaImpl<AlerteAcheteur, Long>(AlerteAcheteur.class);
}
@Bean(autowire = Autowire.BY_NAME)
public GenericDao<AlerteVendeur, Long> alerteVendeurDao() {
return new GenericDaoJpaImpl<AlerteVendeur, Long>(AlerteVendeur.class);
}
// other DAOs
...
}
AlerteServiceImpl.java (看起来怎么样)
请注意,字段名称很重要,因为DAO是按名称自动装配的。如果您不想为alerteAcheteurDao
这样的字段命名,可以将@Qualifier
与@Autowired
一起使用。
@Service
public class AlerteServiceImpl implements AlerteService {
@Autowired
private GenericDao<AlerteAcheteur, Long> alerteAcheteurDao;
@Autowired
private GenericDao<AlerteVendeur, Long> alerteVendeurDao;
...
}
这是一个非常优雅的解决方案。您不必使用AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer>
之类的垃圾邮件类。添加新实体后,您只需向Spring配置中添加GenericDaoJpaImpl
的新实例。
我希望这会有所帮助。
答案 1 :(得分:2)
你的GenericDaoJpaImpl应该是抽象的。只有具体的后代类型才能解析泛型类型T,PK并定义为Spring bean。