appFuse2.x第四篇--Using Hibernate

This tutorial will show you two things:

  1. You don't need to write DAOs if you just need generic CRUD functionality.
  2. How to write DAOs when you need custom functionality.

If you're new to Hibernate, you might want to read the Hibernate Reference Guide before starting this tutorial.

Table of Contents

  1. Register a personDao bean definition
  2. Create a DAO Test to test finder functionality
  3. Create a DAO Interface and implementation
  4. Run the DAO Test

Source Code

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

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

Register a personDao bean definition

AppFuse 2.x doesn't require you to write a DAO to persist a POJO. You can use one of the pre-existing classes if all you need is CRUD on an object:

  • GenericDaoHibernate: A generics-based class that requires you to create a Spring bean definition.
  • UniversalDaoHibernate: A class that requires you to cast to the specific object type.

The UniversalDaoHibernate class is already registered as a "dao" bean, so you can easily use it without any additional configuration. However, many developers prefer the generics-based DAO because it provides type safety. To register a personDao bean, create src/main/webapp/WEB-INF/applicationContext.xml (or core/src/main/resources/applicationContext.xml for a modular archetype) and add the following to it:

The applicationContext.xml file should already exist if you're using AppFuse 2.0 M5+.
<script class="javascript" src="/download/resources/confluence.ext.code:code/shCore.js" type="text/javascript"></script><script class="javascript" src="/download/resources/confluence.ext.code:code/shBrushCSharp.js" type="text/javascript"></script><script class="javascript" src="/download/resources/confluence.ext.code:code/shBrushPhp.js" type="text/javascript"></script><script class="javascript" src="/download/resources/confluence.ext.code:code/shBrushJScript.js" type="text/javascript"></script><script class="javascript" src="/download/resources/confluence.ext.code:code/shBrushVb.js" type="text/javascript"></script><script class="javascript" src="/download/resources/confluence.ext.code:code/shBrushSql.js" type="text/javascript"></script><script class="javascript" src="/download/resources/confluence.ext.code:code/shBrushXml.js" type="text/javascript"></script><script class="javascript" src="/download/resources/confluence.ext.code:code/shBrushShell.js" type="text/javascript"></script><script class="javascript" src="/download/resources/confluence.ext.code:code/shBrushDelphi.js" type="text/javascript"></script><script class="javascript" src="/download/resources/confluence.ext.code:code/shBrushPython.js" type="text/javascript"></script><script class="javascript" src="/download/resources/confluence.ext.code:code/shBrushJava.js" type="text/javascript"></script>

<?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-2.0.xsd">  
 
    <bean id="personDao" class="org.appfuse.dao.hibernate.GenericDaoHibernate">  
        <constructor-arg value="org.appfuse.tutorial.model.Person"/>   
        <property name="sessionFactory" ref="sessionFactory"/>  
    </bean>   
</beans> 
<script class="javascript" type="text/javascript"> if(!window.newcodemacro_initialised) { window.newcodemacro_initialised = true; window.oldonloadmethod = window.onload; window.onload = function(){ dp.SyntaxHighlighter.HighlightAll('newcodemacro'); if(window.oldonloadmethod) { window.oldonloadmethod(); } } } </script>

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

public void setPersonDao(GenericDao<Person, Long> personDao) {  
    this.personDao = personDao;  
} 
<script class="javascript" type="text/javascript"> if(!window.newcodemacro_initialised) { window.newcodemacro_initialised = true; window.oldonloadmethod = window.onload; window.onload = function(){ dp.SyntaxHighlighter.HighlightAll('newcodemacro'); if(window.oldonloadmethod) { window.oldonloadmethod(); } } } </script>

If you need more than just CRUD functionality, you'll want to continue reading below. If not, you can continue to Creating new Managers. This is a tutorial 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.

Create a DAO Test to test finder functionality

Now you'll create a DaoTest to test that your DAO works. "Wait a minute," you say, "I haven't created a DAO!" You are correct. However, I've found that Test-Driven Development breeds higher quality software. For years, I thought write your test before your class was hogwash. It just seemed stupid. Then I tried it and I found that it works great. The only reason I do test-driven stuff now is because I've found it rapidly speeds up the process of software development.

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 AbstractTransactionalDataSourceSpringContextTests. 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.

package org.appfuse.tutorial.dao;  
 
import java.util.List;  
 
