第4章 Spring的DAO模块.doc
。第4章 Spring的DAO模块本章学习目的和要求本章重点和难点Spring的DAO模块提供了对JDBC、Hibernate、JDO等DAO层支持。本节先介绍对DAO模块对JDBC技术的支持。DAO模块依赖于commons-pool.jar、commons-collections.jar,Eclipse自带的Spring DAO类库没有这两个类库,需要自己添加。4.1 实例:保存Person实体传统的JDBC编程中,总免不了与Connection、Statement、PreparedStatement、ResultSet、SQLException等打交道,还要注意打开连接后要释放连接等琐碎的问题。Spring框架对JDBC进行了封装,完全抛弃了JDBC API。数据库连接、事务等也交给了Spring打点,开发者只需要使用封装好的JdbcTemplate执行SQL语句,然后得到需要的结果。看一个例子。4.1.1 实体类Person代码本例的POJO实体类为Person类。本例将使用Spring提供的JdbcTemplate将Person持久化到数据库中,或者将Person从数据库中读取出来。Person类的代码为(getter、setter略):Person.javapackage com.helloweenvsfei.spring.dao;import java.util.Date;public class Person/ POJOprivate Integer id;/ IDprivate String name;/ 姓名private String sex;/ 性别private int age;/ 年龄private Date birthday;/ 生日4.1.2 Dao层接口Dao层接口定义了操作Person实体类的方法。接口不会与Spring的Dao模块耦合。代码为:IPersonDao.javapackage com.helloweenvsfei.spring.dao;import java.util.List;public interface IPersonDao/Person的DAO接口public String getPersonName(Integer id);/ 根据id获取Person姓名public void addPerson(Person person);/ 添加Person对象public int getPersonsCount();/ 返回所有Person的数目public List<Person> listPersons();/ 返回所有的Person4.1.3 继承JdbcDaoSupportDao层实现IPersonDao接口,继承自Spring的DAO模块的JdbcDaoSupport类。JdbcDaoSupport提供JdbcTemplate对象,封装了常用的JDBC操作。Dao实现代码为:PersonDaoImpl.javapackage com.helloweenvsfei.spring.dao;import org.springframework.jdbc.core.support.JdbcDaoSupport;public class PersonDaoImpl extends JdbcDaoSupport implements IPersonDao/继承Supportpublic void initDatabase()/ 初始化方法,创建表String sql=" create table if not exists tb_person "/SQL语句+ "(id int auto_increment,"+"name varchar(255),"+ "sex varchar(10),age int,birthday timestamp,primary key (id)"getJdbcTemplate().execute(sql);/执行SQLSuppressWarnings("all")public List<Person> listPersons()/ 返回所有的PersonString sql = " select id, name, sex, age, birthday from tb_person "/ SQL语句List<Map<String, Object>> list = getJdbcTemplate().queryForList(sql);/ 返回数据List<Person> personList = new ArrayList<Person>();/ List<Person>对象for (Map<String, Object> row : list)/ 遍历数据Person person = new Person();/ 封装为Person对象person.setId(Integer) row.get("id");/ 设置IDperson.setName(String) row.get("name");/ 设置nameperson.setSex(String) row.get("sex");/ 设置sexperson.setAge(Integer) row.get("age");/ 设置ageperson.setBirthday(Date) row.get("birthday");/ 设置birthdaypersonList.add(person);/ 添加到listreturn personList;/ 返回Listpublic void addPerson(Person person)/保存PersonString sql = " insert into tb_person ( name, sex, age, birthday ) values ( ?, ?, ?, ? ) "getJdbcTemplate().update( sql,/ 执行SQL语句new Object person.getName(), person.getSex(),/ 设置SQL参数person.getAge(), person.getBirthday(), );public String getPersonName(Integer id) / 获取person姓名String sql = " select name from tb_person where id = " + id;/ SQL语句return (String) getJdbcTemplate().queryForObject(sql, String.class);/ 查询并返回public int getPersonsCount() / 返回Person数量String sql = " select count(*) from tb_person "/ SQL语句return getJdbcTemplate().queryForInt(sql);/ 查询总数PersonDaoImpl实现了IPersonDao接口。代码直接继承了Spring提供的JdbcDaoSupport,因此没有使用Connection、Statement等JDBC API。Dao操作数据库是使用的Spring封装好的JdbcTemplate,用它来执行SQL、查询Person列表、查询单个Person属性值、查询Person的总数。查询Person列表时返回的是一个List<Map<String, Object>>类型数据。4.1.4 Spring配置PersonDaoImpl还添加了一个非接口的方法initDatabase(),用于生成表结构。需要将initDatabase配置到Spring中。配置代码为:ApplicationContext.xml<bean id="dataSource" class="mons.dbcp.BasicDataSource" destroy-method="close"><!-数据源-><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql:/localhost:3306/spring?characterEncoding=UTF-8" /><property name="username" value="root" /><property name="password" value="admin" /></bean><bean id="personDao" class="com.helloweenvsfei.spring.dao.PersonDaoImpl"<!-DAO->depends-on="dataSource" init-method="initDatabase"><!-初始化方法-><property name="dataSource" ref="dataSource"></property><!-设置数据源-></bean>JdbcDaoSupport内置数据源属性,配置时需要设置。4.1.5 运行代码运行程序前要创建数据库spring。下面的代码中先从Spring中获取Dao对象,然后实例化一个Person对象,并保存进数据库。运行代码为:DaoRun.javapackage com.helloweenvsfei.spring.dao;import org.springframework.beans.factory.xml.XmlBeanFactory;import org.springframework.core.io.ClassPathResource;public class DaoRunpublic static void main(String args)XmlBeanFactory factory = new XmlBeanFactory(/ 获取Factorynew ClassPathResource("applicationContext.xml");IPersonDao personDao = (IPersonDao) factory.getBean("personDao");/获取DAOPerson person = new Person();/实例化一个对象person.setName("Helloween");/设置姓名person.setAge(30);/设置年龄person.setSex("男");/设置性别person.setBirthday(new Date();/设置生日personDao.addPerson(person);/添加到数据库System.out.println("Count: " + personDao.getPersonsCount();/查询Person总数System.out.println(personDao.getPersonName(1);/查询Person姓名List<Person> personList = personDao.listPersons();/查询所有Personfor (Person p : personList)/遍历System.out.println("Name: " + p.getName();/打印姓名DaoRun分别调用了IPersonDao里的四个方法。运行效果如图4.1。图4.1 DaoRun运行效果4.2 返回实体对象JdbcTemplate有个小小的遗憾,不能直接返回Java对象。Spring中提供了MappingSqlQuery类把查询结果封装为Java对象。4.2.1 MappingSqlQuery实现代码MappingSqlQuery是一个抽象类,开发者需要实现它的mapRow(ResultSet, int)方法实现从ResultSet到Java对象的映射。该方法直接返回实体类对象。例如:PersonMappingQuery.javapackage com.helloweenvsfei.spring.dao;import java.sql.ResultSet;import java.sql.SQLException;import org.springframework.jdbc.object.MappingSqlQuery;public class PersonMappingQuery extends MappingSqlQueryOverrideprotected Object mapRow(ResultSet rs, int columnIndex) throws SQLException/封装Person person=new Person();/实例化一个Personperson.setId(rs.getInt("id");/设置idperson.setName(rs.getString("name");/设置nameperson.setSex(rs.getString("sex");/设置sexperson.setAge(rs.getInt("age");/设置ageperson.setBirthday(rs.getTimestamp("birthday");/ 设置birthdayreturn person;/ 返回封装好的对象4.2.2 使用MappingSqlQueryMappingSqlQuery可以重复利用,无论是查找单个的实体,还是查找多个实体,都可以使用MappingSqlQuery对象。使用代码为:PersonDaoImpl.javapublic List findAllPersons()/重写List方法PersonMappingQuery personQuery=new PersonMappingQuery();/使用封装查询personQuery.setDataSource(getDataSource();/设置数据源personQuery.setSql(" select * from tb_person ");/设置SQL/personQuery.declareParameter(new SqlParameter(java.sql.Types.NUMERIC);/参数personQpile();/遍历SQLreturn personQuery.execute(new Object );/直接封装后的ListPersonMappingQuery需要传入DataSourcce与SQL语句,并执行compile编译。如果有参数,需要用declareParameter()设置参数。该方法效果跟前面的listPersons()是一样的。4.2.3 SqlUpdate执行更新SqlUpdate用来执行SQL更新语句,可以设置参数。功能与JdbcDaoSupport是一样的。SqlUpdate可以将某个功能模块化。具体用法为:import java.sql.Types;import javax.sql.DataSource;import org.springframework.jdbc.core.SqlParameter;import org.springframework.jdbc.object.SqlUpdate;public class UpdateCreditRating extends SqlUpdate/继承SqlUpdatepublic UpdateCreditRating(DataSource ds)/构造函数 setDataSource(ds);/设置数据源 setSql("update customer set credit_rating = ? where id = ?");/设置SQL declareParameter(new SqlParameter(Types.NUMERIC);/设置参数位置 declareParameter(new SqlParameter(Types.NUMERIC);/设置参数位置compile();/ 编译SQL public int run(int id, int rating)/执行 Object params = new Object new Integer(rating), new Integer(id);return update(params);调用时实例化一个该对象,然后执行run(id, rating)即可。示例代码略。4.2.4 SqlFunction返回单一结果SqlFunction返回单一行的查询结果,默认返回int类型。开发者可以重载返回其他类型的数据,功能相当于JdbcTemplate的queryForInt()、queryForString()、queryForObject()等。例如:public int countRows()SqlFunction sf=new SqlFunction(dataSource, "select count(*) from mytable"); pile(); return sf.run();4.3 Spring事务管理Spring有一套默认的事务管理机制,因此上面的代码中都没有事务相关的代码。Spring默认会在Dao的方法上加事务,一个方法算是一个事务。像前面的DaoRun代码中:DaoRun.javaIPersonDao personDao=(IPersonDao) factory.getBean("personDao"); /获取DAO对象Person person=new Person();/实例化PersonpersonDao.addPerson(person);/保存PersonSystem.out.println("Count: " + personDao.getPersonsCount();/ 查询Person总数System.out.println(personDao.getPersonName(1);/获取Person姓名addPerson()方法是一个事务,getPersonsCount()是一个事务,getPersonName()是一个事务。如果getPersonName()抛出异常、执行失败了,addPerson()的执行不会回滚,因为它们属于两个事务。多数情况下Spring默认的事务管理都不满足要求。例如getPersonName()执行失败了,要求addPerson()要回滚。这时要根据需要配置事务管理。4.3.1 TransactionRun代码Spring使用DataSourceTransactionManager作为JDBC的事务管理者,同时把被管理的对象使用TransactionProxyFactoryBean配置。这是一个事务代理Bean,能够使用IoC、AOP等注入事务管理代码。例如为TransactionRun的run()方法配置事务。TransactionRun的代码为:TransactionRun.javapackage com.helloweenvsfei.spring.dao;import java.util.*;import org.springframework.beans.factory.xml.XmlBeanFactory;import org.springframework.core.io.ClassPathResource;public class TransactionRunprivate IPersonDao personDao;/DAO接口对象public IPersonDao getPersonDao()/personDao的getter方法return personDao;public void setPersonDao(IPersonDao personDao)/personDao的setter方法this.personDao=personDao;public void run()/主方法,供Spring调用Person person = new Person();/实例化Personperson.setName("事务回滚");/设置nameperson.setAge(30);/设置ageperson.setSex("男");/设置sexperson.setBirthday(new Date();/设置birthdaypersonDao.addPerson(person);/保存System.out.println("Count: " + personDao.getPersonsCount();/获取总数System.out.println(personDao.getPersonName(Integer.MAX_VALUE);/ 抛出异常List<Person> personList = personDao.findAllPersons();/ 查询所有Personfor (Person p : personList)/遍历System.out.println("Name: " + p.getName();/ 输出姓名代码中personDao.getPersonName(Integer.MAX_VALUE)将因为person为null无法获取姓名而抛出异常。如果没有事务,先前保存的person将会被写入数据库。如果有事务,该事务会回滚,导致写入失败。4.3.2 配置事务run()里的代码与前面DaoRun的代码相同的,依次调用了addPerson()、getPersonsCount()、getPersonName()等方法。不同的是getPersonName(Integer.MAX_VALUE)会因数据库中没有记录而抛出异常,因此addPerson()的执行结果会回滚。事务配置代码为:applicationContext.xml<!- JDBC 事务管理 -><bean id="jdbcTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property></bean><!- 为所有的方法配置事务 -><bean id="transactionAttributeSource" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource"><property name="properties"><props><prop key="*">PROPAGATION_REQUIRED</prop><!- 事务类型 -></props></property></bean><!- TransactionRun -><bean id="transactionRun" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><property name="transactionManager" ref="jdbcTransactionManager" /><property name="target"><bean class="com.helloweenvsfei.spring.dao.TransactionRun"><property name="personDao" ref="personDao"></property></bean></property><property name="transactionAttributeSource" ref="transactionAttributeSource" /></bean>Spring会在指定方法执行前开启事务,在指定方法结束后提交事务。如果中间有异常抛出,则事务会失败、回滚。4.3.3 运行代码下面的代码从Spring容器中获取Bean,并执行添加了事务管理的run()方法。代码为:TransactionRun.javapackage com.helloweenvsfei.spring.dao;public class TransactionRunpublic static void main(String args)XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(/工厂"applicationContext.xml");TransactionRun transactionRun = (TransactionRun) factory.getBean("transactionRun");transactionRun.run();/执行run。该方法已经被加上事务执行完毕后从数据库查看表tb_person的内容,名为“事务回滚”的数据并没有被写入数据库。如果去掉事务管理,数据则会写入数据库。