1. 程式人生 > 其它 >Spring事務管理實現篇

Spring事務管理實現篇

實現使用的maven工程,程式碼有點多,耐心看完~

1.事務概念:

事務: 是邏輯上一組操作,要麼全都成功,要麼全都失敗.
事物目的就是解決【資料不一致】的問題。

2.事務特性:

2.1.ACID:
原子性: 事務不可分割
一致性: 事務執行的前後,資料完整性保持一致.
隔離性: 一個事務執行的時候,不應該受到其他事務的打擾
永續性: 一旦結束,資料就永久的儲存到資料庫.

2.事務3類讀問題

如果不考慮隔離性,事務由3類讀問題

  1. 髒讀: 一個事務讀到另一個事務未提交資料
  2. 不可重複讀 :一個事務讀到另一個事務已經提交資料(update)導致一個事務多次查詢結果不一致
  3. 虛讀 :一個事務讀到另一個事務已經提交資料(insert)導致一個事務多次查詢結果不一致

事務解決併發操作3類讀問題:設定隔離級別
併發訪問丟失更新的問題:Java(鎖)/資料庫(鎖)

spring事務的實現:

1.使用MyBatis逆向工程建立資料庫的Mapper和pojo類

<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/new" userId="root"
                        password="123456">
        </jdbcConnection>
        <!-- 預設false,把JDBC DECIMAL 和 NUMERIC 型別解析為 Integer,為 true時把JDBC DECIMAL 和
            NUMERIC 型別解析為java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成PO類的位置 -->
        <javaModelGenerator targetPackage="pojo"
                            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="User"
               enableCountByExample="false"
               enableDeleteByExample="false"
               enableSelectByExample="false"
               enableUpdateByExample="false"></table>
    </context>
</generatorConfiguration>

生成方法:

public static void main(String[] args)  throws Exception{
		List<String> warnings = new ArrayList<String>();
		boolean overwrite = true;
		File configFile = new File("E:\\檔案\\Test\\src\\main\\resources\\generator.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);
	}

2.匯入相關maven依賴, 有點多複製過去就好了

<dependencies>
        <!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache-core</artifactId>
            <version>2.6.11</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.taglibs</groupId>
            <artifactId>taglibs-standard-spec</artifactId>
            <version>1.2.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.taglibs</groupId>
            <artifactId>taglibs-standard-impl</artifactId>
            <version>1.2.5</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.41</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring -->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-indexer</artifactId>
            <version>5.3.2</version>
            <scope>optional</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>5.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-instrument</artifactId>
            <version>5.3.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jms -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
            <version>5.3.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-messaging -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-messaging</artifactId>
            <version>5.3.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.3.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-oxm -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-oxm</artifactId>
            <version>5.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>com.springsource.org.apache.commons.dbcp</artifactId>
            <version>1.2.2.osgi</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>com.springsource.org.apache.commons.pool</artifactId>
            <version>1.5.3</version>
        </dependency>
        <!--<dependency>-->

3.建立業務層(Services)

在這裡插入圖片描述

方法實現程式碼

@Component
public class UserServiceImpl implements UserService {
    public static SqlSession getSqlSession(){
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 2、根據配置檔案建立SqlSessionFactory
        SqlSessionFactory sqlSessionFactory =new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory.openSession();
    }
    @Override
    public boolean giveMoney(int one, int two, int money) {
        SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        //轉賬人資訊
        User user = mapper.selectByPrimaryKey(one);
        int i = 0;
        if (user!=null){
            //設定轉賬金額
            user.setMoney(user.getMoney()-money);
            //受影響得行數
            i=mapper.updateByPrimaryKey(user);
        }

        //轉賬人資訊
        User user1 = mapper.selectByPrimaryKey(two);
        int j = 0;
        if (user1!=null){
            //設定轉賬金額
            user1.setMoney(user1.getMoney()+money);
            j = mapper.updateByPrimaryKey(user1);
        }
        if (i>0&&j>0){
            System.out.println("轉賬成功");
            sqlSession.commit();
            return true;
        }else {
            System.out.println("轉賬失敗");
            sqlSession.rollback();
            return false;
        }
    }
}

4.建立Sqlsession的工具類

public class SqlSessionUtil {
    public static SqlSession getSession(){
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = null;

        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory.openSession();
    }
}

5.編寫Spring的事務管理器

public class TransactionManager implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        SqlSession sqlSession = SqlSessionUtil.getSession();
        //呼叫目標方法
        boolean result = (boolean) methodInvocation.proceed();

        if (result){
            sqlSession.commit();
            System.out.println("****提交事務****");
        }else {
            sqlSession.rollback();
            System.out.println("****回滾事務****");
        }
        return result;
    }
}

