Generic Java service with programmatic bean creation does not inject EntityManager
I have a (hopefully) standard set-up with a bunch of @Controller
, @Service
, @Entity
and @Repository
annotated classes, for example:
@Service
public class ChannelManager {
@Autowired
private ChannelDao channelDao;
public List<Channel> allChannels() {
return channelDao.findAll();
}
}
where ChannelDao
is:
public interface ChannelDao extends PersistentDao<Channel> {}
ChannelImpl
is:
@Repository
public class ChannelImpl extends PersistentImpl<Channel> implements ChannelDao {}
Channel
is:
@Entity开发者_如何学JAVA
@Table(name = "channel")
public class Channel extends Persistent {
@Id
@Column(name = "id", nullable = false)
private Long id;
// set/get
}
PersistentDao
is:
public interface PersistentDao<T extends Persistent> {
public List<T> findAll();
}
and finally PersistentImpl
is:
public class PersistentImpl<T extends Persistent> implements PersistentDao<T> {
@PersistenceContext
protected EntityManager entityManager;
private Class<T> clazz; // gets assigned correctly
public List<T> findAll() {
return entityManager.createQuery("from " + clazz.getSimpleName()).getResultList();
}
}
All this works as expected. I am using <context:annotation-config/>
and <context:component-scan/>
as well as the <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" ...>
with a persistence.xml
. Basically it all works.
But now I want to generalize the ChannelManager since I have many other entities and want to avoid duplication. For example I also need a JobManager with a List<Job> allJobs() { return jobDao.findall(); }
method.
So I created this:
@Service
public class GeneralManager<T extends PersistentDao, E extends Persistent> {
@Autowired
private AutowireCapableBeanFactory factory;
public void setFactory(AutowireCapableBeanFactory factory) {
this.factory = factory;
}
private Class<? extends T> impl;
private T dao;
public GeneralManager(Class<? extends T> impl) throws Exception {
this.impl = impl;
}
@PostConstruct
public void postCtor() {
dao = factory.createBean(impl); // seems to be created and is not null
}
public List<E> all() {
return dao.findAll();
}
}
And I updated the context.xml
<bean id="jobImpl" class="a.b.c.GeneralManager">
<property name="factory" ref="autowireFactory"/>
<constructor-arg value="a.b.c.JobImpl"/>
</bean>
<bean id="autowireFactory" class="org.springframework.beans.factory.support.DefaultListableBeanFactory"/>
<context:annotation-config/>
<context:component-scan base-package="a.b.c">
<context:exclude-filter type="regex" expression=".*GeneralManager"/>
</context:component-scan>
And finally added a declaration in the controller:
@Autowired
@Qualifier(value="jobImpl")
private GeneralManager<JobDao, Job> jobManager;
Just imagine the Jobxxx classes to be similar to the Channelxxx ones.
However, when it comes to execute the findAll()
method in the JobImpl
specialization, the entityManager is null, so something is not set-up correctly.
So my question really is "can this be done"? Although if there is an alternate and nicer way to do this please enlighten me!
Edit: Might be of note that I'm using Spring 3.0.5, Java 1.6.0_21-b07 and Hibernate 3.6.2.Final
So after thinking a bit more about it, I've gone in a slightly different direction by explicitly defining the beans which was something I was trying to avoid. But it is working for me. I still don't know if what I was trying to do was just not possible because of type erasure.
My code now looks like:
public class GeneralManager<T extends PersistentDao, E extends Persistent> {
private T dao;
public void setDao(T dao) {
this.dao = dao;
}
...
}
with the controller using:
@Autowired
private GeneralManager<ChannelDao, Channel> channelManager;
@Autowired
private GeneralManager<JobDao, Job> jobManager;
and context.xml now has:
<bean id="jobManager" class="com.a.b.service.EntityManager" p:dao-ref="jobImpl"/>
<bean id="jobImpl" class="com.a.b.dao.JobImpl"/>
<bean id="channelManager" class="com.a.b.service.EntityManager" p:dao-ref="channelImpl"/>
<bean id="channelImpl" class="com.a.b.dao.ChannelImpl"/>
Hope this helps someone!
why not simply use spring-data project? it does exactly what you need and more.
精彩评论