Appfuse开发实践(二)——创建DAO对象 转自[临海观潮]的blog

Appfuse开发实践(二)——创建DAO对象


      第一部分: 在Appfuse中创建新的DAO对象 - 本部分描述如何创建一个POJO并且创建一个Java类把这个对象存储到数据库中实现持久化。 

      我们将使用Hibernate作为示例,如果需要使用其他的持久化方案,可以参考Appfuse的相关文档。
 
内容提要


      第一部分: 在Appfuse中创建新的DAO对象 - 本部分描述如何创建一个POJO并且创建一个Java类把这个对象存储到数据库中实现持久化。 

      我们将使用Hibernate作为示例,如果需要使用其他的持久化方案,可以参考Appfuse的相关文档。
 
内容提要


[1] 创建一个新的POJO对象并且加入xdoclet标签
[2] Create a new database table from the object using Ant
[3] Create a new DAOTest to run JUnit tests on the DAO
[4] Create a new DAO to perform CRUD on the object
[5] Configure Spring for the Person object and PersonDAO
[6] Run the DAOTest


1、创建一个新的POJO对象并且加入xdoclet标签


     首先要创建一个需要持久化的对象。我们将创建一个简单的Person对象(包括id,firstName,lastName三个属性)把这个对象放在src/dao/**/model目录中

 

None.gif package org.myApp.model;
None.gif
ExpandedBlockStart.gifContractedBlock.gif
public   class  Person extends BaseObject  dot.gif {
InBlock.gif  
private Long id;
InBlock.gif  
private String firstName;
InBlock.gif  
private String lastName;
InBlock.gif   
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//*产生三个属性对应的setter/getter方法
ExpandedSubBlockEnd.gif     产   
*/

ExpandedBlockEnd.gif}
  
None.gif
None.gif

      这个类必须继承基类org.appfuse.model.BaseObject,必须实现三个抽象方法equals(), hashCode() and toString().前两个方法是Hibernate需要的.比较容易的方法是使用Commonclipse 插件自动产生着几个方法的实现.


      使用IntelliJ IDEA可以产生generate equals() 和 hashCode()方法, 不过没有toString()方法. 可以使用 ToStringPlugin 插件。

现在创建了POJO对象,接下来需要加入XDoclet标签以便产生Hibernate映射文件。这个映射文件描述了对象→表和属性 →字段的映射关系。

用@hibernate.class标签说明表和对象的对应关系:


 

ExpandedBlockStart.gif ContractedBlock.gif /**/ /**
InBlock.gif * @hibernate.class table="person"
ExpandedBlockEnd.gif 
*/

ExpandedBlockStart.gifContractedBlock.gif
public   class  Person extends BaseObject  dot.gif {  
InBlock.gif
InBlock.gifWe also have to add a primary key mapping or XDoclet will puke when generating the mapping file. Note that all @hibernate.
* tags should be placed in the getters' Javadocs of your POJOs. 
InBlock.gif

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//**
InBlock.gif     * @return Returns the id.
InBlock.gif     * @hibernate.id column="id"
InBlock.gif     *  generator-class="increment" unsaved-value="null"
ExpandedSubBlockEnd.gif     
*/

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
public Long getId() dot.gif{
InBlock.gif        
return this.id;
ExpandedSubBlockEnd.gif    }
  
InBlock.gif
InBlock.gif

 

 

      这里使用generator-class="increment" 代替 generate-class="native" 因为作者在使用某些数据库时发现了一些问题。如果仅仅使用MySQL,建议使用"native" value。


2、使用Ant创建数据库表

      这是,可以运行"ant setup-db"任务来创建Person表,这个任务将创建Person.hbm.xml 文件并且在数据库中创建"person."表。 在控制台上,你可以看见Hibernate创建的table schema:
[schemaexport] create table person (
[schemaexport]    id bigint not null,
[schemaexport]    primary key (id)
[schemaexport] );

