【转】 Spring对DAO的支持

来源:互联网 发布:mac开机出现五国 编辑:程序博客网 时间:2024/05/17 23:05

 

#spring对DAO的支持
#降低耦合度,倾向于面向接口编程,简化编程{愈来愈简化}
我们从以下3部分来逐步了解spring对DAO的支持:1、传统模式的DAO,2、spring对JDBC的DAO的支持,3、spring对Hibernate的DAO的支持。

#实现传统DAO模式必须实现以下几个组件:DAO工厂、DAO接口、DAO实现类和数据传递对象(也称值对象,通常使用JavaBean封装)

#示例的实现图

#实现代码

/*实体PersonBean*/
public class PersonBean
{
private int id;
private String name;
private int age;

public PersonBean(){
}
public PersonBean(String name , int age) {
   this.name = name;
   this.age = age;
}
public void setName(String name) {
   this.name = name;
}

public void setAge(int age) {
   this.age = age;
}

public int getId() {
   return (this.id);
}

public String getName() {
   return (this.name);
}

public int getAge() {
   return (this.age);
}

}

/*dao接口PersonDao*/
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.ArrayList;

public interface PersonDao
{
void createPerson(PersonBean p)throws Exception;
PersonBean getPerson(int id) throws Exception;
List findPersonsByName(String name) throws Exception;
void deletePerson(int id) throws Exception;
void deletePersonsByAge(int startAge , int EndAge) throws Exception;
void updatePerson(PersonBean pb) throws Exception;
}

/*dao接口实现PersonDaoImpl*/
import java.sql.Statement;
import java.sql.ResultSet;
import java.util.List;
import java.util.ArrayList;

public class PersonDaoImpl implements PersonDao
{
private Statement stmt;
private DBConn dc;
public PersonDaoImpl()
{
   dc = DBConn.instance();
}
public void createPerson(PersonBean p)throws Exception
{
   stmt = dc.openStmt();
   stmt.execute("insert into person_test(p_name,p_age) values('" + p.getName() + "'," + p.getAge()+")");
}
public PersonBean getPerson(int id) throws Exception
{
   stmt = dc.openStmt();
   ResultSet rs = stmt.executeQuery("select * from person_test where p_id = " + id);
   return new PersonBean(rs.getString("p_name"),rs.getInt("p_age"));
}
public List findPersonsByName(String name) throws Exception
{
   stmt = dc.openStmt();
   String sql = "select * from person_test where p_name like '%" + name + "%'";
   ResultSet rs = stmt.executeQuery(sql);
   List result = new ArrayList();
   while (rs.next())
   {
    result.add(new PersonBean(rs.getString("p_name"),rs.getInt("p_age")));
   }
   return result;
}
public void deletePerson(int id) throws Exception
{
   stmt = dc.openStmt();
   stmt.execute("delete from person_test where p_id = " + id);
}
public void deletePersonsByAge(int startAge , int EndAge) throws Exception
{
   stmt = dc.openStmt();
   stmt.execute("delete from person_test where p_age between " + startAge + " and " + EndAge);
}
public void updatePerson(PersonBean pb)throws Exception
{
   stmt = dc.openStmt();
   stmt.execute("update person_test set p_name = '" + pb.getName() + "' , p_age=" + pb.getAge() + " where p_id =" + pb.getId());
}
}

/*辅助类DBConn*/
import java.sql.Connection;
import java.sql.Statement;
import java.sql.DriverManager;

