【Java EE 学习 52】【Spring学习第四天】【Spring与JDBC】【JdbcTemplate创建的三种方式】【Spring事务管理】【事务中使用dbutils则回滚失败!!!??】

一、JDBC编程特点

  静态代码+动态变量=JDBC编程。

  静态代码:比如所有的数据库连接池 都实现了DataSource接口,都实现了Connection接口。

  动态变量:用户名、密码、连接的数据库、表名、SQL语句等信息。

  在spring中动态变量能够通过注入的形式给予。这样的变成方式适合包装成模板。静态代码构成了模板,而动态变量是需要传入的参数。

二、核心类JdbcTemplate

  1.基于模板的设置。

  2.完成了资源的创建和释放的工作。

  3.简化了我们的JDBC操作。

  4.完成了对JDBC的核心流程的工作,包括SQL语句的创建和执行。

  5.仅仅需要传递DataSource就可以将其实例化。

  6.JdbcTemplate只需要创建一次。

  7.JdbcTemplate是线程安全类。

三、使用三种方式将DataSource对象传递给JdbcTemplate,创建JdbcTemplate对象。

  1.第一种方法:继承org.springframework.jdbc.core.support.JdbcDaoSupport类

    (1)首先配置数据源DataSource,这里使用c3p0数据库连接池

1 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
2         <constructor-arg index="0" value="namedconfig"></constructor-arg>
3 </bean>
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <c3p0-config>
 3     <!-- 默认配置,只可以出现一次 -->
 4     <default-config>
 5         <!-- 连接超时设置30秒 -->
 6         <property name="checkoutTimeout">30000</property>
 7         <!-- 30秒检查一次connection的空闲 -->
 8         <property name="idleConnectionTestPeriod">30</property>
 9         <!--初始化的池大小 -->
10         <property name="initialPoolSize">2</property>
11         <!-- 最多的一个connection空闲时间 -->
12         <property name="maxIdleTime">30</property>
13         <!-- 最多可以有多少个连接connection -->
14         <property name="maxPoolSize">10</property>
15         <!-- 最少的池中有几个连接 -->
16         <property name="minPoolSize">2</property>
17         <!-- 批处理的语句-->
18         <property name="maxStatements">50</property>
19         <!-- 每次增长几个连接 -->
20         <property name="acquireIncrement">3</property>
21         <property name="driverClass">com.mysql.jdbc.Driver</property>
22         <property name="jdbcUrl">
23             <![CDATA[jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8]]>
24         </property>
25         <property name="user">root</property>
26         <property name="password">5a6f38</property>
27     </default-config> 
28     
29     <named-config name="namedconfig">
30         <!-- 连接超时设置30秒 -->
31         <property name="checkoutTimeout">30000</property>
32         <!-- 30秒检查一次connection的空闲 -->
33         <property name="idleConnectionTestPeriod">30</property>
34         <!--初始化的池大小 -->
35         <property name="initialPoolSize">2</property>
36         <!-- 最多的一个connection空闲时间 -->
37         <property name="maxIdleTime">30</property>
38         <!-- 最多可以有多少个连接connection -->
39         <property name="maxPoolSize">2</property>
40         <!-- 最少的池中有几个连接 -->
41         <property name="minPoolSize">2</property>
42         <!-- 批处理的语句-->
43         <property name="maxStatements">50</property>
44         <!-- 每次增长几个连接 -->
45         <property name="acquireIncrement">2</property>
46         <property name="driverClass">com.mysql.jdbc.Driver</property>
47         <property name="jdbcUrl">
48             <![CDATA[jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8]]>
49         </property>
50         <property name="user">root</property>
51         <property name="password">5a6f38</property>
52     </named-config> 
53 </c3p0-config>
c3p0-config.xml

    (2)定义Dao

1 package com.kdyzm.spring.jdbc;
2 
3 public interface CourseDao {
4     public Course getCourse(Long cid);
5 }
com.kdyzm.spring.jdbc.CourseDao

    (3)实现Dao,这是非常关键的一步,因为需要继承JdbcDaoSupport类。

 1 package com.kdyzm.spring.jdbc;
 2 
 3 import java.sql.SQLException;
 4 
 5 import org.apache.commons.dbutils.QueryRunner;
 6 import org.apache.commons.dbutils.handlers.BeanHandler;
 7 import org.springframework.jdbc.core.support.JdbcDaoSupport;
 8 
 9 public class CourseDaoImpl extends JdbcDaoSupport implements CourseDao{
10     @Override
11     public Course getCourse(Long cid) {
12         String sql="select cid,cname from course where cid = ?";
13         QueryRunner run=new QueryRunner(this.getDataSource());
14         Course course=null;
15         try {
16             course = run.query(sql,new BeanHandler<Course>(Course.class),1L);
17         } catch (SQLException e) {
18             e.printStackTrace();
19         }
20         return course;
21     }
22 }

    (4)在applicationContext.xml文件中进行配置,注意这里使用proerty标签相当于调用了JdbcDaoSupport的setDataSource方法。