    如果你Hibernate产生的Person.hbm.xml的文件,可以在build/dao/gen/**/hibernate 目录下找到它,这是这个文件的具体内容 :


 

None.gif <? xml version="1.0" ?>
None.gif
None.gif
<! DOCTYPE hibernate-mapping PUBLIC
None.gif    "-//Hibernate/Hibernate Mapping DTD 2.0//EN" 
None.gif    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"
>
None.gif
None.gif
< hibernate-mapping >
None.gif    
< class
None.gif        
name ="org.myApp.model.Person"
None.gif        table
="person"
None.gif        dynamic-update
="false"
None.gif        dynamic-insert
="false"
None.gif    
>
None.gif
None.gif        
< id
None.gif            
name ="id"
None.gif            column
="id"
None.gif            type
="java.lang.Long"
None.gif            unsaved-value
="null"
None.gif        
>
None.gif            
< generator  class ="increment" >
None.gif            
</ generator >
None.gif        
</ id >
None.gif
None.gif        
<!--
None.gif            To add non XDoclet property mappings, create a file named
None.gif                hibernate-properties-Person.xml
None.gif            containing the additional properties and place it in your merge dir.
None.gif        
-->
None.gif
None.gif    
</ class >
None.gif
None.gif
</ hibernate-mapping >   
None.gif

 

     现在加入@hibernate.property描述我们的其他字段信息:


    

ExpandedBlockStart.gif ContractedBlock.gif /**/ /**
InBlock.gif     * @hibernate.property column="first_name" length="50"
ExpandedBlockEnd.gif     
*/

ExpandedBlockStart.gifContractedBlock.gif    
public  String getFirstName()  dot.gif {
InBlock.gif        
return this.firstName;
ExpandedBlockEnd.gif    }

None.gif
ExpandedBlockStart.gifContractedBlock.gif    
/**/ /**
InBlock.gif     * @hibernate.property column="last_name" length="50"
ExpandedBlockEnd.gif     
*/

ExpandedBlockStart.gifContractedBlock.gif    
public  String getLastName()  dot.gif {
InBlock.gif        
return this.lastName;
ExpandedBlockEnd.gif    }
  
None.gif
None.gif

      在这个例子中,加入column属性是因为创建的数据表的字段名和对应的类中的属性名不一样,如果是一样的,不需要加入这个属性说明。

运行 "ant setup-db"创建数据表.

[schemaexport] create table person (
[schemaexport]    id bigint not null,
[schemaexport]    first_name varchar(50),
[schemaexport]    last_name varchar(50),
[schemaexport]    primary key (id)
[schemaexport] );

       如果需要改变字段的长度,修改@hibernate.property 标签对应的属性值。如果希望他是一个必须的字段(NOT NULL),,加上not-null="true"这样的属性。


3、创建一个DAOTest对象并且用JUnit测试你的DAO对象


      现在我们创建一个DAOTest类来测试我们的DAO的工作状况。 “等一下”你说,“我们还没有创建一个DAO对象”你是对的。然而我们发现测试驱动开发(Test-Driven Development)是开发高质量软件的有效手段。在过去的几年里,我认为先编写测试再编写实现是无稽之谈。现在看起来我错了。现在我发现这是一个很伟大的方法。我现在推崇测试驱动的开发方式是因为我发现使用这种方法可以极大地加速软件开发过程。

      首先,在test/dao/**/dao目录下创建一个PersonDAOTest.java对象。这个对象必须继承 BaseDAOTestCase对象。这个父对象用来加载一个Spring的.properties文件 (资源邦定文件),此文件的文件名
和*Test.class一样。在这个例子里面如果你把PersonDAOTest.properties、PersonDAOTest.java放在一个目录下面, 这个文件的属性可以自动被赋值。

 

None.gif package org.myApp.dao;
None.gif
None.gifimport org.myApp.model.Person;
None.gifimport org.springframework.dao.DataAccessException;
None.gif
ExpandedBlockStart.gifContractedBlock.gif
public   class  PersonDAOTest extends BaseDAOTestCase  dot.gif {
InBlock.gif    
InBlock.gif    
private Person person = null;
InBlock.gif    
private PersonDAO dao = null;
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
protected void setUp() throws Exception dot.gif{
InBlock.gif        super.setUp();
InBlock.gif        dao 
= (PersonDAO) ctx.getBean("personDAO");
ExpandedSubBlockEnd.gif    }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
protected void tearDown() throws Exception dot.gif{
InBlock.gif        super.tearDown();
InBlock.gif        dao 
= null;
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}
  
None.gif
None.gif

 