public class DBConn
{
private static DBConn dc;
private Connection conn = null;
private Statement stmt = null;
private DBConn()
{
}
public static DBConn instance()
{
   if (dc == null)
   {
    dc = new DBConn();
   }
   return dc;
}

public Statement openStmt()
{
   if (stmt == null)
   {
    conn = getConn();
    try
    {
     stmt = conn.createStatement();
    }
    catch (Exception e)
    {
     System.err.println("创建Statement异常: " + e.getMessage());
    }
   }
   return stmt;
}
public void closeStmt()
{
   if (stmt != null)
   {
    try
    {
     stmt.close();
    }
    catch (Exception e)
    {
     System.err.println("Statement关闭异常");
    }
   }
   if (conn != null)
   {
    try
    {
     conn.close();
    }
    catch (Exception e)
    {
     System.err.println("数据库关闭异常");
    }
   }
}

private Connection getConn()
{
   if (conn == null)
   {
    try
    {
     Class.forName("com.mysql.jdbc.Driver");
     conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/j2ee","root","32147");
    }
    catch (Exception e)
    {
     e.printStackTrace();
    }
   }
   return conn;
}
}

/*dao工厂DaoFactory*/

public class DaoFactory
{
private static DaoFactory df;
private DaoFactory()
{
}
public static DaoFactory instance()
{
   if (df == null)
   {
    df = new DaoFactory();
   }
   return df;
}

public Object getDao(String className)
{
   try
   {
    Class daoClazz = this.getClass().getClassLoader().loadClass(className);
    return daoClazz.newInstance();
   }
   catch (Exception e)
   {
    System.err.println("创建Dao实例异常");
   }
   return null;
}
}

/*
*测试程序
*/
public class Test
{
    public static void main(String[] args) throws Exception
    {
   DaoFactory df = DaoFactory.instance();
        PersonDao pd = (PersonDao)df.getDao("lee.PersonDaoImpl");
   pd.createPerson(new PersonBean("ddd",34));
   System.out.println(pd.findPersonsByName("ee"));
    }
}

#2、Spring对JDBC的DAO的支持
Spring中DAO体系:1、允许用相同的方式、不同的数据访问技术,如jdbc、hibernate或者jdo;2、在不同的持久层访问技术上提供抽象,应用的持久层访问基于Spring的dao,故,应用程序可以在不同的持久层技术之间切换;
      3、Spring提供一致的异常抽象,并将原有的checked异常转换包装成runtime异常,因而,编码时无须捕获各种技术中特定的异常。
#2.1 统一的异常体系
Spring提供统一的异常继承体系,所有持久层访问的异常代码都继承DataAccessException--它是运行时异常,无须显式捕获,只需要捕获Spring异常。
Spring的异常是不可恢复的,Spring异常体系对所有的数据库访问技术是一致的,因此,可以在一致的编程模型下使用不同的数据库访问技术。
对于采用Template的ORM数据库访问技术,访问步骤更加简单。例如,使用Hibernate,甚至无须获得Hibernate Session对象,HibernateTemplate自动获取该对象。
如果使用基于拦截器的类,应用程序必须谨慎处理HibernateException和JDOException,最后使用SessionFactoryUtils中的converHibernateAccessException和converJdoAccessException方法代理。这些方法可以把异常包装成Spring兼容的异常。因为,JdoException属于unchecked异常,它们则被简单抛出,尽管这将牺牲通用的DAO抽象。
#2.2 统一的DAO抽象
Spring针对不同的技术,提供对应的DAO支持类-都是抽象的,为的是简化DAO的开发步骤,不管底层采用何种技术,应用中都可采用一致的编程模型。
这些抽象类提供一些方法来设置数据源,以及所使用的访问技术中的配置信息。应用的DAO类继承这些抽象类,能大大简化应用的开发。最大的好处是,继承这些抽象的DAO以一致方式访问数据库,意味着应用可方便地在不同的持久层访问技术中切换。
#2.3 常见的DAO支持类
Spring可以使用相同的访问方式,不同的数据库访问技术,提供多种数据库访问技术DAO的支持,这种支持主要是通过以下三个组件完成:
》DaoSupport   》Template   》Callback
三个组件有机地结合在一起,能极大地减少数据库访问的代码量,提高开发效率。
Spring所支持的数据库访问技术:
》JDBC:主要由JdbcDaoSupport、JdbcTemplate和StatementCallback组成;
》Hibernate:主要由HibernateDaoSupport、HibernateTemplate和HibernateCallback组成;
》JDO:主要由JdoDaoSupport、JdoTemplate和JdoCallback组成;
》iBATIS:主要由SqlMapClientDaoSpport、SqlMapClientTemplate和SqlMapClientCallback组成;
》TopLink、OJB、Jpa等
#2.4 Spring对JDBC的DAO的支持
Spring提供JdbcDaoSupport简化Jdbc Dao的开发,JdbcDaoSupport类有如下两个方法:
》void setDataSource(DataSource dataSource):依赖注入所需的setter方法
》getJdbcTemplate():获得JdbcTemplate对象
通过这种JdbcDaoSupport的支持,DAO对象无须手动注册驱动,获取连接。将DAO对象配置在Spring容器中,并为其注入DataSource对象,DAO对象就可获取JdbcTemplate对象
JdbcTemplate提供的主要的CRUD方法:
》void execute(String sql):主要用于执行DDL语句
》List query(String sql,Object[]args,RowMapper rowMapper):执行SQL查询,并将每条记录映射成bean实例,返回bean实例集合
》List queryForList(String sql,Object[] args):执行SQL查询,将ResultSet的每条记录包装成List对象,返回这些List组成的集合,结果为List的元素仍是List
》Object queryForObject(String sql,RowMapper rowMapper):执行SQL查询,将查询的ResultSet包装成对象后返回
》int update(String sql):执行SQL更新
》int update(String sql,Object[] args):执行带参数的SQL更新

