applicationContext.xml,spring声明式事务配置
<context:component-scan base-package="com.mazh.demo"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://locahost:3306/demo01"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven/>
pom.xml,jar包配置
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>5.2.12.RELEASE</spring.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
</dependencies>
AccountService.java
package com.mazh.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@SuppressWarnings("ALL")
@Service
public class AccountService {
@Autowired
JdbcTemplate jdbcTemplate;
/**
* 有异常自动回滚
*/
@Transactional
public void add1() {
jdbcTemplate.execute("insert into account(name,money) values ('hangman',1000)");
int i = 1 / 0;
}
/**
* 捕获了异常而未抛出,事务失效
*/
@Transactional
public void add2() {
jdbcTemplate.execute("insert into account(name,money) values ('hangman',1000)");
try {
int i = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 捕获异常并抛出,事务回滚, ArithmeticException是RuntimeException的子类
*/
@Transactional
public void add3() {
jdbcTemplate.execute("insert into account(name,money) values ('hangman',1000)");
try {
int i = 1 / 0;
} catch (ArithmeticException e) {
throw new ArithmeticException(e.getMessage());
}
}
/**
* 捕获异常并抛出(FileNotFoundException),事务失效,因为FileNotFoundException是Exception子类
* Spring的事务管理默认是针对Error异常和RuntimeException异常以及其子类进行事务回滚
*/
@Transactional
public void add4() throws FileNotFoundException {
jdbcTemplate.execute("insert into account(name,money) values ('hangman',1000)");
try {
new FileInputStream(new File(""));//java.io.FileNotFoundException
} catch (FileNotFoundException e) {
throw new FileNotFoundException();
}
}
/**
* jdbcTemplate空指针异常,spring事务对final、static修改的方法无法提供代理。
* final修改的方法不能被重写,static修饰的方法属于类级别的,类的信息在编译期就知道了,并不支持运行期动态绑定
*/
@Transactional
public final void add5() {
jdbcTemplate.execute("insert into account(name,money) values ('hangman',1000)");
int i = 1 / 0;
}
/**
* 非事务的方法调用内部的事务方法,事务失效,没有经过代理对象调用重写后的add7方法
*/
public void add6() {
jdbcTemplate.execute("insert into account(name,money) values ('hangman',2000)");
add7();
}
@Transactional
public void add7() {
jdbcTemplate.execute("insert into account(name,money) values ('hangman',3000)");
int i = 1 / 0;
}
/**
* 两个事务方法之间的调用,事务生效.
* 因为默认事务的传播是required,当前就事务就加入,没有就创建一个。所以从始至终是一个事务
*/
@Transactional
public void add8() {
jdbcTemplate.execute("insert into account(name,money) values ('hangman',2000)");
add9();
}
@Transactional
public void add9() {
jdbcTemplate.execute("insert into account(name,money) values ('hangman',3000)");
int i = 1 / 0;
}
/**
* 这种方式等同add8, 因为@Transactional(propagation = Propagation.REQUIRED)是默认的传播行为
*/
@Transactional
public void add10() {
jdbcTemplate.execute("insert into account(name,money) values ('hangman',2000)");
add11();
}
@Transactional(propagation = Propagation.REQUIRED)
public void add11() {
jdbcTemplate.execute("insert into account(name,money) values ('hangman',3000)");
int i = 1 / 0;
}
/**
* 事务回滚,REQUIRES_NEW这个属性表示不论当前是否存在事务,总是会新建一个事务,那么同一个事务回滚两个插入操作都回滚
*/
@Transactional
public void add12() {
jdbcTemplate.execute("insert into account(name,money) values ('hangman',2000)");
add13();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void add13() {
jdbcTemplate.execute("insert into account(name,money) values ('hangman',3000)");
int i = 1 / 0;
}
/**
* 事务回滚,真是颠覆程序员的认知啊,导致程序员畏手畏脚的罪魁祸首就是以讹传讹,不亲生实践
* 并且int i = 1 / 0;在其中任意方法效果一致
* @Transactional(propagation = Propagation.SUPPORTS)表示以非事务的方式运行
*/
// @Transactional(propagation = Propagation.SUPPORTS)
@Transactional
public void add14() {
jdbcTemplate.execute("insert into account(name,money) values ('hangman',2000)");
add15();
// int i = 1 / 0;
}
@Transactional(propagation = Propagation.SUPPORTS)
public void add15() {
int i = 1 / 0;
jdbcTemplate.execute("insert into account(name,money) values ('hangman',3000)");
}
}
AccountServiceTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {
@Autowired
private AccountService accountService;
@Test
public void test01() {
accountService.add();
}
}
account 表
CREATE TABLE `account` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
`money` int(11) NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
AUTO_INCREMENT=2
ROW_FORMAT=COMPACT
;