        现在我们需要编写代码测试DAO对象中的CRUD (create, retrieve, update, delete)方法。我们创建的这个方法必须以"test" (all 全部小写字母)开头。只要这些方法是public类型的,它必须是一个void类型返回值,并且不带任何参数,这些测试方法将会被定义在Ant build.xml的<junit>任务自动调用。这是一个对CRUD方法的简单测试方法。一个重要的需要记住的事情是每个方法应该是独立的。在这个例子里我们在PersonDAOTest.java文件中加入下面的代码:


    

ExpandedBlockStart.gif ContractedBlock.gif public   void  testGetPerson() throws Exception  dot.gif {
InBlock.gif        person 
= new Person();
InBlock.gif        person.setFirstName(
"Matt");
InBlock.gif        person.setLastName(
"Raible");
InBlock.gif
InBlock.gif        dao.savePerson(person);
InBlock.gif        assertNotNull(person.getId());
InBlock.gif
InBlock.gif        person 
= dao.getPerson(person.getId());
InBlock.gif        assertEquals(person.getFirstName(), 
"Matt");
ExpandedBlockEnd.gif    }

None.gif
ExpandedBlockStart.gifContractedBlock.gif    
public   void  testSavePerson() throws Exception  dot.gif {
InBlock.gif        person 
= dao.getPerson(new Long(1));
InBlock.gif        person.setFirstName(
"Matt");
InBlock.gif
InBlock.gif        person.setLastName(
"Last Name Updated");
InBlock.gif
InBlock.gif        dao.savePerson(person);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
if (log.isDebugEnabled()) dot.gif{
InBlock.gif            log.debug(
"updated Person: " + person);
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        assertEquals(person.getLastName(), 
"Last Name Updated");
ExpandedBlockEnd.gif    }

None.gif
ExpandedBlockStart.gifContractedBlock.gif    
public   void  testAddAndRemovePerson() throws Exception  dot.gif {
InBlock.gif        person 
= new Person();
InBlock.gif        person.setFirstName(
"Bill");
InBlock.gif        person.setLastName(
"Joy");
InBlock.gif
InBlock.gif        dao.savePerson(person);
InBlock.gif
InBlock.gif        assertEquals(person.getFirstName(), 
"Bill");
InBlock.gif        assertNotNull(person.getId());
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
if (log.isDebugEnabled()) dot.gif{
InBlock.gif            log.debug(
"removing persondot.gif");
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        dao.removePerson(person.getId());
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
try dot.gif{
InBlock.gif            person 
= dao.getPerson(person.getId());
InBlock.gif            fail(
"Person found in database");
ExpandedSubBlockStart.gifContractedSubBlock.gif        }
 catch (DataAccessException dae) dot.gif{
InBlock.gif            log.debug(
"Expected exception: " + dae.getMessage());
InBlock.gif            assertNotNull(dae);
ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }
  
None.gif
None.gif

 

    在testGetPerson方法中,我们创建了一个Person对象并且调用了get方法。实际上我们这样做必须保证在数据库中有一条对应的纪录。因为采用了DBUni可以以我们需要的测试数据自动填充数据库,你只需要在metadata/sql/sample-data.xml文件中简单的说明需要增加的纪录详细信息即可:

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


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

     
      此时,PersonDAOTest类无法通过编译,因为我们没有创建 PersonDAO.class,我们要创建它。 PersonDAO.java是一个接口,PersonDAOHibernate.java是一个基于这个接口的Hibernate持久化实现。下面我们将继续创建这些对象。
4、创建一个DAO对象执行CRUD操作

      首先在src/dao/**/dao目录中创建一个PersonDAO.java接口,并且说明实现类必须实现的CRUD方法。为了显示方便这里去掉了JavaDoc信息。


package org.myApp.dao;

import org.myApp.model.Person;

public interface PersonDAO extends DAO {
    public Person getPerson(Long personId);
    public void savePerson(Person person);
    public void removePerson(Long personId);
}  

      你可能注意到上面的类的方法说明上没有exception处理。这是因为Spring的强大例外处理功能能把Exception封装成为RuntimeExceptions。此时,您可以运行"ant compile-dao"来编译源文件。然而如果你运行"ant test-dao -Dtestcase=PersonDAO",你将看到一个错误提示:No bean named 'personDAO' is defined。这是一个Spring产生的错误信息 - 我们需要给applicationContext-hibernate.xml指定一个具体的java Bean 到personDAO属性上。在此之前我们首先要创建一个PersonDAO的实现类。


      执行测试任务的ant命令是"test-dao"。如果传递特定的参数(用 -Dtestcase=name), 他将会搜索 **/*${testcase}*目录 - 允许我们执行PersonDAOTest测试。

接下来我们创建PersonDAOHibernate类实现PersonDAO接口使用Hibernate来实现Person对象的get/save/delete操作。为了实现这个目标在src/dao/**/dao/hibernate 目录下创建PersonDAOHibernate.java.
他需要继承 BaseDAOHibernate 对象并且实现PersonDAO接口。


 

None.gif package org.myApp.dao.hibernate;
None.gif
None.gifimport org.myApp.model.Person;
None.gifimport org.myApp.dao.PersonDAO;
None.gifimport org.springframework.orm.ObjectRetrievalFailureException;
None.gif
ExpandedBlockStart.gifContractedBlock.gif
public   class  PersonDAOHibernate extends BaseDAOHibernate implements PersonDAO  dot.gif {
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
public Person getPerson(Long id) dot.gif{
InBlock.gif        Person person 
= (Person) getHibernateTemplate().get(Person.class, id);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
if (person == nulldot.gif{
InBlock.gif            
throw new ObjectRetrievalFailureException(Person.class, id);   
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
return person;
ExpandedSubBlockEnd.gif    }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
public void savePerson(Person person) dot.gif{
InBlock.gif        getHibernateTemplate().saveOrUpdate(person);
ExpandedSubBlockEnd.gif    }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
public void removePerson(Long id) dot.gif{
InBlock.gif        
// object must be loaded before it can be deleted
InBlock.gif
        getHibernateTemplate().delete(getPerson(id));
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}
  
None.gif
None.gif

      现在如果运行"ant test-dao -Dtestcase=PersonDAO",会看到同样的错误。我们需要配置Spring让它知道PersonDAOHibernate是PersonDAO的实现,我们也需要告诉它Person对象的信息。


5、在Spring配置Person和PersonDAO对象


       首先要告诉Spring Hibernate mapping映射文件的位置。打开src/dao/**/dao/hibernate/applicationContext-hibernate.xml并且加入Person.hbm.xml信息。


 

None.gif < property  name ="mappingResources" >  
None.gif    
< list >  
None.gif        
< value > org/myApp/model/Person.hbm.xml </ value >  
None.gif        
< value > org/myApp/model/Role.hbm.xml </ value >  
None.gif        
< value > org/myApp/model/User.hbm.xml </ value >  
None.gif        
< value > org/myApp/model/UserCookie.hbm.xml </ value >   
None.gif    
</ list >  
None.gif
</ property >    

 

     现在我们需要增加一些XML标记绑定PersonDAOHibernate到PersonDAO。在文件的结尾处加入下面的内容:


 

None.gif <!--  PersonDAO: Hibernate implementation  -->  
None.gif
< bean  id ="personDAO"  class ="org.myApp.dao.hibernate.PersonDAOHibernate" >  
None.gif    
< property  name ="sessionFactory" >< ref  local ="sessionFactory" /></ property >  
None.gif
</ bean >    

 


     你也可以使用autowire="byName" 这样不用对"sessionFactory"属性赋值。个人习惯,我喜欢在xml文档说明对应关系。


6、执行DAOTest

     保存文件再一次运行 "ant test-dao -Dtestcase=PersonDAO"。

OK,成功了^_^

BUILD SUCCESSFUL
Total time: 9 seconds

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值