Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Info
titleSource Code

The code for this tutorial is located in the "tutorial-jpa" module of the appfuse-demos project on Google Code. Use the following command to check it out from Subversion:

No Format

svn checkout http://appfuse-demos.googlecode.com/svn/trunk/tutorial-jpa

...

To register a personDao bean, open src/main/webapp/WEB-INF/applicationContext.xml (or core/src/main/resources/applicationContext.xml for a modular archetype) and add the following to it:

Code Block
languagexml

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

    <bean id="personDao" class="org.appfuse.dao.jpa.GenericDaoJpa">
        <constructor-arg value="org.appfuse.tutorial.model.Person"/> 
    </bean> 
</beans>

After doing this, you can use this bean on an object by adding the following setter method:

Code Block
languagejava

public void setPersonDao(GenericDao<Person, Long> personDao) {
    this.personDao = personDao;
}

...

To start, create a PersonDaoTest.java class in your src/test/java/**/dao directory (or core/src/test/java/**/dao directory for a modular archetype). This class should extend org.appfuse.dao.BaseDaoTestCase, a subclass of Spring's AbstractTransactionalJUnit4SpringContextTests. This parent class is used to load Spring's ApplicationContext (since Spring binds interfaces to implementations), and for (optionally) loading a .properties file that has the same name as your *Test.class. In this example, if you put a PersonDaoTest.properties file in src/test/resources/org/appfuse/tutorial/dao, this file's properties will be available via an "rb" variable.

Code Block
languagejava

package org.appfuse.tutorial.dao;

import org.appfuse.dao.BaseDaoTestCase;
import org.appfuse.tutorial.model.Person;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.test.annotation.ExpectedException;

import java.util.List;

import static org.junit.Assert.*;

public class PersonDaoTest extends BaseDaoTestCase {
    @Autowired
    private PersonDao personDao;
}

...

Now you need test that the finder method works in your DAO. AppFuse uses JUnit 4, which allows you to indicate test methods with a @Test annotation. Add the following method to your PersonDaoTest.java file:

Code Block
languagejava

@Test
public void testFindPersonByLastName() throws Exception {
    List<Person> people = personDao.findByLastName("Raible");
    assertTrue(people.size() > 0);
}

You'll notice that this method relies on pre-existing data in order to pass. The DbUnit Maven Plugin is used to populate the database with test data before the tests are run, so you can simply add the new table/record to the src/test/resources/sample-data.xml file (or core/src/test/resources/sample-data.xml for a modular archetype).

Code Block
languagexml

<table name='person'>
  <column>id</column>
  <column>first_name</column>
  <column>last_name</column>
  <row>
    <value>1</value>
    <value>Matt</value>
    <value>Raible</value>
  </row>
</table>

Since the PersonDao you're about to write includes CRUD functionality, you can also write a test to verify CRUD works properly.

Code Block
languagejava

@Test
@ExpectedException(DataAccessException.class)
public void testAddAndRemovePerson() throws Exception {
    Person person = new Person();
    person.setFirstName("John");
    person.setLastName("Elway");

    person = personDao.save(person);

    person = personDao.get(person.getId());

    assertEquals("John", person.getFirstName());
    assertNotNull(person.getId());

    log.debug("removing person...");

    personDao.remove(person.getId());

    // should throw DataAccessException
    personDao.get(person.getId());
}

In the above example, you can see that person.set*(value) is being called to populate the Person object before saving it. This is easy in this example, but it could get quite cumbersome if you're persisting an object with 10 required fields. This is why a ResourceBundle exists in BaseDaoTestCase. Simply create a PersonDaoTest.properties file in the same directory as PersonDaoTest.java and define your property values inside it:

Code Block

firstName=Matt
lastName=Raible

...

Then, rather than calling person.set* to populate your objects, you can use the BaseDaoTestCase.populate(java.lang.Object) method:

Code Block
languagejava

Person person = new Person();
person = (Person) populate(person);

...

Create a PersonDao.java interface in the src/main/java/**/dao (or core/src/main/java/**/dao for a modular archetype) directory and specify the finder method for any implementation classes.

Code Block
languagejava

package org.appfuse.tutorial.dao;

import org.appfuse.dao.GenericDao;
import org.appfuse.tutorial.model.Person;

import java.util.List;

public interface PersonDao extends GenericDao<Person, Long> {
    public List<Person> findByLastName(String lastName);
}

...

Create a PersonDaoJpa class that implements the finder method in PersonDao. To do this, create a new class in src/main/java/**/dao/jpa (or _core/src/main/java/**/dao/jpa for the modular archetype) and name it PersonDaoJpa.java. It should extend GenericDaoJpa and implement PersonDao. Javadocs eliminated for brevity.

Code Block
languagejava

package org.appfuse.tutorial.dao.jpa;

import org.appfuse.dao.jpa.GenericDaoJpa;
import org.appfuse.tutorial.dao.PersonDao;
import org.appfuse.tutorial.model.Person;
import org.springframework.stereotype.Repository;

import javax.persistence.Query;
import java.util.List;

@Repository("personDao")
public class PersonDaoJpa extends GenericDaoJpa<Person, Long> implements PersonDao {

    public PersonDaoJpa() {
        super(Person.class);
    }

    @SuppressWarnings("unchecked")
	public List<Person> findByLastName(String lastName) {
        Query q = getEntityManager().createQuery("select p from Person p where p.lastName=?");
        q.setParameter(1, lastName);
        return q.getResultList();
    }
}

Now, if you try to run mvn test -Dtest=PersonDaoTest, it should pass. The @Repository annotation indicates this is a Spring bean. The following XML (in your applicationContext.xml file) scans for classes with these annotations. If you are writing your own Dao and Dao interface, be sure to comment out or remove the reference to the generic Dao in the applicationContext.xml file in src/main/webapp/WEB-INF (or core/src/main/resources for a modular archetype).

Code Block
languagexml

<!-- Activates scanning of @Repository and @Service -->
<context:component-scan base-package="org.appfuse.tutorial"/>

If you don't like annotations, you can also use XML. To do this, you need to configure Spring so it knows that PersonDaoHibernate PersonDaoJpa is the implementation of PersonDao. Open the applicationContext.xml file in src/main/webapp/WEB-INF (or core/src/main/resources for a modular archetype) and add the following XML to it:

Code Block
languagexml

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

    <bean id="personDao" class="org.appfuse.tutorial.dao.jpa.PersonDaoJpa"/>
</beans>

...

Save all your edited files and try running mvn test -Dtest=PersonDaoTest one more time.

Yeah Baby, Yeah:

Wiki Markup
{span:style=color: green}BUILD SUCCESSFUL\\
Total time: 11 seconds{span}

 

Next Up: Part II: Creating new Managers - A HowTo for creating Business Facades, which are similar to Session Facades, but don't use EJBs. These facades are used to provide communication from the front-end to the DAO layer.