天天看点

Spring JdbcTemplate&声明式事务

1 Spring JdbcTemplate基本使用

1.1 JdbcTemplate概述

JdbcTemplate是spring框架中提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装。

spring框架为我们提供了很多操作模板类。列如:操作关系型数据的JdbcTemplate和HibernateTemplate,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等等。

1.2 JdbcTemplate开发的基本步骤

① 导入spring-jdbc和spring-tx坐标

② 创建数据库表和实体类

③ 创建JdbcTemplate对象

④ 执行数据库操作

代码实现过程,我们先来明确两个问题。

  • JdbcTemplate是否需要数据源?如果需要如何设置数据源?

    创建JdbcTemplate对象,然后设置数据源,因此需要提前创建数据源,同时设置好数据库相关信息。

  • JdbcTemplate调用哪个方法完成插入操作?

    调用JdbcTemplate的update方法传入sql语句和参数完成插入操作

① 导入spring-jdbc和spring-tx坐标

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>5.0.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>5.0.5.RELEASE</version>
</dependency>
           

② 创建数据库表和实体类

public class Account {

    private String name;
    private double money;

    public String getName() {
        return name;
    }

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

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}
           

③ 创建JdbcTemplate对象

//创建数据源对象
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUser("root");
        dataSource.setPassword("root");

        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //设置数据源对象  知道数据库在哪
        jdbcTemplate.setDataSource(dataSource);
           

④ 执行数据库操作

//执行操作
        int row = jdbcTemplate.update("insert into account values(?,?)", "tom", 5000);
        System.out.println(row); //受影响的行数
           

1.4 JdbcTemplate XML配置

将JdbcTemplate的创建权交给Spring,将数据源DataSource的创建权也交给spring,在spring容器内部将数据源DataSource注入到JdbcTemplate模板对象中,而这些都可以通过Spring的配置来完成。

  • Spring如何配置bean及如何通过配置完成注入?
    1. 先配置数据源,然后配置JdbcTemplate,然后将数据源注入到JdbcTemplate中。
    2. 通过Spring容器获得JdbcTemplate对象,调用JdbcTemplate对象的update方法,传入sql语句及参数完成插入数据的操作。
<!--数据源对象-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
        <property name="user" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <!--jdbc模板对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
           
  • 抽取jdbc.properties文件
  1. 将数据库的连接信息配置抽取成外部的properties配置文件
  2. Spring读取外部的properties属性配置文件
<!--加载jdbc.properties-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--数据源对象-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--jdbc模板对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
           

1.5 JdbcTemplate基本操作-CRUD

注入JdbcTemplate

@Autowired
    private JdbcTemplate jdbcTemplate;
           

JdbcTemplate执行增删改都是使用update方法。

  1. 更新
@Test
    public void testUpdate(){
        jdbcTemplate.update("update account set money=? where name=?",10000,"tom");
    }
           
  1. 删除
@Test
    public void testDelete(){
        jdbcTemplate.update("delete from account where name=?","tom");
    }
           
  1. 查询

    要将结果集封装成javaBean我们需要传入一个参数BeanPropertyRowMapper,并且传入要封装成的JavaBean的字节码。

    Query方法表示将结果集封装成javaBean的集合,queryForObject方法表示将结果集封装成单个javaBean对象或者封装结合查询的结果,比如统计查询。

@Test
    public void testQueryCount(){
        Long count = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
        System.out.println(count);
    }

    @Test
    public void testQueryOne(){
        Account account = jdbcTemplate.queryForObject("select * from account where name=?", new BeanPropertyRowMapper<Account>(Account.class), "tom");
        System.out.println(account);
    }

    @Test
    public void testQueryAll(){
        List<Account> accountList = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
        System.out.println(accountList);
    }
           

2 Spring的事务控制