<!-- 第一种方法:继承JdbcDaoSupport -->
    <bean id="courseDao" class="com.kdyzm.spring.jdbc.CourseDaoImpl">
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
    </bean>

    (5)测试代码:

1 ApplicationContext context=new ClassPathXmlApplicationContext("com/kdyzm/spring/jdbc/applicationContext.xml");
2 CourseDao courseDao = (CourseDao) context.getBean("courseDao");
3 Course course=courseDao.getCourse(1L);
4 System.out.println(course);
 1 package com.kdyzm.spring.jdbc;
 2 /*
 3  * 课程类
 4  */
 5 public class Course {
 6     private Long cid;
 7     private String cname;
 8     
 9     public Course() {
10     }
11     @Override
12     public String toString() {
13         return "Course [cid=" + cid + ", cname=" + cname + "]";
14     }
15     public Long getCid() {
16         return cid;
17     }
18     public void setCid(Long cid) {
19         this.cid = cid;
20     }
21     public String getCname() {
22         return cname;
23     }
24     public void setCname(String cname) {
25         this.cname = cname;
26     }
27 }
com.kdyzm.spring.jdbc.Course

      测试结果:

      

    分析:继承了JdbcDaoSupport类为什么是关键?JdbcDaoSupport类中有一个非常关键的方法setDataSource,它有一个JdbcTemplate类型的成员变量,我们通过使用该成员变量来完成所有工作:

      

    通过上面的流程图可以看到实际上JdbcDaoSupport将DataSource的引用传递给了JdbcTemplate,JdbcTemplate对象使用该DataSource对象完成对数据库的增删查改操作。

  2.第二种方法:使用JdbcTemplate对象作为成员变量

    (1)配置数据源

<!-- 引入DataSource -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <constructor-arg index="0" value="namedconfig"></constructor-arg>
    </bean>

    (2)创建CourseDaoImpl1类,该类不再继承JdbcDaoSupport类,只是实现了CourseDao接口,但是新增加了成员变量JdbcTemplate jdbcTemplate,并提供了set、get方法。

 1 package com.kdyzm.spring.jdbc;
 2 /*
 3  * 第二种实现方式:使用JdbcTemplate类作为成员变量
 4  */
 5 import java.sql.SQLException;
 6 
 7 import org.apache.commons.dbutils.QueryRunner;
 8 import org.apache.commons.dbutils.handlers.BeanHandler;
 9 import org.springframework.jdbc.core.JdbcTemplate;
10 
11 public class CourseDaoImpl1 implements CourseDao{
12 
13     JdbcTemplate jdbcTemplate=null;
14     
15     public JdbcTemplate getJdbcTemplate() {
16         return jdbcTemplate;
17     }
18 
19     public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
20         this.jdbcTemplate = jdbcTemplate;
21     }
22 
23     public Course getCourse(Long cid) {
24         String sql="select cid,cname from course where cid = ?";
25         QueryRunner run=new QueryRunner(this.getJdbcTemplate().getDataSource());
26         Course course=null;
27         try {
28             course = run.query(sql,new BeanHandler<Course>(Course.class),cid);
29         } catch (SQLException e) {
30             e.printStackTrace();
31         }
32         return course;
33     }
34 }

    (3)配置applicationContext.xml文件

        * 将JdbcTemplate对象纳入Spring容器管理

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg index="0">
            <ref bean="dataSource"/>
        </constructor-arg>
    </bean>

        * 将courseDao1纳入Spring容器管理

<bean id="courseDao1" class="com.kdyzm.spring.jdbc.CourseDaoImpl1">
        <property name="jdbcTemplate">
            <ref bean="jdbcTemplate"/>
        </property>
