MyBatis(六)MyBatis使用事務


什么是事務

  • 可以把一系列要執行的操作稱為事務,而事務管理就是管理這些操作要么完全執行,要么完全不執行(很經典的一個例子是:A要給B轉錢,首先A的錢減少了,但是突然的數據庫斷電了,導致無法給B加錢,然后由於丟失數據,B不承認收到A的錢;在這里事務就是確保加錢和減錢兩個都完全執行或完全不執行,如果加錢失敗,那么不會發生減錢)。
  • 事務管理的意義:保證數據操作的完整性。
  • mysql中並不是所有的數據引擎都支持事務管理的,只有innodb支持事務管理。

事務是指的是一個業務上的最小不可再分單元,通常一個事務對應了一個完整的業務,而一個完整的業務需要批量的DML語句共同聯合完成。一般,同一個事務中的SQL語句是保存到數據庫中的同一個Transaction對象中,原因是Transaction具有一致性的特征,也就是說事務中如果有任何一條sql語句運行失敗,那么這個事務中所有的SQL語句都會被判定為無效SQL。

事務管理的特性

  • 原子性(Atomicity):事務的整個操作是一個整體,不可以分割,要么全部成功,要么全部失敗。
  • 一致性(Consistency):事務操作的前后,數據表中的數據沒有變化。
  • 隔離性(Isolation):事務操作是相互隔離不受影響的。
  • 持久性(Durability):數據一旦提交,不可改變,永久的改變數據表數據。

事務隔離級別

事務隔離級別 臟讀 不可重復讀 幻讀
讀未提交(read-uncommitted)也叫臟讀
不可重復讀(read-committed)也叫讀已提交
可重復讀(repeatable-read)默認級別
串行化(serializable)

使用JDBC的事務管理機制

在mybatis-config.xml中添加如下配置

<!-- mybatis使用jdbc事務管理方式 -->
<transactionManager type="jdbc"/>

子節點<transactionManager> 的type 會決定我們用什么類型的事務管理機制。

完整配置如下

<?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>
        <!-- 打印sql日志 -->
        <setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/jdbc_test?characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/OrderMapper.xml"/>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

事務的創建

創建事務

 JdbcTransaction jdbcTransaction= new JdbcTransaction(getConnection());

完整代碼如下

UserMapper

package org.mybatis.example;

import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserMapper {
    List<User> selectUserByListId(@Param("ids") List<Integer> ids);

    List<User> selectUser(String username);

    int insertUser(User user);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.UserMapper">
    <!-- 一對多 根據id查詢用戶及其關聯的訂單信息:級聯查詢的第一種方法(分步查詢) -->
    <resultMap type="org.mybatis.example.User" id="myResult">
        <id property="id" column="id" />
        <result property="username" column="username" />
        <result property="sex" column="sex" />
        <result property="birthday" column="birthday" />
        <result property="address" column="address" />
    </resultMap>
    <select id="selectUserByListId" parameterType="List" resultMap="myResult">
        select * from user
        <where>
            <!--
                collection:指定輸入對象中的集合屬性
                item:每次遍歷生成的對象
                open:開始遍歷時的拼接字符串
                close:結束時拼接的字符串
                separator:遍歷對象之間需要拼接的字符串
                select * from user where 1=1 and id in (1,2,3)
              -->
            <foreach collection="ids" item="id" open="and id in (" close=") " separator=",">
                #{id}
            </foreach>
        </where>
    </select>

    <select id="selectUser" resultMap="myResult">
        <bind name="username" value="'%'+_parameter+'%'" />
        select * from user where username like #{username}
    </select>

    <insert id="insertUser">
        insert into user(username,sex,birthday,address) value(#{username},#{sex},#{birthday},#{address})
    </insert>
</mapper>

MainApplication

package org.mybatis.example;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.jdbc.JdbcTransaction;

import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;

public class MainApplication {
    public static void main(String[] args) throws IOException, SQLException {
        InputStream config = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
        SqlSession ss = ssf.openSession();
        JdbcTransaction jdbcTransaction= new JdbcTransaction(ss.getConnection());
        try{
            UserMapper userMapper = ss.getMapper(UserMapper.class);

            User user = new User();
            user.setAddress("地址1");
            user.setUsername("用戶1");
            userMapper.insertUser(user);
            user.setUsername("用戶2");
            userMapper.insertUser(user);
            System.out.println("事務提交");
            jdbcTransaction.commit();
        }catch(Exception ex){
            System.out.println("事務回滾");
            jdbcTransaction.rollback();
            throw ex;
        }
    }
}

運行MainApplication輸出如下

Opening JDBC Connection
Created connection 371439501.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1623b78d]
==>  Preparing: insert into user(username,sex,birthday,address) value(?,?,?,?)
==> Parameters: 用戶1(String), null, null, 地址1(String)
<==    Updates: 1
==>  Preparing: insert into user(username,sex,birthday,address) value(?,?,?,?)
==> Parameters: 用戶2(String), null, null, 地址1(String)
<==    Updates: 1
事務提交
Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1623b78d]

查看數據庫

驗證是否正確,在插入第一條數據后報錯。

package org.mybatis.example;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.jdbc.JdbcTransaction;

import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;

public class MainApplication {
    public static void main(String[] args) throws IOException, SQLException {
        InputStream config = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
        SqlSession ss = ssf.openSession();
        JdbcTransaction jdbcTransaction= new JdbcTransaction(ss.getConnection());
        try{
            UserMapper userMapper = ss.getMapper(UserMapper.class);

            User user = new User();
            user.setAddress("地址1");
            user.setUsername("用戶3");
            userMapper.insertUser(user);

            int i = 1/0;

            user.setUsername("用戶4");
            userMapper.insertUser(user);
            System.out.println("事務提交");
            jdbcTransaction.commit();
        }catch(Exception ex){
            System.out.println("事務回滾");
            jdbcTransaction.rollback();
            throw ex;
        }
    }
}

運行如下,事務正常回滾。

Opening JDBC Connection
Created connection 371439501.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1623b78d]
==>  Preparing: insert into user(username,sex,birthday,address) value(?,?,?,?)
==> Parameters: 用戶3(String), null, null, 地址1(String)
<==    Updates: 1
事務回滾
Rolling back JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1623b78d]
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at org.mybatis.example.MainApplication.main(MainApplication.java:27)

查看數據庫中是否存在 用戶3 的記錄,當前不存在。

更多理論請查看:

mysql之事務管理 - 隨風行雲 - 博客園 (cnblogs.com)

JDBC介紹和Mybatis運行原理及事務處理 - rayallenbj - 博客園 (cnblogs.com)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM