Spring 作为一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,主要负责技术整合,那么Spring是如何整合数据访问的呢?下面就让我们一起来探讨,有不对的地方敬请指教。

一、Spring 提供了编写 DAO 的支持类
1、DaoSupport 类: JdbcDaoSupport、 HibernateDaoSupport,自己写的 DAO 按使用的访问技术,有选择的继承它们。
2、Template 类: JdbcTemplate、 HibernateTemplate,封装了通用操作,如:增删改查。特殊操作,如:分页查询。
3、继承 DaoSupport 类后,就可通过 getJdbcTemplate()、 getHibernateTemplate()方法获得对应的 Template 类对象,即可进行通用操作:
(1)update():实现更新。 

(2)query():实现查询多行记录。

(3)queryForObject():实现查询单行记录。 

(4)queryForInt():实现查询单个 int 值。
4、 将一条记录转换成一个实体对象,需要实现 Spring 提供的 RowMapper 接口(将实体与记录间的转换写在它的实现类中),因为 Spring 提供的 Template 对象中的查询方法 query()有 RowMapper 类型的参数。

二、Spring 提供了声明式事务管理方法
Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。 Spring事务管理器的接口org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。基于 AOP 配置实现,不需要写 Java 代码,加注解标签即可。

Spring 中常用的事务类型:
1、REQUIRED: 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是默认值。
2、SUPPORTS: 支持当前事务,如果当前没有事务,就以非事务方式执行。
3、MANDATORY: 支持当前事务,如果当前没有事务,就抛出异常。
4、REQUIRES_NEW: 新建事务,如果当前存在事务,把当前事务挂起。
5、NOT_SUPPORTED: 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6、NEVER: 以非事务方式执行,如果当前存在事务,则抛出异常。
7、NESTED: 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与REQUIRED 类似的操作。拥有多个可以回滚的保存点,内部回滚不会对外部事务产生影响。只对 DataSourceTransactionManager 有效。

三、Spring对JDBC的支持实例

1、新建一个工程,引入 Spring 开发包( IoC 和 AOP 开发包)和配置文件

2、引入JDBC技术相关的开发包(驱动包)

3、根据要操作的表,编写对应的实体类。

4、编写DAO接口TestDAO和实现类 JdbcTestDAO(实现类继承 JdbcDaoSupport,并使用其提供的 getJdbcTemplate 对象实现增删改查操作)
(1) TestDAO 接口

public test findById(int id);
public List<Cost> findAll();


(2) JdbcTestDAO 实现类

public class JdbcTestDAO extends JdbcDaoSupport implements TestDAO{
public Test findById(int id) { 
String sql="select * from test where ID=?";
Object[] params={id};
RowMapper mapper=new TestRowMapper();
Test test=(Test)getJdbcTemplate().queryForObject(sql, params,mapper);
return test; }public List<Test> findAll() {
String sql="select * from test";
/** 将一条记录转换成一个 Test 对象,需要实现 Spring 提供的 RowMapper 接口  */
RowMapper mapper=new TestRowMapper();
List<Test> list=getJdbcTemplate().query(sql,mapper); 
return list; }
 }


(3) 在 org.audTest.entity 包中创建 TestRowMapper 类

public class TestRowMapper implements RowMapper{//实现 RowMapper 接口
/** rs:结果集。 index:当前记录的索引 。 Object:返回实体对象。 */
public Object mapRow(ResultSet rs, int index) throws SQLException {
Test test = new Test(); 
test.setId(rs.getInt("ID"));
Test.setName(rs.getString("NAME"));
return test;
}
  }

5、将 DAO 组件交给 Spring 容器,在 applicationContext.xml 中进行相关配置
(1)定义 DAO 组件的<bean>元素
<bean id="TestDAO" class="org.audTest.dao.impl.JdbcTestDAO">
<!-- 此处需要注入连接资源给 DaoSupport,用于实例化 Template 对象,否则没有与
数据库的连接! dataSource:代表连接池对象。此处实际是给 JdbcDaoSupport 注入连接,用
于实例化 JdbcTemplate 对象。 -->

