本地事务是特定于资源的,例如与JDBC连接关联的事务。本地事务可能更容易使用,但具有明显的缺点:它们无法跨多个事务资源工作。例如,使用JDBC连接管理事务的代码无法在全局JTA事务中运行。由于应用程序服务器不参与事务管理,因此无法确保跨多个资源的正确性。(值得注意的是,大多数应用程序使用单个事务资源。)另一个缺点是本地事务对编程模型是侵入性的。
@Transactional 设置
该@Transactional注解的是,指定的接口,类或方法必须具有事务语义的元数据(例如,“调用该方法时启动一个全新的只读事务,悬浮剂任何现有的交易”)。
默认@Transactional设置如下:
传播设置是 PROPAGATION_REQUIRED.
隔离级别是 ISOLATION_DEFAULT.
该事务是读写的。
事务超时默认为基础事务系统的默认超时,如果不支持超时,则默认为none。
任何RuntimeException触发器回滚,任何检查Exception都没有。
@Transactional设置
事务传播Transaction Propagation
理解 PROPAGATION_REQUIRED
PROPAGATION_REQUIRED如果当前没有事务存在,则执行本地事务的本地事务,或者参与为更大范围定义的现有“外部”事务。这是同一线程内公共调用堆栈安排的一个很好的默认值(例如,委托给几个存储库方法的服务外观,其中所有底层资源都必须参与服务级别事务)。
当传播设置为时PROPAGATION_REQUIRED,将为应用设置的每个方法创建逻辑事务范围。每个这样的逻辑事务范围可以单独确定仅回滚状态,外部事务范围在逻辑上独立于内部事务范围。在标准PROPAGATION_REQUIRED行为的情况下,所有这些范围都映射到同一物理事务。因此,内部事务范围中的仅回滚标记集确实会影响外部事务实际提交的机会。
但是,在内部事务作用域设置仅回滚标记的情况下,外部事务尚未决定回滚本身,因此回滚(由内部事务作用域静默触发)是意外的。UnexpectedRollbackException在那一点上抛出相应的对应 物。这是预期的行为,因此事务的调用者永远不会被误导,假设在实际上没有执行提交。因此,如果内部事务(外部调用者不知道)以静默方式将事务标记为仅回滚,则外部调用者仍会调用commit。外部调用者需要接收一个UnexpectedRollbackException以清楚地表明已执行回滚。
理解 PROPAGATION_REQUIRES_NEW
PROPAGATION_REQUIRES_NEW与此相反PROPAGATION_REQUIRED,始终对每个受影响的事务范围使用独立的物理事务,从不参与外部范围的现有事务。在这样的安排中,底层资源事务是不同的,因此可以独立地提交或回滚,外部事务不受内部事务的回滚状态的影响,并且内部事务的锁在完成后立即释放。这样一个独立的内部事务也可以声明它自己的隔离级别,超时和只读设置,而不是继承外部事务的特性。
理解 PROPAGATION_NESTED
PROPAGATION_NESTED使用具有多个保存点的单个物理事务,它可以回滚到该事务。这种部分回滚允许内部事务作用域触发其作用域的回滚,外部事务能够继续物理事务,尽管已经回滚了一些操作。此设置通常映射到JDBC保存点,因此它仅适用于JDBC资源事务。见春天DataSourceTransactionManager。
理想操作:
The configured profiling aspect starts.
配置的性能分析方面开始。
The transactional advice executes.
事务建议执行。
The method on the advised object executes.
建议对象上的方法执行。
The transaction commits.
事务提交。
The profiling aspect reports the exact duration of the whole transactional method invocation.
概要分析方面报告整个事务方法调用的确切持续时间。
简单应用
技术点:springboot + jdbc + test + maven
pom.xml添加如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
application.properties
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/user-manager?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=666666
Dao层
public interface UserDao {
//充值
public int recharge(String username, Double money) throws Exception;
//扣款
public int Deduction(String username, Double money) throws Exception;
//查询
public Object query(String username);
}
@Repository
public class UserDaoImpl implements UserDao {
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public Double query(String username) {
Double money = this.jdbcTemplate.queryForObject(
"select money from user_money where username = ?",
new String[] {username}, Double.class);
return money;
}
@Override
public int recharge(String username, Double money) throws Exception {
String sql = "update user_money set money = money + ? where username = ?";
int update = jdbcTemplate.update(sql, money, username);
// throw new Exception("转出失败");
return update;
}
@Override
public int Deduction(String username, Double money) throws Exception {
String sql = "update user_money set money = money - ? where username = ? and money > ? ";
int update = jdbcTemplate.update(sql, money, username, money);
throw new RuntimeException("转出失败");
// return update;
}
}
Server层
@Service
public class UserServer {
@Autowired
private UserDaoImpl userDaoImpl;
@Transactional(rollbackFor = Exception.class)
public void transfer(String intoUserName, String outUserName,Double money) throws Exception {
int recharge = userDaoImpl.recharge(intoUserName, money);
int deduction = userDaoImpl.Deduction(outUserName, money);
if(!(recharge > 0 && deduction > 0)) {
throw new Exception("转账异常");
}
}
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringTmTestApplicationTests {
@Autowired
private UserServer userServer;
@Test
public void contextLoads() throws Exception {
userServer.transfer("zhangsan", "lisi", 10D);
//userServer.transfer("zhangsan", "lisi", 10D);
}
}