/*
*实体bean
*/
public class PersonBean
{
private int id;
private String name;
private int age;

public PersonBean(){
}
public PersonBean(String name , int age) {
   this.name = name;
   this.age = age;
}
public void setName(String name) {
   this.name = name;
}

public void setAge(int age) {
   this.age = age;
}

public int getId() {
   return (this.id);
}

public String getName() {
   return (this.name);
}

public int getAge() {
   return (this.age);
}

}
/*
*DAO接口
*/
public interface PersonDao
{
void createPerson(PersonBean p);
PersonBean getPerson(int id);
List findPersonsByName(String name);
void deletePerson(int id);
void deletePersonsByAge(int startAge , int EndAge);
void updatePerson(PersonBean pb);
}
/*
*编写DAO实现类
*/
public class PersonDaoJdbc extends JdbcDaoSupport implements PersonDao
{

public void createPerson(PersonBean p)
{
   Object[] args = {p.getName() , new Integer(p.getAge()) };
   getJdbcTemplate().update("insert into person_test(p_name,p_age) values(?,?)", args );
}
public PersonBean getPerson(int id)
{
   Object[] args = {new Integer(id)};
   return (PersonBean)getJdbcTemplate().queryForObject("select p_name,p_age from person_test where p_id = ?", args, new PersonRowMapper());
}
public List findPersonsByName(String name)
{
   return getJdbcTemplate().query("select * from person_test where p_name like '%" + name +"%'" , new PersonRowMapper());
}
public void deletePerson(int id)
{
   Object[] args = {new Integer(id)};
   getJdbcTemplate().update("delete from person_test where p_id = ", args);
}
public void deletePersonsByAge(int startAge , int EndAge)
{
   Object[] args = {new Integer(startAge),new Integer(EndAge)};
   getJdbcTemplate().update("delete from person_test where p_age between ? and ? " ,args);
}
public void updatePerson(PersonBean pb)
{
   Object[] args = {pb.getName(),new Integer(pb.getAge()),new Integer(pb.getId())};
   getJdbcTemplate().update("update person_test set p_name = ?, p_age=? where p_id =? " ,args);
}

private class PersonRowMapper implements RowMapper
{
   public Object mapRow(ResultSet rs, int rowNumber) throws SQLException
   {
    PersonBean pb = new PersonBean(rs.getString("p_name"),rs.getInt("p_age"));
    return pb;
   }
}
}
/*
*配置文件bean.xml--继承JdbcDaoSupport的DAO对象,需要DataSource的支持,因此,应该使用Spring容器管理该对象,容器为其注入DataSource对象,
*并管理其生命周期。Spring容器提供工厂功能,因此,不在需要DAO工厂
*/
<?xml version="1.0" encoding="gb2312"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
   <property name="driverClassName">
    <value>com.mysql.jdbc.Driver</value>
   </property>
   <property name="url">
    <value>jdbc:mysql://localhost:3306/j2ee</value>
   </property>
   <property name="username">
    <value>root</value>
   </property>
   <property name="password">
    <value>1234</value>
   </property>