<property name="dataSource" ref="MyDataSource"></property>
 </bean>


(2) 需要 DAO 的 Bean 注入一个 dataSource 对象。 dataSource 对象采用一个连接池构建(此处使用 dbcp 连接池),先引入 dbcp 连接池开发包( commons-pool.jar、 commons-dbcp-1.2.2.jar、commons-collections-3.1.jar),再定义 dataSource 对象的<bean>元素。

<!-- 定义连接池 Bean 对象 -->
 <bean id="MyDataSource" class="org.apache.commons.dbcp.BasicDataSource"
 destroy-method="close">
  <!-- 注入数据库连接参数 -->
  <property name="url" value="jdbc:mysql://localhost:3306/web?useUnicode=true&characterEncoding=UTF-8"></property>
  <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  <property name="username" value="root"></property>
  <property name="password" value="root"></property>
  <property name="maxActive" value="20"></property><!-- 设置连接最大数 -->
  <!-- 连接池实例化时初始创建的连接数 -->
  <property name="initialSize" value="2"></property>
 </bean>

6、创建 TestJdbcTestDAO 类,用于测试 xml 配置,可正常执行

@Test
 public void testFindAll() {//测试基于 xml 配置方法
String conf="/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(conf);
TestDAO TestDAO=(TestDAO)ac.getBean("TestDAO");
List<Test> list=TestDAO.findAll();for(Test c:list){ 
System.out.println(c.getId()+" "+c.getName()); 
}
System.out.println("findById: "+TestDAO.findById(1).getName()); }

7、使用注解配置,保留 5 中的连接资源 MyDataSource,而 Bean 组件 TestDAO 删除,并添加开启组件扫描的配置
<context:component-scan base-package="org.audTest" /><!-- 开启组件扫描 -->

