1. 程式人生 > 其它 >spring---事務管理練習

spring---事務管理練習

技術標籤:springjava

所要完成的功能—轉賬(這裡使用的是maven工程)

案例:甲乙兩個人轉錢,正常情況下:甲輸入乙的賬號,輸入金額,然後甲的賬戶扣除相應的錢,乙的賬戶扣除相應的錢;
但是就是有特殊情況,甲在輸入賬號的時候輸錯了,就會發生一個情況,甲的錢扣掉了,但是乙沒有收到錢,肯定就會出現問題;這就是事務相關的問題:

解決辦法:但雙方進行轉賬時其中一方發生異常,及時進行回滾,等待資料正常時在進行提交

一、建立進行轉賬的賬號資料庫

CREATE TABLE userinfo (
id int(11) NOT NULL AUTO_INCREMENT,
username varchar(255) DEFAULT NULL,

birthday datetime DEFAULT NULL,
sex varchar(1) DEFAULT NULL,
address varchar(255) DEFAULT NULL,
money int(255) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

插入資料:BEGIN;
INSERT INTO userinfo VALUES (1, ‘旺財’, ‘2020-09-29 00:00:00’, ‘女’, ‘五一廣場’, 2100);
INSERT INTO userinfo

VALUES (2, ‘來福’, ‘2020-09-16 00:00:00’, ‘男’, ‘五一新幹線’, 700);
COMMIT;
主鍵:SET FOREIGN_KEY_CHECKS = 1;

二、

在這之前大家記得匯入相關架包(因為使用的是maven空間,大家架包可以去maven中央倉庫去下載

這裡是maven倉庫連結

三、建立一個轉賬的介面
在這裡插入圖片描述
四、配製資料庫連線

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!--<setting name="lazyLoadingEnabled" value="true"/>--> <!--<setting name="aggressiveLazyLoading" value="false"/>--> <!-- 開啟二級快取總開關 --> <setting name="cacheEnabled" value="true"/> </settings> <typeAliases> <package name="pojo"/> <package name="vo"/> </typeAliases> <!-- 配置mybatis的環境資訊 --> <environments default="development"> <environment id="development"> <!-- 配置JDBC事務控制,由mybatis進行管理 --> <transactionManager type="JDBC"></transactionManager> <!-- 配置資料來源,採用dbcp連線池 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/userinfo?useUnicode=true&amp;characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!-- 載入mapper --> <mappers> <!--<mapper resource="sqlmap/User.xml"/>--> <package name="mapper"/> </mappers> </configuration>

五、使用MyBateis來生成資料庫相關語句、介面、實現類等

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="testTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自動生成的註釋 true:是 : false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--資料庫連線的資訊:驅動類、連線地址、使用者名稱、密碼 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/userinfo"
                        userId="root"
                        password="123456">
        </jdbcConnection>
        <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
            connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg"
            userId="yycg"
            password="yycg">
        </jdbcConnection> -->

        <!-- 預設false,把JDBC DECIMAL 和 NUMERIC 型別解析為 Integer,為 true時把JDBC DECIMAL 和
            NUMERIC 型別解析為java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成PO類的位置 -->
        <javaModelGenerator targetPackage="po"
                            targetProject=".\src">
            <!-- enableSubPackages:是否讓schema作為包的字尾 -->
            <property name="enableSubPackages" value="false" />
            <!-- 從資料庫返回的值被清理前後的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- targetProject:mapper對映檔案生成的位置 -->
        <sqlMapGenerator targetPackage="mapper"
                         targetProject=".\src">
            <!-- enableSubPackages:是否讓schema作為包的字尾 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- targetPackage:mapper介面生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="mapper"
                             targetProject=".\src">
            <!-- enableSubPackages:是否讓schema作為包的字尾 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>

        <table tableName="userinfo"
               domainObjectName="Userinfo"
               enableCountByExample="false"
               enableDeleteByExample="false"
               enableSelectByExample="false"
               enableUpdateByExample="false"/>
    </context>
</generatorConfiguration>

然後用測試類來生成介面和實現類(記得修改包的相關路徑)

    @Test
    public void testMBG() throws Exception{
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("mbg.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }
    
}
生成了之後大家記得改變包的位置不然怕載入不了

六、建立實現類

@Configuration
public class ChangeMoneyImpl implements changmoney {


    @Override
    public boolean giveMoney(int on, int to, int money) {
        SqlSession sqlSession= sqlSessionUtil.openSession();
        UserinfoMapper mapper = sqlSession.getMapper(UserinfoMapper.class);
        //轉錢人的資訊
        Userinfo userinfo =mapper .selectByPrimaryKey(on);
        int i=0;
        if (userinfo!=null){
            //設定轉錢人的餘額
            userinfo.setMoney(userinfo.getMoney()-money);
            //收影響的行數
            i = mapper.updateByPrimaryKey(userinfo);
        }

        //被轉錢人的資訊
        Userinfo userinfo2 =mapper .selectByPrimaryKey(to);
        int j=0;
        if (userinfo2!=null){
            //設定轉錢人的餘額
            userinfo2.setMoney(userinfo2.getMoney()+money);
            //收影響的行數
            j = mapper.updateByPrimaryKey(userinfo2);
        }
        if (i>0&&j>0){
            System.out.println("轉錢成功");
            //sqlSession.commit();
            return true;
        }else {
            System.out.println("轉錢失敗");
            //sqlSession.rollback();
            return false;
        }
    }

}

七、構建SqlSessionFactory

public class sqlSessionUtil {
    private static  SqlSession sqlSession= null; 
    static {
      //1、根據mybaits的配置檔案構建SqlSessionFactory 
        //需要改成我們自己的mybatis-config.xml的路徑
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //2、建立SqlSession   
       sqlSession= sqlSessionFactory.openSession();
    }
    public static SqlSession openSession() {
        return sqlSession;
    }
}

測試是可以轉轉成功的
在這裡插入圖片描述
接下來配置代理事務了

首先配置資料庫連線

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

	<!--1、載入資料庫的配置資訊 -->
    <context:property-placeholder location="database.properties"/>
	<!--2、datasource資料來源 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName" value="${driver}" />
		<property name="url"  value="${url}"/>
		<property name="username"  value="${username2}"/>
		<property name="password" value="${password}"/>
	</bean>
	<!-- 3、sqlSessionFactory -->
	<bean id="sqlSessionFactory"
        class="org.mybatis.spring.SqlSessionFactoryBean">
         <!-- 別名 -->
       <property name="typeAliasesPackage"  value="com.cc.model"></property>
       <!-- mapper  XML對映 -->
        <property name="mapperLocations"  value="classpath*:mapper/*Mapper.xml"></property>
        <!-- 資料來源 -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
	
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
       <property name="basePackage" value="com.cc.mapper"></property>
    </bean>

    <!-- 5、事務管理 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
	
</beans>
url=jdbc:mysql://localhost:3306/userinfo?useUnicode=true&amp&characterEncoding=utf-8
driver=com.mysql.jdbc.Driver
username2=root
password=123456

接下來就是配置事務代理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        ">
    <!--  1、啟動註解掃描-->
     <!--  <context:annotation-config/> -->
       <context:component-scan base-package="change"></context:component-scan>

       
       <!-- 1)目標 -->
       <bean id="target" class="change.ChangeMoneyImpl"></bean>
       <!-- 2)黑客 -->     
       <bean id="transactionManager" class="proxy.TransactionManager"></bean>
       <!--3)代理  -->  
       
     <bean  id="ChangeMoneyImplProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces"  value="change.changmoney"></property>
        <!-- 1)注入目標物件 --> 
        <property name="target"  ref="target"/>
        <!-- 2)黑客物件  -->
        <property name="interceptorNames">
            <array>
                <value>transactionManager</value>
            </array>
        </property>
    </bean>

     
  </beans>

編寫實體代理類

public class TransactionManager  implements MethodInterceptor{
    @Override
    public Object invoke(MethodInvocation method) throws Throwable {
        SqlSession sqlSession = sqlSessionUtil.openSession();
        //呼叫目標方法
        boolean result = (boolean) method.proceed();
            if(result) {
                sqlSession.commit();
                System.out.println("====提交事務===");
            }else {
                sqlSession.rollback();
                System.out.println("====回滾事務===");
            }
        return result;
    }

}

最後在去進行測試

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration({"classpath:beans.xml"})
public class Test2 {
    @Autowired
    @Qualifier("ChangeMoneyImplProxy")
    changmoney changmoney;
    @Test
    public void test1(){
        boolean b = changmoney.giveMoney(1, 2, 30);
        System.out.println(b);
    }
}

所得結果為
在這裡插入圖片描述