</bean>

      原理分析:实际上JdbcDaoSupport类的核心就是JdbcTemplate,同理,我们也可以将该类对象作为成员变量,效果是相同的。

  3.第三种方式:继承JdbcTemplate类。

    (1)配置数据源,同上

    (2)新建CourseDaoImpl2,该类继承了JdbcTemplate类,并且实现了CourseDao接口。

 1 package com.kdyzm.spring.jdbc;
 2 /*
 3  * 第二种实现方式:使用JdbcTemplate类作为成员变量
 4  */
 5 import java.sql.SQLException;
 6 
 7 import javax.sql.DataSource;
 8 
 9 import org.apache.commons.dbutils.QueryRunner;
10 import org.apache.commons.dbutils.handlers.BeanHandler;
11 import org.springframework.jdbc.core.JdbcTemplate;
12 
13 public class CourseDaoImpl2 extends JdbcTemplate implements CourseDao{
14     public CourseDaoImpl2(DataSource dataSource) {
15         super(dataSource);
16     }
17 
18     public Course getCourse(Long cid) {
19         String sql="select cid,cname from course where cid = ?";
20         QueryRunner run=new QueryRunner(this.getDataSource());
21         Course course=null;
22         try {
23             course = run.query(sql,new BeanHandler<Course>(Course.class),cid);
24         } catch (SQLException e) {
25             e.printStackTrace();
26         }
27         return course;
28     }
29 }

    (3)配置applicationContext.xml文件

<!-- 第三种方法:继承JdbcTemplate类 -->
    <bean id="courseDao2" class="com.kdyzm.spring.jdbc.CourseDaoImpl2">
        <constructor-arg index="0">
            <ref bean="dataSource"/>
        </constructor-arg>
    </bean>

    测试略。原理分析:最根本的就是JdbcTemplate类,所以干脆继承该类。

四、spring中的事务管理

  1.Spring的事务管理是声明式的事务管理。什么是"声明式"的事务管理?在配置文件中声明某个方法需要开启事务进行管理,然后被声明的这个方法就会开启事务。

    Spring事务管理相对于Hibernate和JDBC的优越之处:

      

  2.Spring的事务管理器

    spring没有直接管理事务,而是将管理事务的责任委托给JTA或者相应的持久性机制所提供的某个特定平台的事务实现。

事务管理器

目标

org.springframework.transaction.PlatformTransactionManager

事务管理器的顶级接口

org.springframework.transaction.support.AbstractPlatformTransactionManager

事务管理器的超类

org.springframework.jdbc.datasource.DataSourceTransactionManager

在单一的JDBC DataSource中的事务管理

org.springframework.orm.hibernate3.HibernateTransactionManager

当持久化机制是hibernate的时候,使用它来管理事务

org.springframework.orm.jdo.JdoTransactionManager

使用一个JTA实现来管理事务,当一个事务跨越多个资源的时候必须使用

  3.Spring事务的传播属性

    

      在实际工作当中,99%的使用过的是REQUIRED。

  4.spring事务的隔离级别

    

      在实际工作中,99%使用的是DEFAULT。

  5.XML配置练习

    (1)引入命名空间

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4     xmlns:context="http://www.springframework.org/schema/context"
 5     xmlns:aop="http://www.springframework.org/schema/aop"
 6    xmlns:tx="http://www.springframework.org/schema/tx"
 7     
 8     xsi:schemaLocation="
 9            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
10            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
11            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
12          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
13            ">
14     
15 </beans>

     (2)user表:

