1 概述
Spring
为开发者提供了JDBCTemplate
,能够简化不少数据库操做相关的代码,本文主要介绍JDBCTemplate
的使用以及事务管理功能。java
2 JDBC Template
2.1 配置
配置的话主要配置如下几项:mysql
- 数据源:
org.springframework.jdbc.datasource.DriverManager.DataSource
- 数据库驱动:
com.cj.mysql.jdbc.Driver
,这里采用的是MySQL 8
,注意MySQL 5.7
如下的驱动名字不一样,另外如果其余数据库请对应修改 - 数据库
URL
:jdbc:mysql://localhost:3306/test
,MySQL
默认的3306
端口,数据库test
- 数据库用户名
- 数据库密码
JDBC
模板:org.springframework.jdbc.core.jdbcTemplate
参考配置以下:git
<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 依赖
首先导入依赖:github
<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
的版本请根据我的须要更改,或使用其余数据库的驱动。spring
2.3.2 配置文件
完整配置文件以下:sql
<?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
的话会提示不推荐:express
因此利用了Lombok
的注解@RequiredArgsConstructor
,效果至关以下构造方法,只不过是简化了一点:编程
@Autowired public TestDao(JdbcTemplate template) { this.template = template; }
2.3.5 测试
测试以前先建表:app
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
中的事务管理有两种方法:
- 编程式事务管理:代码中显式调用
beginTransaction
、commit
、rollback
等就是编程式事务管理 - 声明式事务管理:经过
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
实现
根据PlatformTransactionManager
、TransactionDefinition
、TransactionStatus
几个核心接口,经过编程方式进行事务管理,首先配置事务管理器:
<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)
开启 - 执行业务方法
- 根据业务方法是否出现异常手动调用
DataSourceTransaction
的commit(TransactionStatus)
进行提交 - 出现异常调用
rollback(TransactionStatus)
进行回滚
测试以下:
3.1.2 基于TransactionTemplate
步骤:
- 经过调用
TransactionTemplate
的execute
实现 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
版:
Kotlin
版: