Moving from Spring's XML to Annotations in AppFuse
Last night, I did a spike on AppFuse to change XML to Spring annotations (@Repository, @Service and @Autowired) in its service and data modules. While I was able to accomplish everything in a few hours (including converting tests), I did run into a couple issues.
AbstractTransactionalJUnit4..Tests vs. AbstractTransactionalDataSource..Tests
I've switched from my favorite Spring class to the annotation-happy AbstractTransactionalJUnit4SpringContextTests. However, this has presented an issue: when using ATDSSCT, I was able to call endTransaction() and startNewTransaction(). With ATJ4SCT, this doesn't seem possible. Below is a screenshot of the diff on a test method in the JPA implementation of UserDaoTest:
On the right, you'll notice that I had to comment out @ExpectedException to get the test to pass. This concerns me since this exception should be thrown. Is there a way to call endTransaction() and startNewTransaction() when subclassing AbstractTransactionalJUnit4SpringContextTests?
Instantiating GenericDao Implementations Programmatically
The second feature I tried to add is the ability to instantiate a GenericDao programatically rather than requiring a XML bean definition. In current versions of AppFuse, you can use the following bean definition to create a GenericDao for a model object.
<bean id="personDao" class="org.appfuse.dao.hibernate.GenericDaoHibernate">
<constructor-arg value="org.appfuse.tutorial.model.Person"/>
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
When moving to a no-XML required architecture, it'd be nice to allow users to create GenericDao's programmatically. Below is the easiest way I've found to do this in a test:
GenericDao<User, Long> genericDao;
@Autowired
SessionFactory sessionFactory;
@Before
public void setUp() {
genericDao = new GenericDaoHibernate<User, Long>(User.class);
genericDao.setSessionFactory(sessionFactory);
}
However, there's a couple problems with this. First of all, mixing constructor injection and setter injection probably isn't a good idea. Changing the constructor to take a SessionFactory solves this problem, but now all subclasses need to have a more verbose constructor:
@Autowired
public UserDaoHibernate(SessionFactory sessionFactory) {
super(User.class, sessionFactory);
}
Whereas before they had:
public UserDaoHibernate() {
super(User.class);
}
In an ideal world, I could call new GenericDaoHibernate<User, Long>(User.class) and the SessionFactory would be wired in auto-magically. Is this possible with Spring 2.5?
The 2nd problem this presents is your client code will now be dependent on an implementation rather than the interface. I don't know how to solve that one, but I'd love to figure out a way to create GenericDaos with no XML and no implementation details in the client. Any ideas are most welcome.
If you'd like to see all the changes I made in converting from XML to Annotations, please see this patch.
From http://raibledesigns.com/rd/entry/moving_from_spring_s_xml
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)






Comments
Mike Funk replied on Wed, 2008/11/05 - 10:25am
Consider building a true generic entity manager (just one class) that is autowired with a hibernate template. Then autowire your generic entity manager into any service, and use transactional annotations as required. No more daos.
Jonathan Curran replied on Wed, 2008/11/05 - 12:36pm
in response to:
Mike Funk
Mike Funk replied on Thu, 2008/11/06 - 12:20pm
in response to:
Jonathan Curran
Hopefully, this will help:
1. Create a generic spring-hibernate entity manager:
@Repository
public class HibernateGenericEntityManager {
@Autowired private HibernateTemplate hibernateTemplate;
public HibernateGenericEntityManager() {
}
public Object execute(HibernateCallback callback) {
return hibernateTemplate.execute(callback);
}
public <E> void save(E entity) {
hibernateTemplate.saveOrUpdate(entity);
}
public <E> void save(Collection<E> entities) {
hibernateTemplate.saveOrUpdateAll(entities);
}
public <E> void delete(E entity) {
hibernateTemplate.delete(entity);
}
public void delete(Class type, Serializable key) {
delete(find(type, key));
}
@SuppressWarnings("unchecked")
public <E> E find(Class type, Serializable id) {
return (E) hibernateTemplate.load(type, id);
}
@SuppressWarnings("unchecked")
public <E> List<E> list(Class type) {
return (List<E>) hibernateTemplate.loadAll(type);
}
@SuppressWarnings("unchecked")
public <E> List<E> query(String namedQuery) {
return hibernateTemplate.findByNamedQuery(namedQuery);
}
@SuppressWarnings("unchecked")
public <E> List<E> query(String namedQuery, Object... values) {
return hibernateTemplate.findByNamedQuery(namedQuery, values);
}
}
2. Create a service:
@Service
@Transactional(propagation = Propagation.REQUIRED)
public class DefaultOrderService implements OrderService {
@Autowired private HibernateGenericEntityManager entityManager;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Order order(Order order) {
process(order);
entityManager. save(order);
}
/** Transactional with class-level default propagation.
private void process(Order order) {
}
}
3. Create a spring context:
<beans>
<context:component-scan base-package="persistence.hibernate"/>
<context:component-scan base-package="service.order"/>
<context:spring-configured/>
<!-- Configure a datasource, sessionFactory, hibernateTemplate, HibernateTransactionManager, etc -->
<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj"/>
</beans>
4. Configure maven to weave in aspects at compile time:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.6.0</version>
</dependency>
</dependencies>
<configuration>
<complianceLevel>1.5</complianceLevel>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
My apologies for the long post. I have no time to write an article.
Jonathan Curran replied on Thu, 2008/11/06 - 1:12pm
in response to:
Mike Funk
jame jack replied on Mon, 2009/06/29 - 4:34pm