Saturday, November 21, 2009

From Spring applicationContext.xml to Spring Annotations

Moving from Spring applicationContext.xml or any other Spring xml file to Spring annotations is done in a few steps.

First, make sure your Spring’s jar file is at least version 2.5 or download the Spring jar from SpringSource downloads.

Second, your definitions of dataSource bean, sessionFactory bean etc. are still needed, so you still need to have an XML file, though all the POJO bean definitions are replaced by Spring annotations.

So what are the available annotations?

The annotations that I’m going to use in this article are as follows:

- @Repository - org.springframework.stereotype.Repository
Repository is a class level annotation that is used for DAO objects.

- @Service - org.springframework.stereotype.Service
Service is a class level annotation that is used for Business Logic and general Façade POJOs (spring beans).

- @Autowired - org.springframework.beans.factory.annotation.Autowired
Autowired is a method/field level annotation. It handles the automatic detection of relations between classes. It is defined on a setter or a field and indicates that Spring should ‘inject’/initialize the filed with the relevant spring bean.

- @Transactional – org.springframework.transaction.annotation.Transactional
defines the transactional type of the transactions (for example propagation REQUIRED, NEW etc.)
It can be used at the class or method level.

Other useful Spring annotations:

- @Component - org.springframework.stereotype.Component
Component is a class level annotation. Indicates a Spring managed bean.
Repository and Service are both sub-annotations of Component.

- @Controller - org.springframework.stereotype.Controller
Controller is a class level annotation. It is used for Controller/Web controller POJOs. It is a Component as well.


Here you can view the annotations documentation in Spring 2.5 API.

Example of how to replace the applicationContext.xml with Spring annotations:

Before using the annotations:

I have 2 Spring beans (POJOs), one is a façade (MyFacade) and the other is a DAO (MyDao):


---------------------------------------

package com.myStuff.impl;


import java.io.Serializable;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class MyDaoImpl extends HibernateDaoSupport implements MyDao {

public Serializable createORM(MyOrm orm) {
return getHibernateTemplate().save(orm);
}

}

----------------------------------------

package com.myStuff.impl;

public class MyFacadeImpl implements MyFacade {

private MyDao myDao;

public void setMyDao(MyDao myDao) {
this.myDao = myDao;
}

@Override
public void someMethod() {

...

}

}

----------------------------------------

applicationContext.xml is defined with the following bean properties:

<bean id="MyDao" class="com.myStuff.impl.MyDaoImpl"
parent="daoTmpl" />

<bean id="MyFacade" class="com.myStuff.impl.MyFacadeImpl"/>

<bean id="daoTmpl" abstract="true" lazy-init="default"autowire="default"
dependency-check="default">
<property name="sessionFactory">
<
ref bean="sessionFactory" />
property>
bean>

----------------------------------------

In order to replace the Spring beans with annotations let’s make the following modifications in the applicationContext.xml file - We need to make sure that the “beans” tag in the applicationContext.xml imports the correct xsd files and that we enable the annotations configuration:

----------------------------------------

xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<context:component-scan base-package="com.myStuff" />
<context:annotation-config />

<bean id="MyDao" class="com.myStuff.impl.MyDaoImpl"
parent="daoTmpl" />

<bean id="MyFacade" class="com.myStuff.impl.MyFacadeImpl"/>

...

beans>

----------------------------------------

Once we’ve updated the “beans” definitions and added the <context:component-scan base-package="com.myStuff" /> and <context:annotation-config /> tags to the xml file, we can remove MyDao and MyFacade Spring beans from the applicationContext.xml and add the annotations.

So, what you’ll finally get is:


After we replaced the beans definitions with annotations
:

The POJO classes:

----------------------------------------

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

@Repository(value = "MyDao")
@Transactional
public class MyDaoImpl extends HibernateDaoSupport implements MyDao {

/* I had to add constructor in order to initialize
HibernateDaoSupport’s sessionFacade object.
*/
@Autowired
public MyDaoImpl(SessionFactory sessionFactory) {
super.setSessionFactory(sessionFactory);
}

@Override
public Serializable createORM(MyOrm orm) {
return getHibernateTemplate().save(orm);
}

}

----------------------------------------

@Service("MyFacade")
@Transactional(propagation = Propagation.REQUIRED)
public class MyFacadeImpl implements {

private MyDao myDao;

@Autowired
public void setMyDao(MyDao myDao) {
this.myDao = myDao;
}

@Override
public void someMethod() {

...

}

}

----------------------------------------

And the applicationContext.xml would be:

----------------------------------------

xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<context:component-scan base-package="com.myStuff" /> <context:annotation-config />

...

beans>

----------------------------------------

And that’s all. No more defining your custom Spring beans in XMLs.

Tuesday, November 17, 2009

HibernateDaoSupport with annotations - Solved!

I've been trying to move from Spring applicationContext.xml to Spring annotations only.
One of the problems I was facing was that when I tried to move my Dao from xml to Spring annotations I got some errors.

My Dao class was defined as follows:

public class MyDaoImpl extends HibernateDaoSupport implements MyDao {
...
}

My xml configuration was -

<bean id="MyDao" class="com.myStuff.dao.impl.MyDaoImpl"
parent="daoTmpl" />

<bean id="daoTmpl" abstract="true" lazy-init="default" autowire="default"
dependency-check="default">
<property name="sessionFactory">
<ref bean="sessionFactory" />
property>
bean>


My first try was to replace the Dao definition with the Spring annotations the following way:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository(value = "MyDao")
@Transactional
public class MyDaoImpl extends HibernateDaoSupport implements MyDao {
...
}

But then I got an error saying that the SessionFactory was not initialized in the dao -

Caused by: java.lang.IllegalArgumentException: 'sessionFactory' or 'hibernateTemplate' is required

To solve this problem (i.e. to be able to use Spring annotations only and remove the Dao definition from the xml file) you should @Autowire the session factory by creating a constructor in the Dao class:

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository(value = "MyDao")
@Transactional
public class MyDaoImpl extends HibernateDaoSupport implements MyDao {

@Autowired
public MyDaoImpl (SessionFactory sessionFactory)
super.setSessionFactory(sessionFactory);
}

...

}

and of course, you can and should remove MyDao definition from the xml file (not the daoImpl)

Hope this helps anyone who got this problem as well

- Li

Monday, November 16, 2009

Hibernate Annotations - Composite Key

There are several ways to create an ORM mapped to a composite key using hibernate annotations.
A code sample that shows how to create a composite key with hibernate annotations can be find below:

The Composite Key class:
-------------------------
import javax.persistence.Embeddable;

@Embeddable
public class MyCompositeKey implements Serializable {

private static final long serialVersionUID = 8057719523073696665L;

private String name;
private String value;

public MyCompositeKey() {

}

public MyCompositeKey(String name, String value) {
this.name = name;
this.value = value;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name== null) ? 0 : name.hashCode());
result = prime * result + ((value== null) ? 0 : value.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyCompositeKey other = (MyCompositeKey)obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}

}
-------------------------------------------------
The ORM class with the composite key mappings:
-------------------------------------------------
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "my_table_name")
public class MyClassOrm {

private MyCompositeKey id;
private String data;

public MyClassOrm() {

}

@Id
@AttributeOverrides( {
@AttributeOverride(name = "name", column = @Column(name = "name")),
@AttributeOverride(name = "value", column = @Column(name = "value")) })
public MyCompositeKey getId() {
return id;
}

public void setId(MyCompositeKey id) {
this.id = id;
}

@Column(name = "data")
public String getData() {
return data;
}

public void setData(String data) {
this.data = data;
}
}