CREATE TABLE `user` (
   `id` varchar(32) NOT NULL,
   `name` varchar(32) DEFAULT NULL,
   `age` int(11) DEFAULT NULL,
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8

    (3)新建类:

 1 package com.kdyzm.spring.jdbc.transaction1;
 2 
 3 public class User {
 4     private String id;
 5     private String name;
 6     private Integer age;
 7     public User() {
 8     }
 9     public String getId() {
10         return id;
11     }
12     public void setId(String id) {
13         this.id = id;
14     }
15     public String getName() {
16         return name;
17     }
18     public void setName(String name) {
19         this.name = name;
20     }
21     public Integer getAge() {
22         return age;
23     }
24     public void setAge(Integer age) {
25         this.age = age;
26     }
27     @Override
28     public String toString() {
29         return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
30     }
31 }
com.kdyzm.spring.jdbc.transaction1.User
1 package com.kdyzm.spring.jdbc.transaction1;
2 
3 import java.sql.SQLException;
4 
5 public interface UserDao {
6     public User updateUser(User user) throws SQLException;
7 }
com.kdyzm.spring.jdbc.transaction1.UserDao

非常重要的一个类:com.kdyzm.spring.jdbc.transaction1.UserDaoImpl

 1 package com.kdyzm.spring.jdbc.transaction1;
 2 
 3 import java.sql.SQLException;
 4 
 5 import org.apache.commons.dbutils.QueryRunner;
 6 import org.springframework.jdbc.core.JdbcTemplate;
 7 
 8 public class UserDaoImpl implements UserDao{
 9     private JdbcTemplate jdbcTemplate;
10 
11     public JdbcTemplate getJdbcTemplate() {
12         return jdbcTemplate;
13     }
14 
15     public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
16         this.jdbcTemplate = jdbcTemplate;
17     }
18     
19     /**
20      * 使用该方法测试非跨dao事务回滚问题
21      * 一定不能使用dbutils工具,否则不能回滚!!!!!!!!!!!!!
22      * @throws SQLException 
23      */
24     @Override
25     public User updateUser(User user) throws SQLException {
26         String sql="update user set age=age+1 where id='0001'";
27 //        QueryRunner runner=new QueryRunner(this.jdbcTemplate.getDataSource());
28 //        runner.update(this.jdbcTemplate.getDataSource().getConnection(),sql,"0001");
29         this.jdbcTemplate.execute(sql);
30         int a=1/0;
31         sql="update user set age=age+1 where id='0002'";
32         this.jdbcTemplate.execute(sql);
33 //        runner.update(this.jdbcTemplate.getDataSource().getConnection(),sql,"0002");
34         return null;
35     }
36 }

    使用该类应当注意:不能使用dbutils处理事务相关的问题,否则不能回滚!!

1 package com.kdyzm.spring.jdbc.transaction1;
2 
3 import java.sql.SQLException;
4 
5 public interface UserService {
6     public User updateUser(User user) throws SQLException;
7 }
com.kdyzm.spring.jdbc.transaction1.UserService
 1 package com.kdyzm.spring.jdbc.transaction1;
 2 
 3 import java.sql.SQLException;
 4 
 5 
 6 public class UserServiceImpl implements UserService{
 7     private UserDao userDao;
 8     public UserDao getUserDao() {
 9         return userDao;
10     }
11 
12     public void setUserDao(UserDao userDao) {
13         this.userDao = userDao;
14     }
15 
16     @Override
17     public User updateUser(User user) throws SQLException {
18         return userDao.updateUser(user);
19     }
20 
21 }
com.kdyzm.spring.jdbc.transaction1.UserServiceImpl

  使用的数据库连接池:c3p0,配置文件如下:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <c3p0-config>
 3     <!-- 默认配置,只可以出现一次 -->
 4     <default-config>
 5         <!-- 连接超时设置30秒 -->
 6         <property name="checkoutTimeout">30000</property>
 7         <!-- 30秒检查一次connection的空闲 -->
 8         <property name="idleConnectionTestPeriod">30</property>
 9         <!--初始化的池大小 -->
10         <property name="initialPoolSize">2</property>
11         <!-- 最多的一个connection空闲时间 -->
12         <property name="maxIdleTime">30</property>
13         <!-- 最多可以有多少个连接connection -->
14         <property name="maxPoolSize">10</property>
15         <!-- 最少的池中有几个连接 -->
16         <property name="minPoolSize">2</property>
17         <!-- 批处理的语句-->
18         <property name="maxStatements">50</property>
19         <!-- 每次增长几个连接 -->
20         <property name="acquireIncrement">3</property>
21         <property name="driverClass">com.mysql.jdbc.Driver</property>
22         <property name="jdbcUrl">
23             <![CDATA[jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8]]>
24         </property>
25         <property name="user">root</property>
26         <property name="password">5a6f38</property>
27     </default-config> 
28     
29     <named-config name="namedconfig">
30         <!-- 连接超时设置30秒 -->
31         <property name="checkoutTimeout">30000</property>
32         <!-- 30秒检查一次connection的空闲 -->
33         <property name="idleConnectionTestPeriod">30</property>
34         <!--初始化的池大小 -->
35         <property name="initialPoolSize">2</property>
36         <!-- 最多的一个connection空闲时间 -->
37         <property name="maxIdleTime">30</property>
38         <!-- 最多可以有多少个连接connection -->
39         <property name="maxPoolSize">2</property>
40         <!-- 最少的池中有几个连接 -->
41         <property name="minPoolSize">2</property>
42         <!-- 批处理的语句-->
43         <property name="maxStatements">50</property>
44         <!-- 每次增长几个连接 -->
45         <property name="acquireIncrement">2</property>
46         <property name="driverClass">com.mysql.jdbc.Driver</property>
47         <property name="jdbcUrl">
48             <![CDATA[jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8]]>
49         </property>
50         <property name="user">root</property>
51         <property name="password">5a6f38</property>
52     </named-config> 
53 </c3p0-config>
c3p0-config.xml

  最后,最重要的com.kdyzm.spring.jdbc.transaction1.applicationContext.xml文件:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4     xmlns:context="http://www.springframework.org/schema/context"
 5     xmlns:aop="http://www.springframework.org/schema/aop"
 6     xmlns:tx="http://www.springframework.org/schema/tx"
 7     xsi:schemaLocation="
 8            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 9            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