import org.appfuse.dao.BaseDaoTestCase;  
import org.appfuse.tutorial.model.Person;  
import org.springframework.dao.DataAccessException;  
 
public class PersonDaoTest extends BaseDaoTestCase {  
    private PersonDao personDao = null;  
 
    public void setPersonDao(PersonDao personDao) {  
        this.personDao = personDao;  
    }  
} 
<script class="javascript" type="text/javascript"> if(!window.newcodemacro_initialised) { window.newcodemacro_initialised = true; window.oldonloadmethod = window.onload; window.onload = function(){ dp.SyntaxHighlighter.HighlightAll('newcodemacro'); if(window.oldonloadmethod) { window.oldonloadmethod(); } } } </script>

The code you see above is what you need for a basic Spring integration test that initializes and configures an implementation of PersonDao. Spring will use autowiring by name to call the setPersonDao() method and set the "personDao" bean as a dependency of this class.

Now you need test that the finder method works in your DAO. To do this, create a method that begin with "test" (all lower case). As long as this method is public, has a void return type and take no arguments, it invoked and run by JUnit. Add the following method to your PersonDaoTest.java file:

public void testFindPersonByLastName() throws Exception {  
    List<Person> people = personDao.findByLastName("Raible");  
    assertTrue(people.size() > 0);  
} 
<script class="javascript" type="text/javascript"> if(!window.newcodemacro_initialised) { window.newcodemacro_initialised = true; window.oldonloadmethod = window.onload; window.onload = function(){ dp.SyntaxHighlighter.HighlightAll('newcodemacro'); if(window.oldonloadmethod) { window.oldonloadmethod(); } } } </script>

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).

<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> 
<script class="javascript" type="text/javascript"> if(!window.newcodemacro_initialised) { window.newcodemacro_initialised = true; window.oldonloadmethod = window.onload; window.onload = function(){ dp.SyntaxHighlighter.HighlightAll('newcodemacro'); if(window.oldonloadmethod) { window.oldonloadmethod(); } } } </script>

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

import org.springframework.dao.DataAccessException;  
 
public void testAddAndRemovePerson() throws Exception {  
    Person person = new Person();  
    person.setFirstName("Country");  
    person.setLastName("Bry");  
 
    person = personDao.save(person);  
    flush();  
 
    person = personDao.get(person.getId());  
 
    assertEquals("Country", person.getFirstName());  
    assertNotNull(person.getId());  
 
    log.debug("removing person...");  
 
    personDao.remove(person.getId());  
    flush();  
 
    try {  
        personDao.get(person.getId());  
        fail("Person found in database");  
    } catch (DataAccessException dae) {  
        log.debug("Expected exception: " + dae.getMessage());  
        assertNotNull(dae);  
    }  
} 
<script class="javascript" type="text/javascript"> if(!window.newcodemacro_initialised) { window.newcodemacro_initialised = true; window.oldonloadmethod = window.onload; window.onload = function(){ dp.SyntaxHighlighter.HighlightAll('newcodemacro'); if(window.oldonloadmethod) { window.oldonloadmethod(); } } } </script>

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:

firstName=Matt  
lastName=Raible 
<script class="javascript" type="text/javascript"> if(!window.newcodemacro_initialised) { window.newcodemacro_initialised = true; window.oldonloadmethod = window.onload; window.onload = function(){ dp.SyntaxHighlighter.HighlightAll('newcodemacro'); if(window.oldonloadmethod) { window.oldonloadmethod(); } } } </script>

I tend to just hard-code test values into Java code - but the .properties file is an option that works great for large objects.

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

Person person = new Person();  
person = (Person) populate(person); 
<script class="javascript" type="text/javascript"> if(!window.newcodemacro_initialised) { window.newcodemacro_initialised = true; window.oldonloadmethod = window.onload; window.onload = function(){ dp.SyntaxHighlighter.HighlightAll('newcodemacro'); if(window.oldonloadmethod) { window.oldonloadmethod(); } } } </script>

At this point, the PersonDaoTest class won't compile yet because there is no PersonDao.class in your classpath, you need to create it.

Create a DAO Interface and implementation

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.

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);  
} 
<script class="javascript" type="text/javascript"> if(!window.newcodemacro_initialised) { window.newcodemacro_initialised = true; window.oldonloadmethod = window.onload; window.onload = function(){ dp.SyntaxHighlighter.HighlightAll('newcodemacro'); if(window.oldonloadmethod) { window.oldonloadmethod(); } } } </script>

