进阶

Spring中的JDBCTemplate

基本概述

上面这张图是持久层总图,我们今天的主角是JdbcTemplate,可以看到对JDBC进行了薄薄封装

它是 spring 框架中提供的一个对象,是对原始 Jdbc API 对象的简单封装。spring 框架为我们提供了很多的操作模板类。

  • 操作关系型数据的:
    • JdbcTemplate
    • HibernateTemplate
  • 操作 nosql 数据库的:
    • RedisTemplate
  • 操作消息队列的:
    • JmsTemplate
  • 我们今天的主角在spring-jdbc-5.0.2.RELEASE.jar
  • 还需要导入一个 spring-tx-5.0.2.RELEASE.jar(它是和事务相关的)。
  • 不可避免地要导入数据库驱动

JDBCTemplate的作用

与数据库进行交互,实现对表的CRUD

如何创建该对象

原始方式

1、依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bean</groupId>
    <artifactId>SpringAOP</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

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

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
    </dependencies>
</project>

2、表

create table account(
	id int primary key auto_increment,
	name varchar(40),
	money float
)character set utf8 collate utf8_general_ci;

insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);

3、实体类

package com.bean.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private Integer id;
    private String name;
    private Float money;

    public Account() {
    }

    public Account(Integer id, String name, Float money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Float getMoney() {
        return money;
    }

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

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

4、使用JDBCTemplate

package com.bean.jdbcTemplate;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

public class JdbcTemplateDemo {
    public static void main(String[] args) {

        //准备数据源:JdbcTemplate内置数据源
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring");
        dataSource.setUsername("root");
        dataSource.setPassword("root");

        //JdbcTemplate
        JdbcTemplate jdbcTemplate = new JdbcTemplate();

        //设置数据源
        jdbcTemplate.setDataSource(dataSource);

        jdbcTemplate.execute("select * from account");

    }
}

使用Spring来创建

XML创建

1、bean.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--配置jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

</beans>

2、JDBCTemplateDemo

package com.bean.jdbcTemplate;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

public class JdbcTemplateDemo {
    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");

        JdbcTemplate jdbcTemplate = (JdbcTemplate)context.getBean("jdbcTemplate");

        jdbcTemplate.execute("insert into account (name,money) values ('dd',1000)");

    }
}

注解创建

1、注解配置

  • config.SpringConfiguration
package config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.annotation.Resource;
import javax.sql.DataSource;

@Configuration
@ComponentScan("com.bean")
public class SpringConfiguration {

    @Bean
    public JdbcTemplate jdbcTemplateBean(){
        return new JdbcTemplate(dataSourceBean());
    }

    @Bean
    public DriverManagerDataSource dataSourceBean(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }


}
  • JDBCTemplateDemo
package com.bean.jdbcTemplate;

import config.SpringConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

public class JdbcTemplateDemo {
    public static void main(String[] args) {

        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);

        JdbcTemplate jdbcTemplate = (JdbcTemplate)context.getBean("jdbcTemplateBean");

        jdbcTemplate.execute("insert into account (name,money) values ('dd',1000)");

    }
}

注解不能和xml一起,否则报错,前面已经讲过了


JDBCTemplate的CRUD

一个Demo

  • JDBCTemplateDemo
package com.bean.jdbcTemplate;

import com.bean.domain.Account;
import config.SpringConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import java.util.List;

public class JdbcTemplateDemo {
    public static void main(String[] args) {

        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);