10            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
11            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
12            ">
13     <!-- 首先配置数据源 -->
14     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
15         <constructor-arg index="0" value="namedconfig"></constructor-arg>
16     </bean>
17     <!-- 配置JdbcTemplate模板 -->
18     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
19         <constructor-arg index="0">
20             <ref bean="dataSource"/>
21         </constructor-arg>
22     </bean>
23     <!-- 配置JDBC事务管理器 -->
24     <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
25         <property name="dataSource">
26             <ref bean="dataSource"/>
27         </property>
28     </bean>
29     
30     
31     <!-- 配置通知 -->
32     <tx:advice id="advice" transaction-manager="dataSourceTransactionManager">
33         <tx:attributes >
34             <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
35         </tx:attributes>
36     </tx:advice>
37     
38     <!-- 配置切入点 -->
39     <aop:config>
40         <!-- <aop:pointcut expression="execution(* com.kdyzm.spring.jdbc.transaction.*ServiceImpl.*(..))" id="perform"/> -->
41         <aop:pointcut expression="execution(* com.kdyzm.spring.jdbc.transaction1.UserServiceImpl.updateUser(..))" id="perform"/>
42         <aop:advisor advice-ref="advice" pointcut-ref="perform"/>
43     </aop:config>
44     
45     <!-- 需要注意的是不需要配置切面,因为如果要配置切面的话就需要程序员完成(事务管理),
46         这样spring就没有意义了 -->
47         
48     <!-- 程序员需要干的事情! -->
49     <!-- 需要纳入spring容器的 -->
50     <bean id="userDao" class="com.kdyzm.spring.jdbc.transaction1.UserDaoImpl">
51         <property name="jdbcTemplate" ref="jdbcTemplate"></property>
52     </bean>
53     <bean id="userService" class="com.kdyzm.spring.jdbc.transaction1.UserServiceImpl">
54         <property name="userDao" ref="userDao"></property>
55     </bean>
56 </beans>

  (4)测试

ApplicationContext context=new ClassPathXmlApplicationContext("com/kdyzm/spring/jdbc/transaction1/applicationContext.xml");
        UserService userService = (UserService) context.getBean("userService");
        User user=new User();
        userService.updateUser(user);

    由于在UserDaoImpl.updateUser方法中存在/0的异常,所以方法会异常终止,但是spring会回滚方法中的事务。通过查看数据库中user的age字段可以得到结果。如果0001和0002并没有发生任何改变,则表示成功回滚;如果只有0001字段发生了自增长,则表示回滚失败。

  6.小练习总结:

    (1)事务的方法中不能使用dbutils工具,否则回滚失败!

    (2)在service层控制事务,目标类是*ServiceImpl,目标方法是update方法。所以配置切入点表达式是:

        <aop:pointcut expression="execution(* com.kdyzm.spring.jdbc.transaction1.UserServiceImpl.updateUser(..))" id="perform"/>

    (3)事务管理器是Jdbc事务管理器,所以使用的类是DataSourceTransactionManager,创建对象的时候需要调用setDataSource方法传入DataSource参数。

<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
</bean>

    (4)配置通知,需要制定事务管理器,这里的配置是一个模板,并不针对某个类中的方法,而是针对匹配到的所有方法:

<tx:advice id="advice" transaction-manager="dataSourceTransactionManager">
        <tx:attributes >
            <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
            <tx:method name="*" read-only="true" isolation="DEFAULT" propagation="REQUIRED"/>
        </tx:attributes>
</tx:advice>

      * isolation:制定事务的隔离界别,使用default表示使用数据库制定的隔离级别,spring不再另外指定。

      * propagation:指定事务的传播属性,默认是REQUIRED,表示如果当前方法已经在一个事务中运行则加入该事务,否则创建一个新事务。

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

      

 

posted @ 2015-10-02 21:53  狂盗一枝梅  阅读(482)  评论(0编辑  收藏  举报