</bean>

<bean id="personDao" class="PersonDaoJdbc">
   <property name="dataSource">
    <ref local="dataSource"/>
   </property>
</bean>

</beans>

/*
*测试程序
*/
public class Test
{
    public static void main(String[] args) throws Exception
    {
   ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
   PersonDao pd = (PersonDao)ctx.getBean("personDao");
   pd.createPerson(new PersonBean("wee",45));
   System.out.println(pd.getPerson(2));
   System.out.println(pd.findPersonsByName("ee"));
    }
}

#3、Spring对Hibernate的DAO支持
Spring提供的HibernateDaoSupport可简化Hibernate DAO的开发,HibernateDaoSupport类包含如下两个方法:
》setSessionFactory(org.hibernate.SessionFactory sessionFactory):依赖注入所需的setter方法
》HibernateTemplate getHibernateTemplate():获得HibernateTemplate对象
Hibernate的SessionFactory被当作普通的bean可被Spring管理。基于Hibernate的DAO对象需要SessionFactory实例,Spring负责为其注入该实例。
通过HibernateDaoSupport的支持,DAO对象无须手动创建、访问SessionFactory对象。使用Hibernate的getHibernateTemplate()方法可以返回HibernateTemplate对象,该对象提供的CRUD主要方法:
》List find(String querySQL,Object value):执行HQL查询,该语句可使用?作为参数占位符,value用于确定参数值
》List find(String querySQL,Object[] value):同上,只是可以带多个参数
》Object load(Class entityClass,Serializable id):根据主键加载实体
》Serializable save(Object entity):保存瞬间对象
》void saveOrUpdate(Object entity):根据给定对象,保存或修改实体。若该实体是瞬态对象,则保存;若是持久化对象,则更新
》void update(Object entity):根据给定持久化对象,更新记录


/*
*DAO接口{同上,略}
*/

/*
*DAO实现
*编写基于Hibernate的DAO类,推荐继承HibernateDaoSupport。DAO类需要SessionFactory引用,由Spring容器负责注入。Spring容器负责生成并管理DAO实例,担任DAO工厂。
*/
public class PersonDaoHibernate extends HibernateDaoSupport implements PersonDao
{
   public void createPerson(PersonBean p)
   {
    getHibernateTemplate().save(p);
   }
   public PersonBean getPerson(int id)
   {
    return (PersonBean)getHibernateTemplate().get("lee.PersonBean", new Integer(id));
   }
   public List findPersonsByName(String name)
   {
    return getHibernateTemplate().find("from lee.PersonBean p where p.name like ?","%" + name + "%");
   }
   public void deletePerson(int id)
   {
    Object o = getHibernateTemplate().get("lee.PersonBean", new Integer(id));
    getHibernateTemplate().delete(o);
   }
   public void deletePersonsByAge(final int startAge ,final int endAge)
   {
    getHibernateTemplate().execute(new HibernateCallback()
    {
     public Object doInHibernate(org.hibernate.Session session)
     {
      String hqlDelete = "delete lee.PersonBean where age between :startAge and :endAge";
      session.createQuery( hqlDelete )
                             .setInteger( "startAge", startAge )
                             .setInteger( "endAge", endAge )
                             .executeUpdate();
      return null;
     }
    });   
   
   }
   public void updatePerson(PersonBean pb)
   {
    getHibernateTemplate().saveOrUpdate(pb);
   }
}

/*
*实体PersonBean: 因为使用了Hibernate的支持,因此,需要增加持久化类。对原来的PersonBean作简单修改,为其增加id属性的setter方法
*/
public class PersonBean
{
   private int id;
   private String name;
   private int age;

   public PersonBean(){
   }
   public PersonBean(String name , int age) {
    this.name = name;
    this.age = age;
   }

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

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

   public void setAge(int age) {
    this.age = age;
   }

   public int getId() {
    return (this.id);
   }

   public String getName() {
    return (this.name);
   }

   public int getAge() {
    return (this.age);
   }

}

/*
*PersonBean.hbm.xml:要使该POJO能完成持久化操作,还应增加映射文件
*/
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
     PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

     <class name="lee.PersonBean" table="person_test">
         <id name="id" column="p_id">
             <generator class="increment"/>
         </id>
         <property name="name" column="p_name"/>
         <property name="age" column="p_age"/>
     </class>

</hibernate-mapping>

/*
*bean.xml:Spring 容器负责管理DAO实例的创建,并管理DAO实例的依赖关系,Spring容器是DAO工厂
*/
<?xml version="1.0" encoding="gb2312"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
   "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

   <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName">
     <value>com.mysql.jdbc.Driver</value>
    </property>
    <property name="url">
     <value>jdbc:mysql://localhost:3306/j2ee</value>
    </property>
    <property name="username">
     <value>root</value>
    </property>
    <property name="password">
     <value>32147</value>
    </property>
   </bean>
     <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
         <property name="dataSource"><ref local="dataSource"/></property>
         <property name="mappingResources">
             <list>
                 <!--以下用来列出所有的PO映射文件-->
      <value>PersonBean.hbm.xml</value>
             </list>
         </property>
         <property name="hibernateProperties">
         <props>
             <!--此处用来定义hibernate的SessionFactory的属性:不同数据库连接,启动时选择create,update,create-drop-->
     <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
             <prop key="hibernate.hbm2ddl.auto">update</prop>
         </props>
         </property>
     </bean>

   <bean id="personDao" class="lee.PersonDaoHibernate">
         <property name="sessionFactory">
     <ref local="sessionFactory"/>
    </property>
   </bean>

</beans>

/*
   *测试类
   */
public class Test
{
     public static void main(String[] args) throws Exception
     {
    ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
    PersonDao pd = (PersonDao)ctx.getBean("personDao");
    pd.createPerson(new PersonBean("wee",45));
    System.out.println(pd.getPerson(2));
    System.out.println(pd.findPersonsByName("ee"));
    pd.deletePersonsByAge(30,40);
     }
}

:Spring结合Hibernate,使数据库访问变得更加简单,借助于HibernateTemplate,大部分的持久化操作仅需一两行代码就可搞定。
:Spring中的Hibernate没有事务控制语句,事务控制逻辑由Spring接管。

#DAO模式的异常处理
DAO对象的异常处理也是实现DAO模式需要考虑的一个重要问题。
DAO执行的是数据库访问操作,可能抛出底层的SQLException,而底层异常通常不应该暴露给客户端,DAO应负责捕获这些异常而不应该把其扩散到业务逻辑层,让业务逻辑层处理数据库异常。
关于DAO对象异常处理,通常有如下推荐:
DAO方法
   》应该抛出有意义的业务逻辑异常
   》不应该抛出java.lang.Exception异常{该异常范围太大,无实际意义对业务逻辑层}
   》捕获底层数据库访问异常,抛出封装后的异常。不应该抛出数据库访问异常,如SQLException。这些属于底层异常,不应该扩散到业务逻辑层,DAO应该封装这些异常,然后抛出封装后的异常
   》DAO接口中的方法,只抛出业务层期望处理的checked异常。若业务逻辑层没有合适的异常处理,考虑抛出运行时异常
   》为DAO类编写异常类,用于包装数据库访问异常
