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 类添加方法,用于测试查询