2.1 编程式事务控制

  1. PlatformTransactionManager 平台事务管理器

    PlatformTransactionManager接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法。

    Spring JdbcTemplate&amp;声明式事务
    PlatformTransactionManager是接口类型,不同的Dao层技术有不同的实现类,列如:dao层技术是jdbc或mybatis时:org.springframework.jdbc.datasource.DataSourceTransactionManager,当Dao层技术是Hibernate时:org.springframework.orm.hibernateTransactionManager
  2. TransactionDefinition 事务定义器

    TransactionDefinition是事务的定义信息对象,里面有如下方法:

    ① 事务隔离级别

    设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚度。

  • ISOLATION_DEFAULT
  • ISOLATION_READ_UNCOMMITTED
  • ISOLATION_READ_COMMITTED
  • ISOLATION_REPEATABLE_READ
  • ISOLATION_SERIALIZABLE

    ② 事务传播行为

    Spring JdbcTemplate&amp;声明式事务
  1. TransactionStatus 事务状态

    TransactionStatus接口提供的是事务具体的运行状态,方法介绍如下。

    Spring JdbcTemplate&amp;声明式事务

2.2 基于XML的声明式事务控制

  • 什么是声明式事务控制

    spring的声明式事务控制顾名思义就是

    采用声明的方式来处理事务

    。这里所说的声明,就是指在配置文件中声明,用在spring配置文件中声明式的处理事务来代替代码式的处理事务。
  • 声明式事务处理的作用
  1. 事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理师是系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可。
  2. 在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便。

    注意:Spring声明式事务控制底层就是AOP

  • 转账业务环境搭建

账户实体类

public class Account {

    private String name;
    private double money;

    public String getName() {
        return name;
    }

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

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}
           

dao层逻辑

public interface AccountDao {

    public void out(String outMan,double money);
    public void in(String inMan,double money);
}
           
public class AccountDaoImpl implements AccountDao {

    private JdbcTemplate jdbcTemplate;
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void out(String outMan, double money) {
        jdbcTemplate.update("update account set money=money-? where name=?",money,outMan);
    }

    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
    }
}

           

service层业务逻辑

public interface AccountService {

    public void transfer(String outMan,String inMan,double money);

}
           
public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public void transfer(String outMan, String inMan, double money) {
        accountDao.out(outMan,money);
        //int i = 1/0;
        accountDao.in(inMan,money);
    }
}

           

测试,tom向lucy转账500元。

public class AccountController {

    public static void main(String[] args) {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = app.getBean(AccountService.class);
        accountService.transfer("tom","lucy",500);
    }

}
           

当在service层的转账业务出现了异常,则accountDao.out(outMan,money)事务执行完,然而accountDao.in(inMan,money)事务由于int i = 1/0异常而终止,因此导致两个事务不同步的问题。因此,需要声明式事务控制来管理此类事务,我们使用XML配置的方式来控制。

  • 声明式事务控制明确事项:
  1. 谁是切点? ----> 需要被管理事务
  2. 谁是通知? ----->
  3. 配置切面?
  • 声明式事务控制的实现

    ① 引入tx命名空间

xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
           

② 配置事务增强

<!--配置平台事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
 <!--通知  事务的增强-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--设置事务的属性信息的-->
        <tx:attributes>
            <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
            <tx:method name="save" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
            <tx:method name="findAll" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true"/>
            <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
           

③ 配置事务AOP织入

<!--配置事务的aop织入-->
    <aop:config>
        <aop:pointcut id="txPointcut" expression="execution(* com.itheima.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>
           
  • 切点方法的事务参数的配置
    Spring JdbcTemplate&amp;声明式事务
    其中,tx:method代表切点方法的事务参数的配置,列如:

name:切点方法名称

isolation:事务的隔离级别

propagation:事务的传播行为

timeout:超时时间

read-only:是否只读

2.3 基于注解的声明式事务控制

在业务代码中加上以下这个注解,可以在类上也可以定义在方法上:

@Transactional

需要在spring的xml配置文件中开启事务注解驱动,注入事务管理器

<!--开启事务的注解驱动-->
<tx:annotation-driven transaction-manager="transationManager">
           

总结:

① 使用@Transactional在需要进行事务控制的类或是方法上修饰,注解可用的属性同xml配置方式,列如隔离级别、传播行为等。

② 注解使用在类上,那么该类的所有方法都是要同一套注解参数配置。

③ 使用在方法上,不同的方法可以使用不同的事务参数配置。

④ xml配置文件中要开启事务注解驱动

<tx:annotation-driven transaction-manager="transationManager">