/*
*编写DAO异常类
*/
public class DbAccessException extends Exception
{
   public DbAccessException()
   {
   }
   public DbAccessException(String msg)
   {
    super(msg);
   }
}

/*
   *改写DAO接口
*/
public interface PersonDao
{
   void createPerson(PersonBean p)throws DbAccessException;
   PersonBean getPerson(int id) throws DbAccessException;
   List findPersonsByName(String name) throws DbAccessException;
   void deletePerson(int id) throws DbAccessException;
   void deletePersonsByAge(int startAge , int EndAge) throws DbAccessException;
   void updatePerson(PersonBean pb) throws DbAccessException;
}

/*
   *改写DAO接口实现类
*/
public class PersonDaoImpl implements PersonDao
{
private Statement stmt;
private DBConn dc;
    private Log log = LogFactory.getLog(this.getClass());
public PersonDaoImpl()
{
   dc = DBConn.instance();
}
public void createPerson(PersonBean p)throws DbAccessException
{
   try
   {
    stmt = dc.openStmt();
    stmt.execute("insert into person_test(p_name,p_age) values('" + p.getName() + "'," + p.getAge()+")");
   }
   catch (SQLException sqle)
   {
    log.info(sqle.getMessage());
    throw new DbAccessException("数据库访问异常");
   }

}
public PersonBean getPerson(int id) throws DbAccessException
{
   try
   {
    stmt = dc.openStmt();
    ResultSet rs = stmt.executeQuery("select * from person_test where p_id = " + id);
    return new PersonBean(rs.getString("p_name"),rs.getInt("p_age"));
   }
   catch (SQLException sqle)
   {
    log.info(sqle.getMessage());
    throw new DbAccessException("数据库访问异常");
   }

}
public List findPersonsByName(String name) throws DbAccessException
{
   ResultSet rs = null;
   List result = new ArrayList();
   try
   {
    stmt = dc.openStmt();
    String sql = "select * from person_test where p_name like '%" + name + "%'";
    rs = stmt.executeQuery(sql);
    while (rs.next())
    {
     result.add(new PersonBean(rs.getString("p_name"),rs.getInt("p_age")));
    }
   }
   catch (SQLException sqle)
   {
    log.info(sqle.getMessage());
    throw new DbAccessException("数据库访问异常");
   }

   return result;
}
public void deletePerson(int id) throws DbAccessException
{
   try
   {
    stmt = dc.openStmt();
    stmt.execute("delete from person_test where p_id = " + id);
   }
   catch (SQLException sqle)
   {
    log.info(sqle.getMessage());
    throw new DbAccessException("数据库访问异常");
   }

}
public void deletePersonsByAge(int startAge , int EndAge) throws DbAccessException
{
   try
   {
    stmt = dc.openStmt();
    stmt.execute("delete from person_test where p_age between " + startAge + " and " + EndAge);
   }
   catch (SQLException sqle)
   {
    log.info(sqle.getMessage());
    throw new DbAccessException("数据库访问异常");
   }

}
public void updatePerson(PersonBean pb)throws DbAccessException
{
   try
   {
    stmt = dc.openStmt();
    stmt.execute("update person_test set p_name = '" + pb.getName() + "' , p_age=" + pb.getAge() + " where p_id =" + pb.getId());
   }
   catch (SQLException sqle)
   {
    log.info(sqle.getMessage());
    throw new DbAccessException("数据库访问异常");
   }
}
}
   :经过上面的包装,底层的SQLException异常不会扩散到业务逻辑层,若不想强制业务逻辑处理DbAccessException,可对DbAccessException再做简单的改写,让其继承RuntimeException。这将等同于Spring在DAO上的异常处理机制,把底层的checked异常包装成Runtime异常,调用者可以选择在合适的层处理异常。

:通过以上三个阶段的编码对比,我们可以看出,使用框架,可以简化编码,提高效率。