        JdbcTemplate jdbcTemplate = (JdbcTemplate)context.getBean("jdbcTemplateBean");

//        增加:使用update,没啥好说的的,注意类型是float
        jdbcTemplate.update("insert into account (name,money) values(?,?)","new Account",1000f);
//        删除:使用update,没啥好说的的,注意类型是float
        jdbcTemplate.update("delete from account where id=?",5);
//        更新:没啥好说的的,注意类型是float
        jdbcTemplate.update("update account set name=?,money=? where id=?","update test",100f,6);

//        查询
//        查询所有:查询所有,使用query方法,spring使用了这个BeanPropertyRowMapper封装进去了类型,然后直接封装进去
        List<Account> accounts = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
        for (Account account : accounts) {
            System.out.println(account);
        }

//        查询一个:这里也可以使用查询出List的,不过输出第0位就可以了,注意这里使用的构造函数中的最后一个参数为可变参数,只有在jdk1.5之后才能使用
        List<Account> account = jdbcTemplate.query("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), 7);
        System.out.println(account.isEmpty()?"无参数":account.get(0));

//        查询一行一列(聚合函数):这里需要的就是一个类型,当查到的时候会自动转变为这个类型,不过一般推荐Long,因为假如int范围不够了呢
        //注意这里使用的是queryForObject
        jdbcTemplate.queryForObject("select count(id) from account",Long.class);
        //加参数:jdbcTemplate.queryForObject("select count(id) from account where money>?",Long.class,1500);

    }
}

下面是实际开发中的写法,结合Spring使用


使用XML配置

1、bean.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--首先配置dao-->
    <bean id="accountDaoImpl" class="com.bean.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplateBean"></property>
    </bean>

    <!--配置数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplateBean" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>


</beans>

2、Account

package com.bean.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private Integer id;
    private String name;
    private Float money;

    public Account() {
    }

    public Account(Integer id, String name, Float money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Float getMoney() {
        return money;
    }

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

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}
  • AccountDaoImpl
package com.bean.dao.impl;

import com.bean.dao.IAccountDao;
import com.bean.domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;

public class AccountDaoImpl implements IAccountDao {

    private JdbcTemplate jdbcTemplate;

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

    public Account findById(Integer id) {

        List<Account> accounts = jdbcTemplate.query("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), id);

        return accounts.isEmpty()?null:accounts.get(0);
    }

    public Account findByName(String name) {

        List<Account> accounts = jdbcTemplate.query("select * form account where name=?", new BeanPropertyRowMapper<Account>(Account.class), name);

        //假如没有查到,返回null
        if (accounts.isEmpty()){
            return null;
        }
        //假如有多个,返回异常
        if (accounts.size()>1)
        {
            throw new RuntimeException("结果不唯一");
        }
        //否则返回唯一一个
        return accounts.get(0);
    }

    public void updateAccount(Account account) {
        jdbcTemplate.update("update account set name=?,money=?",account.getName(),account.getMoney());
    }
}
  • JDBCTemplateDemo
package com.bean.jdbcTemplate;

import com.bean.dao.IAccountDao;
import com.bean.dao.impl.AccountDaoImpl;
import com.bean.domain.Account;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class JdbcTemplateDemo {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        IAccountDao accountDao = (IAccountDao) context.getBean("accountDaoImpl");
        System.out.println(accountDao.findById(7));
    }
}

使用注解配置

  • SpringConfiguration
package config;

import com.bean.dao.impl.AccountDaoImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

@Configuration
@ComponentScan("com.bean")
public class SpringConfiguration {

//    声明dao
    @Bean
    public AccountDaoImpl accountDao(){
        return new AccountDaoImpl();
    }
//    声明JdbcTemplate
    @Bean
    public JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(dataSource());
    }
//    声明数据源
    @Bean
    public DriverManagerDataSource dataSource(){
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/spring");
        driverManagerDataSource.setUsername("root");
        driverManagerDataSource.setPassword("root");
        return driverManagerDataSource;
    }

}
  • Account
package com.bean.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private Integer id;
    private String name;
    private Float money;

    public Account() {
    }

    public Account(Integer id, String name, Float money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Float getMoney() {
        return money;
    }

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

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}
  • AccountDaoImpl
package com.bean.dao.impl;

