3. Simple Spring MVC Person DAO Bundle

The bundle uses JPA to create a Person service. The Person has a one to many relationship to Address, but to keep the example simple only the Person is used by the web module. JPA is using Hibernate for persistence.

Manifest Configuration

The bundle imports javax.sql, HSQL DB, Spring, and Hibernate. It also exports the package containing the Hibernate persistence beans and the Person DAO interface.

src/main/resources/META-INF/MANIFEST.MF
                    
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Simple Spring MVC Person Bundle
Bundle-Description: Simple Spring MVC Person
Bundle-SymbolicName: org.springbyexample.sdms.simpleForm.person  1
Bundle-Version: 1.0.0  2
Bundle-Vendor: Spring by Example
Import-Package: javax.sql  3
Import-Bundle: com.springsource.org.hsqldb;version="[1.8.0,2.0.0)"  4
Import-Library: org.springframework.spring;version="[2.5.5,3.0.0)",
 org.hibernate.ejb;version="[3.3.2.GA,3.3.2.GA]"  5
Export-Package: org.springbyexample.sdms.simpleForm.orm.bean;version="1.0.0",
 org.springbyexample.sdms.simpleForm.orm.dao;version="1.0.0"  6
                    
                
1 The symbolic name the bundle is deployed under.
2 The version the bundle is deployed under.
3 Imports the package javax.sql.
4 Imports the HSQL DB bundle.
5 The two libraries for Spring and Hibernate are resolved based on their version ranges.
6 The package containing the persistence beans and the one with the Person DAO interface are both exported under the version 1.0.0.

JPA Configuration

The JPA configuration sets up Hibernate as the persistence provider and registers the two entity classes Person and Address.

src/main/resources/META-INF/persistence.xml
                    
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
             version="1.0">
    <persistence-unit name="personDatabase">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <class>org.springbyexample.sdms.simpleForm.orm.bean.Person</class>
        <class>org.springbyexample.sdms.simpleForm.orm.bean.Address</class>
        
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
            <!--
                Note: setting 'hibernate.hbm2ddl.auto' to 'create' will result in
                'import.sql' (in the root of the classpath) being used to populate
                the DB
            -->
            <property name="hibernate.hbm2ddl.auto" value="create" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider" />
        </properties>
    </persistence-unit>
</persistence>
                    
                

Spring Configuration

The context:component-scan is used to Person DAO implementation, and tx:annotation-driven configures Spring to process transaction annotations. A JPA transaction manager is created and JPA with i and also with the DataSource from the OSGi service.

src/main/resources/META-INF/spring/bundle-context.xml
                    
<?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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd">
	
	<context:component-scan base-package="org.springbyexample.sdms.simpleForm.orm.impl"/>
	
	<tx:annotation-driven />

	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory"/>
	</bean>
		
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    	<property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
    </bean>
	
</beans>
                    
                

The javax.sql.DataSource exposed by the previous bundle is referenced, and the Person DAO implementation is registered as an OSGi service.

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

    <reference id="dataSource" interface="javax.sql.DataSource" />
    
    <service ref="personDao" interface="org.springbyexample.sdms.simpleForm.orm.dao.PersonDao" />

</beans:beans>
                    
                

Code Example

The Person entity uses javax.persistence annotation for configuration.

Example 1. Person Entity

Excerpt from sdms/simple-spring-mvc/simple-spring-mvc-person/src/main/java/org/springbyexample/sdms/simpleForm/orm/bean/Person.java

                    
@Entity
@Table(name="PERSON")
public class Person implements Comparable<Person>, Serializable {

    ...
    
    private Integer id = null;
    private String firstName = null;
    private String lastName = null;
    private Set<Address> addresses = null;
    private Date created = null;
    
    /**
     * Gets id (primary key).
     */
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    public Integer getId() {
        return id;
    }

    /**
     * Sets id (primary key).
     */
    public void setId(Integer id) {
        this.id = id;
    }
    
    /**
     * Gets first name.
     */
    @Column(name="FIRST_NAME")
    public String getFirstName() {
        return firstName;
    }

    /**
     * Sets first name.
     */
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    
    /**
     * Gets last name.
     */
    @Column(name="LAST_NAME")
    public String getLastName() {
        return lastName;
    }
    
    /**
     * Sets last name.
     */
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    /**
     * Gets list of <code>Address</code>es.
     */
    @OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
    @JoinColumn(name="PERSON_ID", nullable=false)
	public Set<Address> getAddresses() {
		return addresses;
	}

    /**
     * Sets list of <code>Address</code>es.
     */
	public void setAddresses(Set<Address> addresses) {
		this.addresses = addresses;
	}

    /**
     * Gets date created.
     */
	public Date getCreated() {
		return created;
	}

    /**
     * Sets date created.
     */
	public void setCreated(Date created) {
		this.created = created;
	}
	
	public Address findAddressById(Integer id) {
	    Address result = null;
	    
	    if (addresses != null) {
	        for (Address address: addresses) {
	            if (address.getId().equals(id)) {
	                result = address;
	                
	                break;
	            }
	        }
	    }
	    
	    return result;
	}
	
    ...

}
                    
                

The Person DAO implementation uses the JPA EntityManager for managing a Person.

Example 2. Person DAO Implementation

sdms/simple-spring-mvc/simple-spring-mvc-person/src/main/java/org/springbyexample/sdms/simpleForm/orm/impl/PersonDaoImpl.java
                    
@Repository("personDao")
@Transactional(readOnly = true)
public class PersonDaoImpl implements PersonDao {

    private EntityManager em;

    @PersistenceContext
    public void setEntityManager(EntityManager em) {
        this.em = em;
    }
    
    /**
     * Find persons.
     */
    @SuppressWarnings("unchecked")
    public Person findPersonById(Integer id) {
        return em.find(Person.class, id);
    }
    
    /**
     * Find persons using a start index and max number of results.
     */
    @SuppressWarnings("unchecked")
    public Collection<Person> findPersons(final int startIndex, final int maxResults) {
        return em.createQuery("select p from Person p order by p.lastName, p.firstName")
            .setFirstResult(startIndex).setMaxResults(maxResults).getResultList();
    }

    /**
     * Find persons.
     */
    @SuppressWarnings("unchecked")
    public Collection<Person> findPersons() {
        return em.createQuery("select p from Person p order by p.lastName, p.firstName").getResultList();
    }

    /**
     * Find persons by last name.
     */
    @SuppressWarnings("unchecked")
    public Collection<Person> findPersonsByLastName(String lastName) {
        return em.createQuery("select p from Person p where p.lastName = :lastName order by p.lastName, p.firstName")
            .setParameter("lastName", lastName).getResultList();
    }
    
    /**
     * Saves person.
     */
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public Person save(Person person) {
        return em.merge(person);
    }

    /**
     * Deletes person.
     */
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void delete(Person person) {
        em.remove(em.merge(person));
    }

    /**
     * Saves address to person.
     */
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public Person saveAddress(Integer id, Address address) {
        Person person = findPersonById(id);

        if (person.getAddresses().contains(address)) {
            person.getAddresses().remove(address);
        }
        
        person.getAddresses().add(address);
        
        return save(person);
    }
    
    /**
     * Deletes address from person.
     */
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public Person deleteAddress(Integer id, Integer addressId) {
        Person person = findPersonById(id);
        
        Address address = new Address();
        address.setId(addressId);
        
        if (person.getAddresses().contains(address)) {
            person.getAddresses().remove(address);
        }
        
        return save(person);
    }
    
}