不幸的是,没有任何方法可以使其完全按照您的要求工作。
需要注意的确切声明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
。实际上,对于每个直接超类为的类,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
)。
片段1
Type[] genericInterfaces = getClass().getGenericInterfaces();
ParameterizedType genericInterface = (ParameterizedType) genericInterfaces[0];
System.out.println(genericInterface.getActualTypeArguments()[0]);
System.out.println(genericInterface.getActualTypeArguments()[1]);
片段2
@Bean(name = "alerteVendeurDao")
public GenericDao<AlerteVendeur, Long> alerteVendeurDao() {
return new GenericDaoJpaImpl<AlerteVendeur, Long>();
}
即使存在Snippet 2中带@Configuration
注释的类之类的内容,在运行时也无法知道GenericDaoJpaImpl
由于类型Erase导致参数化了。但是,如果代码片段1是从类似的代码执行的AlerteAcheuteurDao implements GenericDao<AlerteAcheuteur, Long>
,class somepackage.entity.AlerteAcheteur
并且class java.lang.Long
将被打印(因为这些参数是明确的,并且在编译时是已知的)。
最后,组件扫描甚至在逻辑上都不适用于GenericDaoJpaImpl
。带@Component
注释类的Bean 是“ Singleton”作用域的。除了将只创建一个实例这一事实之外,我们如何知道该单例DAO应该在哪个实体上运行?尽管如此,容器仍然能够实例化GenericDaoJpaImpl
,因为在运行时类型信息已经被擦除(类型擦除!)。
此外,在相关情况下,建议使用更具体的注释DAO@Repository
,而不是@Component
。
在您的特定情况下,最好的选择是将实体类声明为构造函数参数。这样,可以GenericDaoJpaImpl
通过将适当的构造函数参数传递给每个实例来在Spring配置中创建大量特定于实体的实例。
通用DaoJpaImpl.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
请注意,也可以通过基于构造函数的依赖注入在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>
。添加新实体后,您只需GenericDaoJpaImpl
向Spring配置添加新实例。
我希望这会有所帮助。