8、在 JdbcTestDAO 中添加注解
@Repository //id 值默认为类名首字母小写
@Scope("prototype") //设置非单例模式
public class JdbcTestDAO extends JdbcDaoSupport implements TestDAO{
@Resource //将容器中的 myDataSource 按类型匹配注入
/** 虽然继承了 JdbcDaoSupport,但我们无法在别人的代码中添加注解,所以我们
添加一个 set 方法, 注意名字别用 setDataSource!因为 JdbcDaoSupport 有同名的 set 方法,
且是 final 修饰的,所以需要稍微改变一下。

*/
 public void setMyDataSource(DataSource ds){
 super.setDataSource(ds);//将注入的 dataSource 给 DaoSupport 注入 }

9、在 TestJdbcTestDAO 类添加方法,用于测试注解配置,可正常执行

@Test
 public void testFindAllByAnnotation() {//测试基于注解配置方法
String conf="/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(conf);
TestDAO TestDAO=(TestDAO)ac.getBean("jdbcTestDAO");
List<Test> list=TestDAO.findAll(); 
for(Test c:list){ System.out.println(c.getId()+" "+c.getName()); }
System.out.println("findById: "+TestDAO.findById(1).getName()); }

10、优化,当大量 Bean 组件需要采取注解配置时,每个 Bean 都要写 set 方法注入 dataSource连接资源,所以比较麻烦,可采取继承方式:
(1) 新建 JdbcBaseDAO 类,并继承 JdbcDaoSupport,把 set 方法写入
public class JdbcBaseDAO extends JdbcDaoSupport {
@Resource //将容器中的 myDataSource 按类型匹配注入
public void setMyDataSource(DataSource ds){//注意名字,详见8
super.setDataSource(ds); } }
(2) 删除8中JdbcTestDAOset的 set 方法及其前面的注解,并继承 JdbcBaseDAO
public class JdbcTestDAO extends JdbcBaseDAO implements TestDAO{ …… }

四、连接池优点
1、增强数据访问的稳定性。
2、连接池可以将连接数控制在安全的范围内。
3、连接池中的连接对象始终与数据库保持联通状态,它的 close 方法被重写,不是真正的关闭,而是把连接又放回池中,避免了重复的新建连接和释放连接过程。

五、Spring对Hibernate的支持实例

1、新建一个工程,引入 Spring 开发包( IoC 和 AOP 开发包)和配置文件

2、引入 Hibernate 相关的开发包( Hibernate 开发包+驱动)

3、编写实体类和 hbm.xml 映射描述文件

4、编写 DAO 接口和 HibernateTestDAO 实现类(实现类继承 HibernateDaoSupport,并使用其提供的 HibernateTemplate 对象实现增删改查操作)

5、将 DAO 组件交给 Spring 容器管理,在 applicationContext.xml 中进行相关配置

<bean id="HibernateTestDAO" scope="prototype"
 class="org.audTest.dao.impl.HibernateTestDAO">
 <property name="sessionFactory" ref="MySessionFactory"></property>
 </bean><!-- name:代表 Hibernate 的连接资源,该资源要么是 hibernateTemplate 类型,要么是 sessionFactory 类型, 我们用 sessionFactory 类型。 ref:名字任意起,是我们配置的 sessionFactory 连接资源,有了该资源, getHibernateTemplate()方法才能执行 -->

6、在 applicationContext.xml 中配置 sessionFactory 资源(相当于 Hibernate 的主配置)

<bean id="MySessionFactory"
 class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
 <!-- LocalSessionFactoryBean 是 Hibernate 中 SessionFactory 的子类,我们不用写 -->
 <!-- 注入数据库连接信息,此处要再配置一下 dataSource 数据库连接资源 -->
 <property name="dataSource" ref="MyDataSource"></property><!--ref:名字任意起-->
 <!-- 注入 Hibernate 配置参数 -->
 <property name="hibernateProperties">
 <props><!-- 放 Spring 中属性要加个前缀 hibernate -->
 <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
 <prop key="hibernate.show_sql">true</prop>
 <prop key="hibernate.format_sql">true</prop>
 </props>
 </property>
 <!-- 注入映射描述 -->
 <property name="mappingResources">
 <list><value>org/audTest/entity/Test.hbm.xml</value></list>
 </property>
 </bean>

7、在 applicationContext.xml 中配置 dataSource 数据库连接资源dataSource 对象采用一个连接池构建(此处使用 dbcp 连接池),先引入 dbcp 连接池开发包

<!-- 定义连接池 Bean 对象 -->
 <bean id="MyDataSource" class="org.apache.commons.dbcp.BasicDataSource"
 destroy-method="close">
  <!-- 注入数据库连接参数 -->
  property name="url" value="jdbc:mysql://localhost:3306/web?useUnicode=true&characterEncoding=UTF-8"></property>
  <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
  <property name="username" value="root"></property>
  <property name="password" value="root"></property>
  <property name="maxActive" value="20"></property><!-- 设置连接最大数 -->
  <!-- 连接池实例化时初始创建的连接数 -->
  <property name="initialSize" value="2"></property>
 </bean>

8、创建 TestHibernateTestDAO 类,用于测试 xml 配置

9、使用注解配置,新建 applicationContext-annotation.xml 配置文件,并添加开启组件
扫描的配置

<context:component-scan base-package="org.audTest" /><!-- 开启组件扫描 -->

10、在 HibernateTestDAO 中添加注解

@Repository("HibernateTestDAO")
 @Scope("prototype")
 public class HibernateTestDAO extends HibernateDaoSupport implements TestDAO {
 @Resource //setSessionFactory 名字用不了是 final 
 public void setMySessionFactory(SessionFactory sf){
 super.setSessionFactory(sf);//将注入的 sessionFactory 给 HibernateDaoSupport 传入
 }

11、在 TestHibernateTestDAO 类添加方法,用于测试注解配置,可正常执行

12、在 TestHibernateTestDAO 类添加方法,用于测试查询