编程式事务管理是通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚。
Spring 出现以前,编程式事务管理是基于 POJO 应用的唯一选择。在 Hibernate 中,我们需要在代码中显式调用 beginTransaction()、commit()、rollback() 等事务管理相关的方法,这就是编程式事务管理。而通过 Spring 提供的事务管理 API,我们可以在代码中灵活控制事务的执行。
下面根据在《Spring事务》一节讲解的 PlatformTransactionManager、TransactionDefinition 和 TransactionStatus 三个核心接口,通过编程的方式实现事务管理。
SQL:
INSERT INTO account (username,salary) VALUES ('Bob',3000);
INSERT INTO account (username,salary) VALUES ('Make',3000);
DAO层,在OrdersDao中注入jdbcTemplate,实现数据库操作:
@Repository
public class OrdersDao {
// 注入jdbcTemplate模板对象
@Autowired
private JdbcTemplate springJdbcTemplate;
/**
* Make减去1000
*/
public void reduceMoney() {
String sql = "update account set salary=salary-? where username=?";
springJdbcTemplate.update(sql, 1000, "Make");
}
/**
* Bob增加1000
*/
public void addMoney() {
String sql = "update account set salary=salary+? where username=?";
springJdbcTemplate.update(sql, 1000, "Bob");
}
}
Service层,OrdersService接口
public interface OrdersService {
//转账操作
void accountMoney();
}
具体实现,OrdersServiceImpl
@Service
public class OrdersServiceImpl implements OrdersService{
@Autowired
private OrdersDao ordersDao;
@Autowired//事务管理器
private DataSourceTransactionManager transactionManager;
// 调用dao的方法,业务逻辑,写转账业务
public void accountMoney() {
TransactionDefinition def = new DefaultTransactionDefinition();
// getTransaction()用于启动事务,返回TransactionStatus实例对象
TransactionStatus status = transactionManager.getTransaction(def);//开启事务
// 也可以不传def,getTransaction会创建默认的DefaultTransactionDefinition
//TransactionStatus status = transactionManager.getTransaction(null);
try {
// 1-Bob增加1000
ordersDao.addMoney();
// 2-加入出现异常如下面int
int i=10/0; //(银行中可能为突然停电等...)
// 3-Make减少1000
ordersDao.reduceMoney();
transactionManager.commit(status);//提交事务
} catch (Exception e) {
transactionManager.rollback(status);//回滚事务
throw e;
}
}
}
application.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/springtemplate?useSSL=false
jdbc.username=root
jdbc.password=root
具体配置类,JdbcConfig
@Configuration
public class JdbcConfig {
@Value("${jdbc.driverClass}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/*数据源*/
@Bean("dataSource")
public DriverManagerDataSource getDriverManagerDataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(this.driver);
dataSource.setUrl(this.url);
dataSource.setUsername(this.username);
dataSource.setPassword(this.password);
return dataSource;
}
/*jdbc模板*/
@Bean("springJdbcTemplate")
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);//配置数据源
return jdbcTemplate;
}
/*事务管理*/
@Bean("transactionManager")
public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);//配置数据源
return transactionManager;
}
}
编写单元测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class OrdersServiceImplTest {
@Autowired
OrdersService ordersService;
@Test
public void accountMoney() {
ordersService.accountMoney();
}
}
测试结果:
java.lang.ArithmeticException: / by zero
at com.swadian.spring_learn.service.OrdersServiceImpl.accountMoney(OrdersServiceImpl.java:41)
at com.swadian.spring_learn.service.OrdersServiceImplTest.accountMoney(OrdersServiceImplTest.java:20)
...
因为出现了异常,所以事务实现了回滚,扣款和加钱操作均没有成功(防止了一方加钱成功另一方扣钱失败的情况),保持了数据的一致性。