import com.bean.dao.IAccountDao;
import com.bean.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class AccountDaoImpl implements IAccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

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

    public Account findById(Integer id) {

        List<Account> accounts = jdbcTemplate.query("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), id);

        return accounts.isEmpty()?null:accounts.get(0);
    }

    public Account findByName(String name) {

        List<Account> accounts = jdbcTemplate.query("select * form account where name=?", new BeanPropertyRowMapper<Account>(Account.class), name);

        //假如没有查到,返回null
        if (accounts.isEmpty()){
            return null;
        }
        //假如有多个,返回异常
        if (accounts.size()>1)
        {
            throw new RuntimeException("结果不唯一");
        }
        //否则返回唯一一个
        return accounts.get(0);
    }

    public void updateAccount(Account account) {
        jdbcTemplate.update("update account set name=?,money=?",account.getName(),account.getMoney());
    }
}
  • JDBCTemplateDemo
package com.bean.jdbcTemplate;

import com.bean.dao.IAccountDao;
import com.bean.dao.impl.AccountDaoImpl;
import com.bean.domain.Account;
import config.SpringConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class JdbcTemplateDemo {
    public static void main(String[] args) {
//        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        IAccountDao accountDao = (IAccountDao) context.getBean("accountDaoImpl");
        System.out.println(accountDao.findById(7));
    }
}

Spring中的事务控制

Spring控制我们需要明确的

1、javaEE体系分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案

2、Spring框架为我们提供了一组事务控制的接口,具体后面介绍,这组接口是在spring-tx-5.0.2.RELEASSE.jar中

3、Spring中的事务控制都是基于AOP的,它既可以使用编程的方式进行实现,也可以使用配置的方式进行实现

Spring中事务控制的API介绍

1、依赖

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

2、事务管理器

PlatformTransacionManager管理器,这是个事务接口,包含三个具体操作

  • 获取事务状态信息:TransactionStatus getTransaction(TransactionDefinition definition)
  • 提交事务:void commit(TransactionStatus status)
  • 回滚事务:void rollback(TransactionStatus status)

实现类

  • DataSourceTransactionManager
    • 包:org.springframework.jdbc.datasource.DataSourceTransactionManager
    • 使用Spring JDBC或者iBatis进行持久化数据时使用
  • HibernateTransactionManager
    • 包:org.springframework.orm.hibernate5.HibernateTransactionManager
    • 使用Hibernate版本进行持久化数据时使用

3、事务的定义信息

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

  • String getName():获取事务对象名称
  • int getIsolationLevel():获取事务隔离级别
    • ISOLATION_DEFAULT:默认级别,归属下列某一种
    • ISOLATION_READ_UNCOMMITTED:可以读取未提交数据
    • ISOLATION_READ_COMMITTED:只能读取已提交数据,解决脏读问题(Oracle默认级别)
    • ISOLATION_REPEATABLE_READ:是否读取其他事务提交修改后的数据,解决不可重复度的问题(MYSQL默认)
    • ISOLATION_SERIALIZABLE:是否读取其他事务提交添加后的数据,解决幻影读问题
  • int getPropagetionBehavior():获取事务传播行为
    • REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入到这个事务(默认),增删改的事务
    • SUPPORTS:支持当前事务,加入当前没有事务,就以非事务方式执行(没有事务),只有查询才能用的事务
    • MANDATORY:使用当前事务,如果当前没有事务,抛出异常
    • REQUERS_NEW:新建事务,如果当前在事务中,就把当前事务挂起
    • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
    • NEVER:以非事务方式运行,加入当前存在事务,抛出异常
    • NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行REQUIRED类似的操作
  • int getTimeout():获取事务超时时间:默认值为-1(没有超时时间)。假如有,则以秒为单位进行设置
  • boolean isReadOnly():获取事务是否只读:建议查询时设置为只读

4、事务的状态信息

TransactionStatus接口描述了某个时间点上事务的状态信息,包含有六个基本操作

  • 刷新事务:void flush()
  • 获取是否是存在存储点:boolean hasSavepoint()

事务是以节点来提交的,每一个节点都是一个储存点,当事务回滚的时候,只会回滚到上个节点,而不是从头来

  • 获取事务是否完成:boolean isCompleted()
  • 获取事务是否为新的事务:boolean isNewTransaction()
  • 获取事务是否回滚:boolean isRollbackOnly()
  • 设置事务回滚:void setRollbackOnly()

环境准备

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bean</groupId>
    <artifactId>SpringAOP</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

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

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
    </dependencies>
</project>

基于XML的事务控制

1、配置事务管理器:注入DataSource

2、配置事务通知

  • 注入事务的约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xsi:schemaLocation="
     http://www.springframework.org/schema/beans
     https://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/tx
     https://www.springframework.org/schema/tx/spring-tx.xsd
     http://www.springframework.org/schema/aop
     https://www.springframework.org/schema/aop/spring-aop.xsd">
  • 配置事务管理器的bean对象:包为:org.springframework.jdbc.datasource.DataSourceTransactionManager
  • 使用标签<tx:advice></tx:advice>标签配置事务通知
    • id:配置唯一标识
    • transaction-manager:事务管理器
  • <tx:advice></tx:advice>内部配置事务的属性
    • <tx:attributes></tx:attributes>
    • <tx:method>
  • name指定service中的方法
  • isolation

用于指定事物的隔离级别,默认值为DEFAULT,表示使用数据库的默认隔离级别

  • propagetion
  • 用于指定事物的传播行为,默认值为REQUIRED,表示一定会有事务,这是增删改的选择
  • 查询可以选择为SUPPORTS
  • read-only

指定事物是否只读,只有查询方法才能设置为true,默认值为false,表示读写

  • timeout

用于指定事物的超时时间,默认值为-1,表示永不超时,如果指定数值则以秒为单位

  • rollback-for

用于指定一个异常,碰到这个异常则回滚,其他异常不会滚。

没有默认值,表示任何异常都回滚

  • no-rollback-for

用于指定一个异常,碰到这个异常不回滚,其他异常都回滚。

没有默认值,表示任何异常都回滚

3、配置AOP的通用切入点表达式

4、建立事务通知和切入点表达式的对应关系:使用<aop:advisor></aop:advicor>


  • bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         https://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/tx
         https://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/aop
         https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="accountService" class="com.bean.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <bean id="accountDao" class="com.bean.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

    <!--上面是配的bean对象,下面开始编写事务-->

    <!--首先要有一个事务管理器,包为:org.springframework.jdbc.datasource.DataSourceTransactionManager
        事务管理器内要配置dataSource
    -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--现在配置事务的状态
        id 为唯一标识
        transaction-manager为配置事务管理器
    -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--配置事务属性-->
        <tx:attributes>
            <!--
                <tx:method></tx:method>
                    name:要配置的方法名称,比如我们这里用AOP配置的全都是service.impl包下的,所以
                        * :匹配全部的方法(虽然也就那么一个transer方法)
                        find*:将来假如有方法为find开头的时候,匹配折现方法
                        优先级:find* 高于 *
                    isolation:指定事物的隔离级别,默认值为DEFAULT,表示数据库的默认隔离级别
                    read-only:是否只读
                    propagation:指定事物的传播行为,默认为REQUIRED,为增删改的,查询可以为SUPPORTS
                    timeout:指定超时时间
                    rollback-for:用于指定一个异常,发生了此异常则回滚,其他异常不回滚。默认值无,表示任何异常都回滚。
                    no-rollback-for:指定一个异常,发生此异常不回滚,其余异常全都回滚。默认值无,表示任何异常都回滚。
            -->
            <tx:method name="*" read-only="false" propagation="REQUIRED"/>
            <tx:method name="find*" read-only="true" propagation="SUPPORTS"></tx:method>
        </tx:attributes>
    </tx:advice>


    <aop:config>
        <aop:pointcut id="pointCut" expression="execution(* com.bean.service.impl.*.*(..))"></aop:pointcut>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointCut"></aop:advisor>
    </aop:config>

</beans>
  • AccountDaoImpl
package com.bean.dao.impl;

import com.bean.dao.IAccountDao;
import com.bean.domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;

public class AccountDaoImpl implements IAccountDao {

    JdbcTemplate jdbcTemplate;

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

    public Account findById(Integer id) {
        List<Account> accounts = jdbcTemplate.query("select * from account where id=?",new BeanPropertyRowMapper<Account>(Account.class),id);
        return accounts.isEmpty()?null:accounts.get(0);
    }

