《Pro Spring》学习笔记之声明式事务

来源:互联网 发布:处cp什么意思网络用语 编辑:程序博客网 时间:2024/04/19 13:08

本文数据库环境为mysql,orm环境为ibatis

数据库脚本:

create table account(id varchar(20primary key,name varchar(20));
create table history(id varchar(20primary key,name varchar(20),accountid varchar(20),
                     
constraint foreign key(accountid) references account(id));

 我们的事务控制,就是想让account表和history同步,也就是account和history的数据必须同时更新,同时插入

DAO接口:

 

package ch12.transaction.declare;

import java.util.List;

public interface IDAO {
   
public void insertAccount(Account account);
   
public void insertHistory(History history,int count) throws MyException ;
   
public List getAllAccount();
   
public List getAllHistory();
   
}

Service接口:

 

package ch12.transaction.declare;

import java.util.List;

public interface AccountManager {
    
public void createAccount(Account account,History history,int count)  throws MyException;
    
public List getAllAccount();
    
public List getAllHistory();
}

 

Domain:

 

package ch12.transaction.declare;

public class Account {
  
private String id;
  
private String name;
  
public Account(){
      
  }

public String getId() {
    
return id;
}

public void setId(String id) {
    
this.id = id;
}

public Account(String id, String name) {
    
super();
    
this.id = id;
    
this.name = name;
}

public String getName() {
    
return name;
}

public void setName(String name) {
    
this.name = name;
}

}



package ch12.transaction.declare;

public class History {
  
private String id;
  
private String name;
  
private Account account;
  
public History(){
      
  }

public History(String id, String name, Account account) {
    
super();
    
this.id = id;
    
this.name = name;
    
this.account = account;
}

public String getId() {
    
return id;
}

public void setId(String id) {
    
this.id = id;
}

public String getName() {
    
return name;
}

public void setName(String name) {
    
this.name = name;
}

public Account getAccount() {
    
return account;
}

public void setAccount(Account account) {
    
this.account = account;
}

}

 

ibatis配置文件:

 

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE sqlMapConfig 
PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN" 
"http://www.ibatis.com/dtd/sql-map-config-2.dtd"
> 
<sqlMapConfig> 

<sqlMap resource="ch12/transaction/declare/Account.xml" /> 
<sqlMap resource="ch12/transaction/declare/History.xml" /> 
</sqlMapConfig>

 

ibatis sql map:

history.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd" >
<sqlMap >
  
<typeAlias type="ch12.transaction.declare.History" alias="history"/>
  
<resultMap id="historyTest" class="history" >
    
<result column="id" property="id" jdbcType="VARCHAR" />
    
<result column="name" property="name" jdbcType="VARCHAR" />
  
</resultMap>
  
 
  

   
<insert id="insertHistory" parameterClass="history">
      insert into history (id,name,accountid) values (#id#,#name#,#account.id#)
   
</insert>
   
<select id="getAllHistory" resultMap="historyTest">
    select * from history
  
</select>
   
</sqlMap>



 

account.xml

 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd" >
<sqlMap >
  
<typeAlias type="ch12.transaction.declare.Account" alias="account"/>
  
<resultMap id="accountTest" class="account" >
    
<result column="id" property="id" jdbcType="VARCHAR" />
    
<result column="name" property="name" jdbcType="VARCHAR" />
  
</resultMap>
  

  
<select id="getAllAccount" resultMap="accountTest">
    select * from account
  
</select>

   
<insert id="insertAccount" parameterClass="account">
      insert into account (id,name) values (#id#,#name#)
   
</insert>
   
   
</sqlMap>

自定义异常:

 

package ch12.transaction.declare;

public class MyException extends Exception {
  
public MyException(String msg){
      
super(msg);
  }

}

 

DAO实现:

 

package ch12.transaction.declare;

import java.sql.SQLException;
import java.util.List;

import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

public class TestDAO extends SqlMapClientDaoSupport implements IDAO {

    
public List getAllHistory() {
        
return this.getSqlMapClientTemplate().queryForList("getAllHistory");
    }

    
public void insertAccount(Account account) {
        
this.getSqlMapClientTemplate().insert("insertAccount",account);
    }

    
public void insertHistory(History history,int count) throws MyException  {
        
if(count%3==0){
            
//当count能被三整除时,模拟错误,促使事务回滚
            throw new MyException("divide by 3");
            
            
//如果使用RuntimeException,则可以直接抛出,并不需要再配置文件中配置"-MyException"参数
            
//因为RuntimeException Spring会自动进行回滚,而非RuntimeException出现,如果不配置参数,spring则会提交
        }
else{
        
this.getSqlMapClientTemplate().insert("insertHistory",history);
        }

    }

    
public List getAllAccount() {
        
return this.getSqlMapClientTemplate().queryForList("getAllAccount");
    }

}

 

service实现:

 

package ch12.transaction.declare;

import java.util.List;

public class AccountManagerImpl implements AccountManager {
    
public List getAllHistory() {
        
return testDAO.getAllHistory();
    }

    
private TestDAO testDAO;
    
public List getAllAccount() {

        
return testDAO.getAllAccount();
    }

    
public TestDAO getTestDAO() {
        
return testDAO;
    }

    
public void setTestDAO(TestDAO testDAO) {
        
this.testDAO = testDAO;
    }

    
public void createAccount(Account account,History history,int count) throws MyException{
         testDAO.insertAccount(account);        
         testDAO.insertHistory(history,count);
        
    }

    

}

 

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"
    xsi:schemaLocation
="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">


<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
  
<property name="driverClassName">
    
<value>com.mysql.jdbc.Driver</value>
  
</property>
  
<property name="username">
    
<value>root</value>
  
</property>
  
<property name="password">
    
<value>1234</value>
  
</property>
  
<property name="url">
    
<value>jdbc:mysql://localhost:3306/spring</value>
  
</property>
</bean>

<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
  
<!-- 此处应注入ibatis配置文件,而非sqlMap文件,否则会出现“there is no statement.....异常” -->
  
<property name="configLocation">
     
<value>ch12/transaction/declare/sqlMapConfig.xml</value>
  
</property>

</bean>
<bean id="testDAO" class="ch12.transaction.declare.TestDAO">
   
<property name="dataSource">
   
<ref bean="dataSource"/>
 
</property>
  
<property name="sqlMapClient">
    
<ref bean="sqlMapClient"/>
  
</property>
</bean>

<bean id="managerTarget" class="ch12.transaction.declare.AccountManagerImpl">
 
<property name="testDAO">
   
<ref local="testDAO"/>
 
</property>
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  
<property name="dataSource">
    
<ref bean="dataSource"/> 
  
</property>
</bean>

<bean id="manager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
  
<property name="transactionManager">
    
<ref bean="transactionManager"/>
  
</property>
  
<property name="target">
    
<ref bean="managerTarget"/>
  
</property>
  
<property name="transactionAttributes">
    
<props>
      
<prop key="create*">PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED,-MyException</prop>
    
</props>
  
</property>
</bean>

</beans>

 

 特别说明:我们在指定目标方法中允许抛出异常,同时我们也定义当异常发生时捕获代理应该进行何种操作,默认情况下,如果RuntimeException抛出,事务管理器进行回滚,如果checked exception抛出,则提交事务,但我们可以通过声明改变这以腥味,如果在异常前追加“-”则代理铺货异常时会自动回滚,如果前缀时"+"则代理将提交事务,如本文中的-MyException,默认情况下MyException会促使事务提交,我们加了前缀“-”,则代理捕获此异常时会进行回滚

测试代码:

 

package ch12.transaction.declare;



import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.sql.SQLException;

public class Test {

    
/**
     * 
@param args
     
*/

    
public static void main(String[] args) {
        ApplicationContext context
=new ClassPathXmlApplicationContext("ch12/transaction/declare/applicationContext.xml");
        AccountManager manager
=(AccountManager)context.getBean("manager");
        
        
int count=0;
        
        
for (int i = 1; i <= 10; i++{
             count
=i;
             Account account
=new Account(String.valueOf(count),"name1");
             History history
=new History(String.valueOf(count),"name1",account); 
          
                 
try {
                    manager.createAccount(account, history,count);
                }
 catch (MyException e) {
                    System.out.println(e.getMessage());
                }

    
                
            
             
            
      }

        System.out.println(
"account data:");
        List result
=manager.getAllAccount();
        
for (Object acc : result) {
            System.out.println(((Account)acc).getId());
        }

        System.out.println(
"history data:");
        List result1
=manager.getAllHistory();
        
for (Object acc1 : result1) {
            System.out.println(((History)acc1).getId());
        }

        
        
    }
    
}

 

结果:

divide by 3
divide by 3
divide by 3
account data:
1
10
2
4
5
7
8
history data:
1
10
2
4
5
7
8

可以看到,能被三整除的操作,account虽然正确,但由于history失败,所以,两边数据保持了一致性

原创粉丝点击