目录
4.1.1 PlatformTransactionManager接口
一、JDBC模板技术概述
1.1 什么是JDBC模板
Spring框架提供的JdbcTemplate类简化了传统JDBC开发流程,解决了以下痛点:
-
自动管理连接资源(Connection/Statement/ResultSet)
-
自动处理JDBC异常(将Checked异常转换为RuntimeException)
-
提供简洁的API执行SQL语句
-
支持事务管理
二、JdbcTemplate使用实战
2.1 基础使用(手动创建对象)
1. 环境搭建(创建 maven 工程,引入坐标依赖)
<?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.qcby</groupId>
<artifactId>springAOP03</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</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>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</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>
<!-- 三、Druid开源的连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
</dependencies>
</project>
2. 测试代码
package com.qcby.demo1;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import java.util.List;
import java.util.Map;
public class Demo1 {
/**
* 一、使用 new 对象方式完成
*/
@Test
public void run1(){
// 创建连接池对象,Spring 框架内置了连接池对象
DriverManagerDataSource dataSource = new DriverManagerDataSource();
// 设置 4 个参数
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring_db");
dataSource.setUsername("root");
dataSource.setPassword("12345");
try {
// 提供模板,创建对象
JdbcTemplate template = new JdbcTemplate(dataSource);
// 完成数据的增删改查
//1.新增
// template.update("insert into account values (null,?,?)","耶耶 ",1000);
// System.out.println("添加成功!");
//2.删除
// template.update("delete from account where name = ? ","耶耶");
// System.out.println("删除成功!");
//3.更新
// template.update("update account set money=money-? where name = ? ",100.00,"可可");
// System.out.println("更新成功!");
//4.查询
List<Map<String, Object>> accounts=template.queryForList("select * from account");
//遍历结果集
System.out.println("\n当前账户信息:");
for (Map<String, Object> account:accounts){
System.out.println(
"ID:"+ account.get("id") +
";姓名:" + account.get("name") +
";余额:" + account.get("money")
);
}
}catch (Exception e){
System.out.println("操作失败!!!");
e.printStackTrace();
}
}
}
2.2 使用Spring管理模板类
1. 将数据源和模板类交给Spring容器管理
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 使用Spring框架管理 -->
<!-- 二、Spring管理内置的连接池 demo1_1.java测试-->
<!-- 配置连接池 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///spring_db" />
<property name="username" value="root" />
<property name="password" value="12345" />
</bean>
<!--配置 jdbc 模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
2. 测试代码
package com.qcby.demo1;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
import java.util.Map;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext_jdbc.xml")
public class Demo1_1 {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 测试的方式
*/
@Test
public void run1(){
try {
jdbcTemplate.update("insert into account values (null,?,?)","图图",500);
System.out.println("添加成功!");
}catch (Exception e){
e.printStackTrace();
}
}
@Test
public void run2(){
try {
jdbcTemplate.update("delete from account where name = ? ","图图");
System.out.println("删除成功!");
}catch (Exception e){
e.printStackTrace();
}
}
@Test
public void run3(){
try {
jdbcTemplate.update("update account set money=money-? where name = ? ",100.00,"可可");
System.out.println("更新成功!");
}catch (Exception e){
e.printStackTrace();
}
}
@Test
public void run4(){
try {
List<Map<String, Object>> accounts=jdbcTemplate.queryForList("select * from account");
//遍历结果集
System.out.println("\n当前账户信息:");
for (Map<String, Object> account:accounts){
System.out.println(
"ID:"+ account.get("id") +
";姓名:" + account.get("name") +
";余额:" + account.get("money")
);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
2.3 使用开源连接池(Druid)
1. 添加Druid依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
2. 创建jdbc.properties
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_db
jdbc.username=root
jdbc.password=12345
3. Spring配置
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 三、Spring 框架管理开源的连接池 属性文件:jdbc.properties-->
<!-- 第一种写法 -->
<!-- 使用开源连接池
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///spring_db" />
<property name="username" value="root" />
<property name="password" value="12345" />
</bean>
-->
<!-- 加载属性文件
<bean id="placeholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean>
-->
<!-- 第二种写法:使用提供标签的方式 -->
<!-- 引入属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 加载属性的文件 配置Druid数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 配置 jdbc 模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入属性,set方法 -->
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
4. 测试
package com.qcby.demo1;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext_jdbc.xml")
public class Demo1_2 {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 测试的方式
*/
@Test
public void run1(){
jdbcTemplate.update("insert into account values (null,?,?)","熊四",800);
System.out.println("新增成功!");
}
/**
* 修改
*/
@Test
public void run2(){
jdbcTemplate.update("update account set name = ?,money = ? where id = ?","小凤",100,7);
System.out.println("修改成功!");
}
/**
* 删除
*/
@Test
public void run3(){
jdbcTemplate.update("delete from account where id = ?",9);
System.out.println("删除成功!");
}
/**
* 通过 id 查询
*/
@Test
public void run4(){
Account account = jdbcTemplate.queryForObject("select * from account where id = ?", new BeanMapper(), 6);
System.out.println(account);
}
/**
* 查询所有的数据
*/
@Test
public void run5(){
List<Account> list = jdbcTemplate.query("select * from account", new BeanMapper());
for (Account account : list) {
System.out.println(account);
}
}
}
/**
* 实现类,用来进行数据封装的
*/
class BeanMapper implements RowMapper<Account> {
/**
* 是一行一行进行数据封装的
* @param resultSet
* @param i
* @return
* @throws SQLException
*/
public Account mapRow(ResultSet resultSet, int i) throws SQLException {
Account account = new Account();
account.setId(resultSet.getInt("id"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getDouble("money"));
return account;
}
}
三、模拟转账开发
3.1 基础实现
3.1.1 Service层
package com.qcby.demo2.service;
public interface AccountService {
/**
* 转账的方法
* @param out 付款人
* @param in 收款人
* @param money 金额
*/
public void pay(String out,String in,double money);
}
package com.qcby.demo2.service;
import com.qcby.demo2.dao.AccountDao;
public class AccountServiceImpl implements AccountService{
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
/**
* 转账方法
* @param out 付款人
* @param in 收款人
* @param money 金额
*/
public void pay(String out, String in, double money) {
// 调用 dao 方法
accountDao.outMoney(out,money);
accountDao.inMoney(in,money);
}
}
3.1.2 Dao层
package com.qcby.demo2.dao;
public interface AccountDao {
/**
* 付款
* @param out
* @param money
*/
public void outMoney(String out,double money);
/**
* 收款
* @param in
* @param money
*/
public void inMoney(String in,double money);
}
package com.qcby.demo2.dao;
import org.springframework.jdbc.core.JdbcTemplate;
public class AccountDaoImpl implements AccountDao{
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* 付款
* @param out
* @param money
*/
public void outMoney(String out, double money) {
jdbcTemplate.update("update account set money = money - ? where name = ?",money,out);
}
/**
* 收款
* @param in
* @param money
*/
public void inMoney(String in, double money) {
jdbcTemplate.update("update account set money = money + ? where name = ?",money,in);
}
}
3.1.3 配置文件
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--第二种写法:使用提供标签的方式-->
<context:property-placeholder location="classpath:jdbc.properties" />
<!--加载属性的文件-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" 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>
<!--配置 service-->
<bean id="accountService" class="com.qcby.demo2.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!--配置 service-->
<bean id="accountDao" class="com.qcby.demo2.dao.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
</beans>
3.1.4 测试代码
package com.qcby.demo2;
import com.qcby.demo2.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext_dao1.xml")
public class Demo2 {
@Autowired
private AccountService accountService;
/**
* 测试转账的方法
*/
@Test
public void testPay(){
accountService.pay("熊大","熊二",100);
}
}
3.2 使用JdbcDaoSupport实现
3.2.1 DAO层改进
package com.qcby.demo2.dao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
//extends JdbcDaoSupport
public class AccountDaoImpl1 extends JdbcDaoSupport implements AccountDao{
/**
* 付款
* @param out
* @param money
*/
public void outMoney(String out, double money) {
this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,out);
}
/**
* 收款
* @param in
* @param money
*/
public void inMoney(String in, double money) {
this.getJdbcTemplate().update("update account set money = money + ? where name = ?",money,in);
}
}
3.2.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--第二种写法:使用提供标签的方式-->
<context:property-placeholder location="classpath:jdbc.properties" />
<!--加载属性的文件-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" 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>
-->
<!--配置 service-->
<bean id="accountService" class="com.qcby.demo2.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!--配置 dao
<bean id="accountDao" class="com.qcby.demo2.dao.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
-->
<!-- 在accountDaoImpl中extends JdbcDaoSupport -->
<bean id="accountDao" class="com.qcby.demo2.dao.AccountDaoImpl1">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
3.2.3 测试代码
package com.qcby.demo2;
import com.qcby.demo2.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext_dao2.xml")
public class Demo2_1 {
@Autowired
private AccountService accountService;
/**
* 测试转账的方法
*/
@Test
public void testPay(){
accountService.pay("熊大","熊二",100);
}
}
四、Spring框架的事务管理
4.1 事务管理相关API
4.1.1 PlatformTransactionManager接口
这是Spring事务管理的核心接口,根据不同的持久层框架有不同的实现:
-
DataSourceTransactionManager
:用于JDBC -
HibernateTransactionManager
:用于Hibernate -
JpaTransactionManager
:用于JPA
4.1.2 TransactionDefinition接口
定义事务属性:
-
事务隔离级别
-
事务传播行为
-
事务超时时间
-
是否只读事务
4.2 声明式事务管理
4.2.1 配置文件方式
<!-- applicationContext_1.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--第二种写法:使用提供标签的方式-->
<context:property-placeholder location="classpath:jdbc.properties" />
<!--加载属性的文件-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--配置事务的通知(没有自己编写切面类,通知方法也不是自己编写,Spring框架提供的)-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--对 pay 进行增强,设置隔离级别,传播行为,超时的时间-->
<tx:method name="pay" isolation="DEFAULT" propagation="REQUIRED" />
<tx:method name="find*" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 配置 AOP 的增强 -->
<aop:config>
<!-- Spring 框架提供系统通知,使用 advisor 标签 -->
<aop:advisor advice-ref="txAdvice" pointcut="execution(public * com.qcby.demo3.AccountServiceImpl.pay(..))" />
</aop:config>
<!-- 配置 service -->
<bean id="accountService" class="com.qcby.demo3.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- 配置 dao -->
<bean id="accountDao" class="com.qcby.demo3.AccountDaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
4.2.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"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--开启注解的扫描-->
<context:component-scan base-package="com.qcby.demo3" />
<!--第二种写法:使用提供标签的方式-->
<context:property-placeholder location="classpath:jdbc.properties" />
<!--加载属性的文件-->
<bean id="dataSource"
class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName"
value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!--配置平台事务管理器-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--配置 Jdbc 模板类-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<!--开启事务注解的支持-->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
package com.qcby.demo3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(isolation = Isolation.DEFAULT)
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountDao accountDao;
/**
* 转账方法
* @param out 付款人
* @param in 收款人
* @param money 金额
*/
public void pay(String out, String in, double money) {
// 调用 dao 方法
accountDao.outMoney(out,money);
// int a = 10 / 0;
accountDao.inMoney(in,money);
// 模拟在转账过程中发生异常
//int a = 10 / 0;
}
}
4.2.3 纯注解方式
package com.qcby.demo3;
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.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.annotation.Resource;
import javax.sql.DataSource;
/**
* 配置类
* @author Administrator
*/
@Configuration
@ComponentScan(basePackages="com.qcby.demo3")
@EnableTransactionManagement // 开启事务注解
public class SpringConfig {
/**
* @return
* @throws Exception
*/
@Bean(name="dataSource")
public DataSource createDataSource() throws Exception{
// 创建连接池对象,Spring 框架内置了连接池对象
DriverManagerDataSource dataSource = new DriverManagerDataSource();
// 设置 4 个参数
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring_db");
dataSource.setUsername("root");
dataSource.setPassword("12345");
return dataSource;
}
/**
* 创建模板对象
* @return
*/
@Resource(name="dataSource") // 不仅可以作用在属性上,也可以作用方法上。
@Bean(name="jdbcTemplate") // 把 JdbcTemplate 保存到 IOC容器中
public JdbcTemplate createJdbcTemplate(DataSource dataSource){
JdbcTemplate template = new JdbcTemplate(dataSource);
return template;
}
/**
* 创建平台事务管理器对象
* @param dataSource
* @return
*/
@Resource(name="dataSource")
@Bean(name="transactionManager")
public PlatformTransactionManager
createTransactionManager(DataSource dataSource){
DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource);
return manager;
}
}
4.3 转账案例与事务管理
4.3.1 Service层
package com.qcby.demo3;
public interface AccountService {
public void pay(String out, String in, double money);
}
package com.qcby.demo3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(isolation = Isolation.DEFAULT)
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountDao accountDao;
/**
* 转账方法
* @param out 付款人
* @param in 收款人
* @param money 金额
*/
public void pay(String out, String in, double money) {
// 调用 dao 方法
accountDao.outMoney(out,money);
// int a = 10 / 0;
accountDao.inMoney(in,money);
// 模拟在转账过程中发生异常
//int a = 10 / 0;
}
}
4.3.2 Dao层
package com.qcby.demo3;
public interface AccountDao {
/**
* 付款
* @param out
* @param money
*/
public void outMoney(String out, double money);
/**
* 收款
* @param in
* @param money
*/
public void inMoney(String in, double money);
}
package com.qcby.demo3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 付款
* @param out
* @param money
*/
public void outMoney(String out, double money) {
jdbcTemplate.update("update account set money = money - ? where name = ?",money,out);
}
/**
* 收款
* @param in
* @param money
*/
public void inMoney(String in, double money) {
jdbcTemplate.update("update account set money = money + ? where name = ?",money,in);
}
}
4.3.3 测试代码
package com.qcby.demo3;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
// 1.配置文件+注解方式
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext_2.xml")
// 2.纯注解方式
//@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(classes = SpringConfig.class)
public class Demo3 {
@Autowired
private com.qcby.demo3.AccountService accountService;
/**
* 测试转账的方法
*/
@Test
public void testPay() {
try {
accountService.pay("熊大", "熊二", 100);
System.out.println("转账成功!!!");
} catch (Exception e) {
e.printStackTrace();
System.out.println("转账失败,事务已回滚!");
}
}
}
4.4 事务传播行为
Spring定义了7种事务传播行为:
-
PROPAGATION_REQUIRED(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
-
PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
-
PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
-
PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
-
PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
-
PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
-
PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。
4.5 事务隔离级别
Spring支持以下隔离级别:
-
ISOLATION_DEFAULT:使用底层数据库的默认隔离级别。
-
ISOLATION_READ_UNCOMMITTED:允许读取未提交的数据变更,可能导致脏读、幻读或不可重复读。
-
ISOLATION_READ_COMMITTED:只能读取已提交的数据,防止脏读,但可能发生幻读和不可重复读。
-
ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据被当前事务修改,防止脏读和不可重复读,但可能发生幻读。
-
ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,确保事务串行执行,防止脏读、不可重复读和幻读。
总结
本文详细介绍了Spring框架中JDBC模板技术的使用方法,Spring的JDBC模板技术大大简化了数据库操作代码,使开发者能够更专注于业务逻辑而非底层数据库交互细节。声明式事务管理则提供了灵活的事务控制方式,确保数据的一致性和完整性。
在实际开发中,建议优先使用注解方式配置事务,它更简洁直观。对于复杂的事务需求,可以结合使用@Transactional
的各种属性进行精细化控制。同时,合理选择事务隔离级别和传播行为,可以在保证数据安全的前提下提高系统性能。