    public void updateAccount(Account account) {
        jdbcTemplate.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
    }
}
  • AccountServiceImpl
package com.bean.service.impl;

import com.bean.dao.IAccountDao;
import com.bean.dao.impl.AccountDaoImpl;
import com.bean.domain.Account;
import com.bean.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao;

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

    public void transfer(Integer Transferer, Integer Payee, Float money) {
        System.out.println("transer");

        Account accountMinus = accountDao.findById(1);

        Account accountAdd = accountDao.findById(2);

        accountMinus.setMoney(accountMinus.getMoney()-money);

        accountAdd.setMoney(accountAdd.getMoney()+money);

        accountDao.updateAccount(accountMinus);
//        int i = 1/0;
        accountDao.updateAccount(accountAdd);
    }
}
  • 测试
import com.bean.dao.impl.AccountDaoImpl;
import com.bean.service.IAccountService;
import com.bean.service.impl.AccountServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

@SpringJUnitConfig
public class TransactionTest {
    @Test
    public void Test(){

        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");

        IAccountService accountService = (IAccountService) context.getBean("accountService");

        accountService.transfer(1,2,100F);
    }
}
  • 效果

效果就是:

  • 当没有报错时:转账正常执行
  • 当出现错误时:回滚

基于注解的事务控制

1、导入关于context的名称空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         https://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/tx
         https://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/aop
         https://www.springframework.org/schema/aop/spring-aop.xsd
         http://www.springframework.org/schema/context
         https://www.springframework.org/schema/context/spring-context.xsd">

2、配置事务管理器

3、开启Spring对注解的支持,使用<tx:annotation-drivern/>

4、在需要事务支持的时候使用@Transactional注解


  • bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         https://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/tx
         https://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/aop
         https://www.springframework.org/schema/aop/spring-aop.xsd
         http://www.springframework.org/schema/context
         https://www.springframework.org/schema/context/spring-context.xsd">

    <!--首先配置一下spring创建容器时要扫描的包-->
    <context:component-scan base-package="com.bean"></context:component-scan>


    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--
        使用注解进行事务控制的配置
        1. 配置事务管理器
        2. 开启spring对注解事务的支持:使用<tx:annotation-driven/>,里面配置事务管理器
        3. 在需要事务支持的时候使用@Transactional注解
    -->


    <!--事务管理器还得留下-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--开启spring对注解事务的支持-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

</beans>
  • AccountDaoImpl
package com.bean.dao.impl;

import com.bean.dao.IAccountDao;
import com.bean.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

import java.util.List;

@Component
public class AccountDaoImpl implements IAccountDao {

    @Autowired
    JdbcTemplate jdbcTemplate;

    public Account findById(Integer id) {
        List<Account> accounts = jdbcTemplate.query("select * from account where id=?",new BeanPropertyRowMapper<Account>(Account.class),id);
        return accounts.isEmpty()?null:accounts.get(0);
    }

    public void updateAccount(Account account) {
        jdbcTemplate.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
    }
}
  • AccountServiceImpl
package com.bean.service.impl;

import com.bean.dao.IAccountDao;
import com.bean.dao.impl.AccountDaoImpl;
import com.bean.domain.Account;
import com.bean.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)  //这里是配置事务控制的注解的,配置的全局为只读型的配置
public class AccountServiceImpl implements IAccountService {

    @Autowired
    private IAccountDao accountDao;

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


    //这里单独配置,配置可写型的配置
    @Transactional(propagation = Propagation.REQUIRED,readOnly = false)
    public void transfer(Integer Transferer, Integer Payee, Float money) {
        System.out.println("transer");

        Account accountMinus = accountDao.findById(1);

        Account accountAdd = accountDao.findById(2);

        accountMinus.setMoney(accountMinus.getMoney()-money);

        accountAdd.setMoney(accountAdd.getMoney()+money);

        accountDao.updateAccount(accountMinus);
//        int i = 1/0;
        accountDao.updateAccount(accountAdd);
    }
}
  • 测试
