事务的作用
事务是为了保证数据的安全性,完整性,一致性等问题。
事务是具有边界的,事务有开始,提交和回归等边界。
事务的性质有原子性,一致性,隔离性,持久性。
使用JDBC API定义事务边界
新建maven项目,添加依赖
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.176</version>
</dependency>
该依赖用来使用轻量级的数据库h2
打开h2数据库,新建表和添加记录
H2数据库的打开方式是在添加的jar包中找到org.h2.tools.Console类,运行它。
Create table account(
Id bigint identity primary key,
Owner_name varchar(255),
Balance double,
Access_time timestamp,
Locked Boolean
);
Insert into account values(100,’owner-1’,10,’2020-02-14’,false);
Insert into account values(101,’owner-2’,0,’2020-02-14’,false);
新建Accountserviece接口并实现,完成转账功能
public class AccountSerivceImpl implements AccountService {
public void transferMoney(long sourceAccountId,long targetAccountId,double amount){
Connection connection = null;
try{
DriverManager.registerDriver(new Driver());
connection = DriverManager.getConnection("jdbc:h2:tcp://localhost/~/test","sa","");
connection.setAutoCommit(false);
Statement statement = connection.createStatement();
String sql1 = "update account set balance = balance - " + amount + " where id = " + sourceAccountId;
String sql2 = "update account set balance = balance + " + amount + " where id = " + targetAccountId;
statement.executeUpdate(sql1);
statement.executeUpdate(sql2);
connection.commit();
}catch (SQLException e){
try{
connection.rollback();
}catch (SQLException s){
}
throw new RuntimeException(e);
}finally {
try{
connection.commit();
}catch (SQLException s){
}
}
}
}
在main方法中转账,并观察数据库·1
public static void main(String[] args) {
AccountService service = new AccountSerivceImpl();
service.transferMoney(100L,101L,5.0d);
}
实例说明
使用DriverManager.registerDriver方法来注册h2数据的驱动。也可用Class.forName()的方法来注册驱动。
JDBCAPI中用connection.setAutoCommit(false)来激活事务,否则sql语句被单句执行。
用connection.commit()来提交事务。
用connection.rollback()来回滚。
最后关闭connection资源。
在Spring容器中配置PlatformTransactionManagerBean
添加Maven依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.0.5.RELEASE</version>
</dependency>
创建bean配置类
@org.springframework.context.annotation.Configuration
public class Configuration {
@Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:tcp://localhost/~/test");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource());
return transactionManager;
}
}
在main方法检测PlatformTransactionManager是否获取
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Configuration.class);
PlatformTransactionManager transactionManager = applicationContext.getBean(PlatformTransactionManager.class);
System.out.println(transactionManager != null);
}
}
示例说明
Spring的事务抽象模型基于PlatformTransactionManager接口。作为开发人员,需要根据不同的数据库技术选用不用的实现。
全局事务和本地事务
本地事务就是只在单一数据库上进行的事务。
而全局事务的操作设计多个数据库,意味着分布式事务管理。
JEE提供了JTA来进行全局事务的管理。但是使用JTA的成本较高,且大多数应用并没有使用全局事务。所以在TransactionManager接口有多种实现,分别针对全局事务和本地事务。
在进行全局事务和本地事务进行切换时,只需要改变相应的Bean,而不用改变对应的业务逻辑。
在Spring中启用声明式事务管理
修改AccountServiceImpl类
public class AccountSerivceImpl implements AccountService {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
@Transactional
public void transferMoney(long sourceAccountId, long targetAccountId, double amount){
Connection connection = DataSourceUtils.getConnection(dataSource);
try{
Statement statement = connection.createStatement();
String sql1 = "update account set balance = balance - " + amount + " where id = " + sourceAccountId;
String sql2 = "update account set balance = balance + " + amount + " where id = " + targetAccountId;
statement.executeUpdate(sql1);
statement.executeUpdate(sql2);
}catch (SQLException e){
throw new RuntimeException(e);
}finally {
DataSourceUtils.releaseConnection(connection,dataSource);
}
}
}
在bena配置类中添加AccountServiceImpl类
@Bean
public AccountService accountService(){
AccountSerivceImpl accountSerivce = new AccountSerivceImpl();
accountSerivce.setDataSource(dataSource());
return accountSerivce;
}
在配置类上加@EnableTransactionManagement注解
在main方法中执行transfer方法
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Configuration.class);
AccountService accountService = applicationContext.getBean(AccountService.class);
accountService.transferMoney(100l,101l,5.0d);
}
示例说明
在配置类上添加@EnableTransactionManagement注解是为了在bean中检测有@transactonal的方法。
事务是具有边界的,必须定义事务开始,提交,回滚的地方。
而使用了@Transactional的方法自动定义事务边界,在操作没V 有异常发生时自动提交,有RuntimeException及其子类异常会自动回滚。
使用TransactionTemplate进行编程式事务管理
修改AccountServiceImpl中的transferMoney方法添加TransacTemplate属性。
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void transferMoney(final long sourceAccountId, final long targetAccountId, final double amount) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
Account sourceAccount = accountDao.find(sourceAccountId);
Account targetAccount = accountDao.find(targetAccountId);
sourceAccount.setBalance(sourceAccount.getBalance() - amount);
targetAccount.setBalance(targetAccount.getBalance() + amount);
accountDao.update(sourceAccount);
accountDao.update(targetAccount);
}
});
}
在Configuration类中添加Bean
@Bean
public TransactionTemplate transactionTemplate(){
TransactionTemplate transactionTemplate = new TransactionTemplate();
transactionTemplate.setTransactionManager(transactionManager());
return transactionTemplate;
}
@Bean
public AccountService accountService(){
AccountServiceImpl accountSerivce = new AccountServiceImpl();
accountSerivce.setAccountDao(accountDao());
accountSerivce.setTransactionTemplate(transactionTemplate());
return accountSerivce;
}
在main方法检测用例
示例说明
使用TransactionTemplate是Spring提供的编程式事务方法。
该类用了模板方法,把需要执行的算法步骤作为回调方法在匿名类中实现。
即用transaction.execute().新建TransactionCallBackWithoutResult匿名类。实现doInTransactionCallBackWithoutResult方法。
使用PlatformTransactionManager API进行编程式事务管理
修改AccountServiceImpl类
private PlatformTransactionManager transactionManager;
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public void transferMoney(final long sourceAccountId, final long targetAccountId, final double amount) {
TransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(definition);
try{
Account sourceAccount = accountDao.find(sourceAccountId);
Account targetAccount = accountDao.find(targetAccountId);
sourceAccount.setBalance(sourceAccount.getBalance() - amount);
targetAccount.setBalance(targetAccount.getBalance() + amount);
accountDao.update(sourceAccount);
accountDao.update(targetAccount);
transactionManager.commit(status);
}catch (Exception e){
transactionManager.rollback(status);
throw new RuntimeException(e);
}
}
修改bean配置类
@Bean
public AccountService accountService(){
AccountServiceImpl accountSerivce = new AccountServiceImpl();
accountSerivce.setAccountDao(accountDao());
accountSerivce.setTransactionManager(transactionManager());
return accountSerivce;
}
示例说明
TransactionDefinition是编程式事务的配置。
使用transaction.getTransaction(definition)表示着事务的开始。
在try代码块中进行业务逻辑后,用transaction.commit(status)提交事务。
在catch代码块中执行回滚。
Spring推荐使用transactionTemplate而不是Transactionmanager进行编程式事务。
编程式事务与声明式事务区别在于是否需要定义事务边界。