Notice in the class above there is no exception on the method signature. This is due to the power of Spring and how it wraps Exceptions with RuntimeExceptions. At this point, you should be able to compile all your code using your IDE or mvn test-compile. However, if you try to run mvn test -Dtest=PersonDaoTest, you will get an error:

Running org.appfuse.tutorial.dao.PersonDaoTest  
INFO - AbstractSingleSpringContextTests.loadContextLocations(179) | Loading context for: classpath*:/applicationContext-*.xml  
Tests run: 2, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 0.449 sec <<< FAILURE! 
<script class="javascript" type="text/javascript"> if(!window.newcodemacro_initialised) { window.newcodemacro_initialised = true; window.oldonloadmethod = window.onload; window.onload = function(){ dp.SyntaxHighlighter.HighlightAll('newcodemacro'); if(window.oldonloadmethod) { window.oldonloadmethod(); } } } </script>

Unfortunately, this doesn't tell you much about what went wrong. To find the real problem, you need to open target/surefire-reports/org.appfuse.tutorial.dao.PersonDaoTest.txt (or core/target/surefire-reports/org.appfuse.tutorial.dao.PersonDaoTest.txt for a modular archetype). In this file, the real problem is shown:

-------------------------------------------------------------------------------  
Test set: org.appfuse.tutorial.dao.PersonDaoTest  
-------------------------------------------------------------------------------  
Tests run: 2, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 0.444 sec <<< FAILURE!  
testFindPersonByLastName(org.appfuse.tutorial.dao.PersonDaoTest)  Time elapsed: 0.401 sec  <<< ERROR!  
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean   
with name 'org.appfuse.tutorial.dao.PersonDaoTest': Unsatisfied dependency expressed   
through bean property 'personDao': Set this property value or disable dependency   
checking for this bean. 
<script class="javascript" type="text/javascript"> if(!window.newcodemacro_initialised) { window.newcodemacro_initialised = true; window.oldonloadmethod = window.onload; window.onload = function(){ dp.SyntaxHighlighter.HighlightAll('newcodemacro'); if(window.oldonloadmethod) { window.oldonloadmethod(); } } } </script>

Showing errors in your console
To show testing errors in your console, append -Dsurefire.useFile=false to your mvn test command.

This is an error message from Spring - indicating that you need to specify a bean named "personDao" in a Spring context file. Before you do that, you need to create the PersonDao implementation.

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

package org.appfuse.tutorial.dao.hibernate;  
 
import java.util.List;  
 
import org.appfuse.dao.hibernate.GenericDaoHibernate;  
import org.appfuse.tutorial.model.Person;  
import org.appfuse.tutorial.dao.PersonDao;  
 
public class PersonDaoHibernate extends GenericDaoHibernate<Person, Long> implements PersonDao {  
 
    public PersonDaoHibernate() {  
        super(Person.class);  
    }  
 
    public List<Person> findByLastName(String lastName) {  
        return getHibernateTemplate().find("from Person where lastName=?", lastName);  
    }  
} 
<script class="javascript" type="text/javascript"> if(!window.newcodemacro_initialised) { window.newcodemacro_initialised = true; window.oldonloadmethod = window.onload; window.onload = function(){ dp.SyntaxHighlighter.HighlightAll('newcodemacro'); if(window.oldonloadmethod) { window.oldonloadmethod(); } } } </script>

Now, if you try to run mvn test -Dtest=PersonDaoTest, you will get the same error. You need to configure Spring so it knows that PersonDaoHibernate is the implementation of PersonDao.

Create an 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:

<?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-2.0.xsd">  
 
    <bean id="personDao" class="org.appfuse.tutorial.dao.hibernate.PersonDaoHibernate">   
        <property name="sessionFactory" ref="sessionFactory"/>  
    </bean>   
</beans> 
<script class="javascript" type="text/javascript"> if(!window.newcodemacro_initialised) { window.newcodemacro_initialised = true; window.oldonloadmethod = window.onload; window.onload = function(){ dp.SyntaxHighlighter.HighlightAll('newcodemacro'); if(window.oldonloadmethod) { window.oldonloadmethod(); } } } </script>

Run the DAO Test

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

Yeah Baby, Yeah:

BUILD SUCCESSFUL
Total time: 9 seconds


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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值