最後我們就是配置XML檔案啦!
1.beans.xml配置

<!--  1、啟動註解掃描-->
       <context:annotation-config/>
       <context:component-scan base-package="service"></context:component-scan>
       
       
       <!-- 1)目標 -->
       <bean id="target" class="service.Impl.UserServiceImpl"></bean>
       <!-- 2)事務 -->     
       <bean id="transactionManager" class="proxy.TransactionManager"></bean>
       <!--3)代理  -->  
     <bean  id="userInfoServiceImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces"  value="service.UserService"></property>
        <!-- 1)注入目標物件 -->
        <property name="target"  ref="target"/>
        <!-- 2)事務物件  -->
        <property name="interceptorNames">
            <array>
                <value>transactionManager</value>
            </array>
        </property>
    </bean>
       
  </beans>

2.database.properties(資料庫連線字串)

url=jdbc:mysql://localhost:3306/new(記得改自己的)?useUnicode=true&amp&characterEncoding=utf-8
driver=com.mysql.jdbc.Driver
uname=root
password=123456(記得改)

3.beans-datasource.xml配置

<!--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="${uname}" />
		<property name="password" value="${password}" />
	</bean>
	<!-- 3、sqlSessionFactory -->
	<bean id="sqlSessionFactory"
		class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 別名 -->
		<property name="typeAliasesPackage" value="pojo"></property>
		<!-- mapper XML對映 -->
		<property name="mapperLocations"
			value="classpath*:mapper/*Mapper.xml"></property>
		<!-- 資料來源 -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<!--4、mapper介面的位置 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="mapper"></property>
	</bean>
	<!-- 1)、事務管理(增強/通知):事務 -->
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
		<property name="dataSource" ref="dataSource"/> </bean>
	
		

4.SqlMapConfig.xml配置

<configuration>
  <typeAliases>
        <package name="pojo"/>
    </typeAliases>
        <plugins>
            <!-- com.github.pagehelper為PageHelper類所在包名 -->
            <plugin interceptor="com.github.pagehelper.PageInterceptor">
            </plugin>
        </plugins>
    <!-- 配置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/new?useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 載入mapper -->
    <mappers>
        <package name="mapper"/>
    </mappers>

</configuration>

腳下留心:記得在實現類裡面加上註解在這裡插入圖片描述

最後我們就可以寫實測試類了

1.事務回滾:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:beans.xml"})
public class Test2 {
    @Qualifier("userInfoServiceImpl")
    @Autowired
    UserService userService;
@Test
    public void Test(){
    boolean b = userService.giveMoney(4, 2, 100);
    System.out.println(b);
}
}

由於資料庫沒有id為4,所所以轉賬失敗
在這裡插入圖片描述

2.事務提交:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:beans.xml"})
public class Test2 {
    @Qualifier("userInfoServiceImpl")
    @Autowired
    UserService userService;
@Test
    public void Test(){
    boolean b = userService.giveMoney(1, 2, 100);
    System.out.println(b);
}
}

在這裡插入圖片描述
轉賬成功!這裡我們看到資料庫發生了改變~