import com.bean.dao.impl.AccountDaoImpl;
import com.bean.service.IAccountService;
import com.bean.service.impl.AccountServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class TransactionTest {

    @Autowired
    private  IAccountService accountService;

    
    @Test
    public void Test(){
        accountService.transfer(1,2,100F);
    }
}

事务回滚规则

默认只接受不受检查(Error和RuntimeException),但是可以利用rollbackFor的参数(类的class数组)指定遇到这个异常来回滚

可以通过noRollbackFor来定义参数(类的class数组)来指定遇到异常不回滚


Spring5新特性介绍

与JDK相关的升级

1、JDK版本要求

spring5.02017-9发布了GA(通用)版本,该版本基于jdk8编写的,所以jdk8之前的版本无法使用

同时tomcat要求8.5以上

我们使用jdk8构建工程,可以降低版本编译,但是不能使用jdk8以下版本构建工程

IDE同时需要更新,eclipse 4.7.2之前的就甭想了

2、升级说明

1、基于JDK8的反射增强

2、@NonNu11注解和@Nullable注解的使用

用@Nullable和@NotNull注解来显示表明可为空的参数和以及返回值。

这样就够在编译的时候理空值而不是在运行时抛出NullPointerExceptions。

3、日志记录方面

Spring Framework 5.0带来了Commons Logging 桥接模块的封装,它被叫做spring-jcl 而不是标准的Commons Logging。

当然,无需任何额外的桥接,新版本也会对Log4j2.x,SLF4J,JUL( java.util. logging) 进行自动检测。

核心容器的升级

Spring Framework 5.0现在支持候选组件索引作为类路径扫描的替代方案。

该功能已经在类路径扫描器中添加,以简化添加候选组件标识的步骤。

应用程序构建任务可以定义当前项目自己的META-INE/spring.components文件。

在编译时,源模型是自包含的,JPA实体和Spring组件是已被标记的。

从索引读取实体而不是扫描类路径对于小于200 个类的小型项目是没有明显差异,但对大型项目影响较大。

加载组件索引开销更低。因此,随着类数的增加,索引读取的启动时间将保持不变。

加载组件索引的耗费是廉价的。因此当类的数量不断增长,加上构建索引的启动时间仍然可以维持一一个常数,

不过对于组件扫描而言,启动时间则会有明显的增长。

这个对于我们处于大型spring项目的开发者所意味着的,是应用程序的启动时间将被大大缩减。

虽然20或者30秒钟看似没什么,但如果每天要这样登上好几百次,加起来就够你受的了。使用了组件索引的话,就能帮

助你每天过的更加高效。

你可以在Spring的Jira上了解更多关于组件索引的相关信息。

JetBarins Kotlin语言支持

这个不多赘述

响应式编程风格

此次spring发行版本的一个激动人心的特性就是新的响应式堆栈WEB框架。

这个堆栈完全的响应阻塞,适合于事件循环风格的处理,可以进行少量线程的扩展。

Junit5支持

完全支持JUnit 5 Jupiter, 所以可以使用JUnit 5来编写测试以及扩展。

此外还提供了一个编程以及扩展模型,Jupiter子项目提供了一个测试引擎来在Spring上运行基于 Jupiter的测试。

另外,spring Framework 5还提供了在Spring TestContext Framework 中进行并行测试的扩展。

针对响应式编程模型,spring-test 现在还引入了支持spring webFlux的WebTestclient 集成测试的支持,类似于MockMvc,并不需要一一个运行着的服务端。

使用一个模拟的请求或者响应,WebTestClient就可以直接绑定到webFlux服务端设施。

你可以在这里找到这个激动人心的TestContext 框架所带来的增强功能的完整列表。

依赖库的更新

终止支持的类库:

  • Portlet .
  • Velocity .
  • JasperReports .
  • XMLBeans .
  • JDO.
  • Guava .

支持的类库:

  • Jackson 2.6+
  • EhCache 2.10+ 1 3.0 GA
  • Hibernate 5.0+
  • JDBC 4.0+
  • XmlUnit 2 .x+
  • OkHttp 3.X+
  • Netty 4.1+

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。