1 概述

Spring为开发者提供了JDBCTemplate,能够简化很多数据库操作相干的代码,本文次要介绍JDBCTemplate的应用以及事务管理性能。

2 JDBC Template

2.1 配置

配置的话次要配置以下几项:

  • 数据源:org.springframework.jdbc.datasource.DriverManager.DataSource
  • 数据库驱动:com.cj.mysql.jdbc.Driver,这里采纳的是MySQL 8,留神MySQL 5.7以下的驱动名字不同,另外若是其余数据库请对应批改
  • 数据库URLjdbc:mysql://localhost:3306/testMySQL默认的3306端口,数据库test
  • 数据库用户名
  • 数据库明码
  • JDBC模板:org.springframework.jdbc.core.jdbcTemplate

参考配置如下:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>    <property name="url" value="jdbc:mysql://localhost:3306/test"/>    <property name="username" value="test"/>     <property name="password" value="test"/></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">    <property name="dataSource" ref="dataSource"/></bean><context:component-scan base-package="pers.dao"/>

2.2 罕用办法

  • int update(String sql,Object args[]):增/删/改操作,应用args设置其中的参数,返回更新的行数
  • List<T> query(String sql,RowMapper<T> rowMapper,Object []args):查问操作,rowMapper将后果集映射到用户自定义的类中

2.3 示例

2.3.1 依赖

首先导入依赖:

<dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-jdbc</artifactId>    <version>5.2.9.RELEASE</version></dependency><dependency>    <groupId>mysql</groupId>    <artifactId>mysql-connector-java</artifactId>    <version>8.0.21</version></dependency>

MySQL的版本请依据集体须要更改,或应用其余数据库的驱动。

2.3.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:context="http://www.springframework.org/schema/context"       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/context        https://www.springframework.org/schema/context/spring-context.xsd">    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>        <property name="url" value="jdbc:mysql://localhost:3306/test"/>        <property name="username" value="test"/>        <property name="password" value="test"/>    </bean>    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">        <property name="dataSource" ref="dataSource"/>    </bean>    <context:component-scan base-package="pers.dao"/></beans>

2.3.3 实体类

public class MyUser {    private Integer id;    private String uname;    private String usex;}

2.3.4 数据拜访层

增加@Repository以及@RequiredArgsConstructor

@Repository@RequiredArgsConstructor(onConstructor = @__(@Autowired))public class TestDao {    private final JdbcTemplate template;    public int update(String sql,Object[] args)    {        return template.update(sql,args);    }    public List<MyUser> query(String sql, Object[] args)    {        RowMapper<MyUser> mapper = new BeanPropertyRowMapper<>(MyUser.class);        return template.query(sql,mapper,args);    }}

因为间接应用@Autowired的话会提醒不举荐:

所以利用了Lombok的注解@RequiredArgsConstructor,成果相当如下构造方法,只不过是简化了一点:

@Autowiredpublic TestDao(JdbcTemplate template){    this.template = template;}

2.3.5 测试

测试之前先建表:

create table MyUser(    id INT AUTO_INCREMENT PRIMARY KEY ,    uname varchar(20),    usex varchar(20))

测试类:

public class Main {    public static void main(String[] args) {        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");        TestDao dao = (TestDao)context.getBean("testDao");        String insertSql = "insert into MyUser(uname,usex) values(?,?)";        String[] param1 = {"chenhengfa1","男"};        String[] param2 = {"chenhengfa2","男"};        String[] param3 = {"chenhengfa3","男"};        String[] param4 = {"chenhengfa4","男"};        dao.update(insertSql,param1);        dao.update(insertSql,param2);        dao.update(insertSql,param3);        dao.update(insertSql,param4);        String selectSql = "select * from MyUser";        List<MyUser> list = dao.query(selectSql,null);        for(MyUser mu:list)        {            System.out.println(mu);        }    }}

输入:

如果出现异常或插入不胜利等其余状况,请查看SQL语句是否编写正确,包含表名以及字段名。

3 事务管理

Spring中的事务管理有两种办法:

  • 编程式事务管理:代码中显式调用beginTransactioncommitrollback等就是编程式事务管理
  • 申明式事务管理:通过AOP实现,不须要通过编程形式治理事务,因而不须要再业务逻辑代码中掺杂事务处理的代码,开发更加简略,便于前期保护

上面先来看一下编程式事务管理的实现。

3.1 编程式事务管理

编程式事务管理的配置又有两种办法:

  • 基于底层API
  • 基于TransactionTemplate

须要的依赖如下:

<dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-tx</artifactId>    <version>5.2.9.RELEASE</version></dependency><dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-expression</artifactId>    <version>5.2.9.RELEASE</version></dependency><dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-aop</artifactId>    <version>5.2.9.RELEASE</version></dependency><dependency>    <groupId>org.aspectj</groupId>    <artifactId>aspectjweaver</artifactId>    <version>1.9.6</version>    <scope>runtime</scope></dependency><dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-aspects</artifactId>    <version>5.2.9.RELEASE</version></dependency>

3.1.1 底层API实现

依据PlatformTransactionManagerTransactionDefinitionTransactionStatus几个外围接口,通过编程形式进行事务管理,首先配置事务管理器:

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

接着批改数据库拜访类:

@Repository@RequiredArgsConstructor(onConstructor = @__(@Autowired))public class TestDao {    private final JdbcTemplate template;    private final DataSourceTransactionManager manager;    public int update(String sql,Object[] args)    {        return template.update(sql,args);    }    public List<MyUser> query(String sql,Object[] args)    {        RowMapper<MyUser> mapper = new BeanPropertyRowMapper<>(MyUser.class);        return template.query(sql,mapper,args);    }    public void testTransaction()    {        TransactionDefinition definition = new DefaultTransactionDefinition();        TransactionStatus status = manager.getTransaction(definition);        String message = "执行胜利,没有事务回滚";        try        {            String sql1 = "delete from MyUser";            String sql2 = "insert into MyUser(id,uname,usex) values(?,?,?)";            Object [] param2 = {1,"张三","男"};            template.update(sql1);            template.update(sql2,param2);            template.update(sql2,param2);            manager.commit(status);        }        catch (Exception e)        {            e.printStackTrace();            manager.rollback(status);            message = "主键反复,事务回滚";        }        System.out.println(message);    }}
3.1.1.1 事务定义

TransactionDefinition是事务定义,是一个接口:

次要定义了:

  • 事务隔离级别
  • 事务流传行为
  • 事务超时工夫
  • 是否为只读事务

DefaultTransactionDefinition就是下面属性的一些默认配置,比方:

也就是定义了:

  • 流传行为为0:也就是常量PROPAGATION_REQUIREDE,示意如果以后存在一个事务,则退出以后事务,如果不存在任何事务,就创立一个新事务
  • 隔离级别为-1:这个也是TransactionDefinition的默认参数,示意应用数据库的默认隔离级别,通常状况下为Read Committed
  • 超时为-1:默认设置不超时,如须要设置超时请调用setTimeout办法,比方如果设置为了60,那么相当于如果操作工夫超过了60s,而且前面还波及到CRUD操作,那么会抛出超时异样并回滚,如果超时操作的前面没有波及到CRUD操作,那么不会回滚
  • 只读事务为false:默认为false,然而该变量不是表明“不能”进行批改等操作,而是一种暗示,如果不蕴含批改操作,那么JDBC驱动和数据库就有可能针对该事务进行一些特定的优化
3.1.1.2 具体执行流程

具体执行流程如下:

  • 定义事务:实例类为DefaultTransactionDefinition
  • 开启事务:通过getTransaction(TransactionDefinition)开启
  • 执行业务办法
  • 依据业务办法是否出现异常手动调用DataSourceTransactioncommit(TransactionStatus)进行提交
  • 出现异常调用rollback(TransactionStatus)进行回滚

测试如下:

3.1.2 基于TransactionTemplate

步骤:

  • 通过调用TransactionTemplateexecute实现
  • execute承受一个TransactionCallback接口参数
  • TransactionCallback定义了一个doInTransaction办法
  • 通常以匿名外部类的形式实现TransactionCallback接口,在其中的doInTransaction编写业务逻辑代码
  • doInTransaction有一个TransactionStatus的参数,能够调用setRollbackOnly进行回滚

默认的回滚规定如下:

  • 如果抛出未查看异样或者手动调用setRollbackOnly,则回滚
  • 如果执行实现或抛出查看异样,则提交事务

示例如下,首先编写配置文件对Bean进行注入:

<!--事务管理器--><bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dataSource"/></bean><!--事务模板--><bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">    <property name="transactionManager" ref="txManager"/></bean>

其次批改数据拜访类,增加一个测试方法:

public void testTransactionTemplate(){    System.out.println(transactionTemplate.execute((TransactionCallback<Object>) transactionStatus -> {        String deleteSql = "delete from MyUser";        String insertSql = "insert into MyUser(id,uname,usex) values(?,?,?)";        Object[] parm = {1, "张三", "男"};        try {            template.update(deleteSql);            template.update(insertSql, parm);            template.update(insertSql, parm);        } catch (Exception e) {            message = "主键反复,事务回滚";            e.printStackTrace();        }        return message;    }));}

大部分代码与第一个例子相似就不解释了,后果也是因为主键反复出现异常,造成事务回滚:

3.2 申明式事务管理

Spring申明式事务管理通过AOP实现,实质是在办法前后进行拦挡,在指标办法开始之前创立或退出一个事务,执行指标办法实现之后依据执行状况提交或回滚事务。相比起编程式事务管理,申明式最大的长处就是不须要通过编程的形式治理事务,业务逻辑代码无需混淆事务代码,然而惟一有余的中央就是最细粒度只能作用到办法上,而不能做到代码块级别。

实现形式有如下两种:

  • 基于XML实现
  • 基于@Transactional实现

3.2.1 基于XML

Spring提供了tx命令空间来配置事务:

  • <tx:advice>:配置事务告诉,个别须要指定id以及transaction-manager
  • <tx:attributes>:配置多个<tx:method>指定执行事务的细节
3.2.1.1 配置文件

残缺配置文件如下:

<?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:context="http://www.springframework.org/schema/context"       xmlns:aop="http://www.springframework.org/schema/aop"       xmlns:cache="http://www.springframework.org/schema/cache"       xmlns:tx="http://www.springframework.org/schema/tx"       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans.xsd       http://www.springframework.org/schema/context       https://www.springframework.org/schema/context/spring-context.xsd       http://www.springframework.org/schema/cache       http://www.springframework.org/schema/cache/spring-cache.xsd       http://www.springframework.org/schema/tx       http://www.springframework.org/schema/tx/spring-tx.xsd       http://www.springframework.org/schema/aop       http://www.springframework.org/schema/aop/spring-aop.xsd">    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>        <property name="url" value="jdbc:mysql://localhost:3306/test"/>        <property name="username" value="test"/>        <property name="password" value="test"/>    </bean>    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">        <property name="dataSource" ref="dataSource"/>    </bean>    <context:component-scan base-package="pers.dao"/>    <!--事务管理器-->    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dataSource"/>    </bean>    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">        <property name="transactionManager" ref="txManager"/>    </bean>    <!--申明式事务-->    <tx:advice id="myAdvice" transaction-manager="txManager">        <tx:attributes>            <!--任意办法-->            <tx:method name="*" />        </tx:attributes>    </tx:advice>    <!--aop配置,具体能够看笔者之前的文章-->    <aop:config>        <!--定义切点,执行testXMLTranscation()时进行加强-->        <aop:pointcut id="txPointCut" expression="execution(* pers.dao.TestDao.testXMLTransaction())"/>        <!--切面-->        <aop:advisor advice-ref="myAdvice" pointcut-ref="txPointCut"/>    </aop:config></beans>
3.2.1.2 测试

测试方法如下:

public void testXMLTransaction(){    String deleteSql = "delete from MyUser";    String saveSql = "insert into MyUser(id,uname,usex) values(?,?,?)";    Object [] parm = {1,"张三","男"};    template.update(deleteSql);    template.update(saveSql,parm);    template.update(saveSql,parm);}

运行后果:

能够看到提醒主键反复了。

3.2.2 基于@Transactional

@Transactional个别作用于类上,使得该类所有public办法都具备该类型的事务属性。上面创立一个示例。

3.2.2.1 配置文件

将上一个例子中的<aop:config>以及<tx:advice>正文掉,同时增加:

<!--事务管理的注解驱动器--><tx:annotation-driven transaction-manager="txManager"/>

3.2.2.2 测试

测试方法与上一个例子统一,后果也是如此:

4 参考源码

Java版:

  • Github
  • 码云
  • CODE.CHINA

Kotlin版:

  • Github
  • 码云
  • CODE.CHINA