首頁 > 軟體

Mybatis基礎概念與高階應用小結

2022-06-14 10:01:40

Mybatis基礎回顧與高階應用

資料庫:mysql5.7

jdk:15

引入依賴

<!--引入依賴-->
    <dependencies>
        <!--mybatis座標-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <!--mysql驅動座標-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
            <scope>runtime</scope>
        </dependency>
        <!--單元測試座標-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
    </dependencies>

User實體

@Data
public class User {
    private Integer id;
    private String username;
}

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1/zdy_mybatis
jdbc.username=root
jdbc.password=root

sqlMapConfig.xml

<?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>
    <!--載入外部的properties檔案-->
    <properties resource="jdbc.properties"></properties>
​
    <!--給實體類的全限定類名給別名-->
    <typeAliases>
        <!--給單獨的實體起別名-->
     <!--   <typeAlias type="com.yun.pojo.User" alias="user"></typeAlias>-->
        <!--批次起別名:該包下所有的類的本身的類名:別名還不區分大小寫-->
        <package name="com.yun.pojo"/>
    </typeAliases>
​
    <!--environments:執行環境-->
    <environments default="development">
        <environment id="development">
            <!--當前事務交由JDBC進行管理-->
            <transactionManager type="JDBC"></transactionManager>
            <!--當前使用mybatis提供的連線池-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
​
    <!--引入對映組態檔-->
    <mappers>
        <mapper resource="UserMapper.xml"></mapper>
    </mappers>
</configuration>

案例一-查詢使用者

UserMapper.xml

<mapper namespace="user">
    <!--select-->
    <select id="findAll" resultType="com.yun.pojo.User">
        select * from user
    </select>
</mapper>
@Test
    public void test1() throws IOException {
        //1.Resources工具類,組態檔的載入,把組態檔載入成位元組輸入流
        InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
        //2.解析了組態檔,並建立了sqlSessionFactory工廠
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //3.生產sqlSession
        // 預設開啟一個事務,但是該事務不會自動提交
        //在進行增刪改操作時,要手動提交事務
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //4.sqlSession呼叫方法:查詢所有selectList  查詢單個:selectOne 新增:insert  修改:update 刪除:delete
        List<User> users = sqlSession.selectList("user.findAll");
        users.forEach(item ->{
            System.out.println(item);
        });
        sqlSession.close();
​
    }

輸出結果

User(id=1, username=Tom)
User(id=2, username=Jerry)

案例二-新增使用者

UserMapper.xml

<!--parameterType:引數型別-->
<insert id="saveUser" parameterType="com.yun.pojo.User">
        insert into user Values (#{id},#{username})
</insert>
@Test
    public void test2() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = new User();
        user.setId(3);
        user.setUsername("jack");
        sqlSession.insert("user.saveUser",user);
        sqlSession.commit();
        sqlSession.close();
    }

資料庫結果:

案例三-編輯使用者

UserMapper.xml

<update id="updateUser" parameterType="com.yun.pojo.User">
        update user set username = #{username} where id = #{id}
</update>
@Test
    public void test3() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = new User();
        user.setId(3);
        user.setUsername("rose");
        sqlSession.update("user.updateUser",user);
        sqlSession.commit();
        sqlSession.close();
    }

資料庫結果:

案例四-刪除使用者

UserMapper.xml

<delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id =#{id}
</delete>
 @Test
    public void test4() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
​
        User user = new User();
        user.setId(3);
        sqlSession.delete("user.deleteUser",3);
        sqlSession.commit();
​
        sqlSession.close();
​
    }

資料庫結果:

傳統開發方式

public interface IUserDao {
​
    //查詢所有使用者
    List<User> findAll() throws IOException;
}

UserDaoImpl

public class UserDaoImpl implements IUserDao {
​
    @Override
    public List<User> findAll() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<User> users = sqlSession.selectList("user.findAll");
        sqlSession.close();
        return users;
    }
}
@Test
    public void test5() throws IOException {
        UserDaoImpl dao = new UserDaoImpl();
        List<User> users = dao.findAll();
        System.out.println(users);
    }

列印結果:

[User(id=1, username=Tom), User(id=2, username=Jerry)]

代理開發方式(使用JDK動態代理產生代理物件,由代理物件執行並且操作)

Mapper介面開發需要遵行以下規範:

mapper.xml檔案中的namespace與mapper介面的全限定名相同;
2. mapper介面方法名和mapper.xml中定義的每個statement的id相同
3. mapper介面方法的輸入引數型別和mapper.xml中定義的每個sql的parameterType的型別相同
4. mapper介面方法的輸出引數型別和mapper.xml中定義的每個sql的resultType的型別相同

根據上述的規範修改UserMapper.xml

<mapper namespace="com.yun.dao.IUserDao">
​
    <!--select-->
    <select id="findAll" resultType="com.yun.pojo.User">
        select * from user
    </select>
</mapper>
public interface IUserDao {
​
    //查詢所有使用者
    List<User> findAll() throws IOException;
}
@Test
public void test6() throws IOException {
    InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
​
    IUserDao mapper = sqlSession.getMapper(IUserDao.class);
    List<User> all = mapper.findAll();
    all.forEach(item ->{
        System.out.println(all);
    });
}

輸出結果:

User(id=1, username=Tom)
User(id=2, username=Jerry)

動態sql語句 if標籤

public interface IUserDao {
​
    //多條件組合查詢:演示if
    public List<User> findByCondition(User user);
}
<!--抽取sql片段-->
<sql id="selectUser">
    select * from user
</sql>
​
<!--多條件組合查詢:演示if-->
<select id="findByCondition" parameterType="user" resultType="user">
   <include refid="selectUser"></include>
   <where>
       <if test="id !=null">
           and id = #{id}
       </if>
       <if test="username !=null">
           and username = #{username}
       </if>
   </where>
</select>
@Test
    public void test7() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
​
        IUserDao mapper = sqlSession.getMapper(IUserDao.class);
​
        User user1 = new User();
        user1.setId(1);
        user1.setUsername("Tom");
​
        List<User> all = mapper.findByCondition(user1);
        for (User user : all) {
            System.out.println(user);
        }
    }

輸出結果:

User(id=1, username=Tom)

動態sql語句 foreach標籤

public interface IUserDao {
​
    //多值查詢:演示foreach
    public List<User> findByIds(int[] ids);
}
<!--多值查詢:演示foreach-->
<select id="findByIds" parameterType="list" resultType="user">
   <include refid="selectUser"></include>
   <where>
       <foreach collection="array" open="id in (" close=")" item="id" separator=",">
           #{id}
       </foreach>
   </where>
</select>
@Test
public void test8() throws IOException {
   InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
   SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
   SqlSession sqlSession = sqlSessionFactory.openSession();
​
   IUserDao mapper = sqlSession.getMapper(IUserDao.class);
​
   int[] arr = {1,2};
​
   List<User> all = mapper.findByIds(arr);
   for (User user : all) {
       System.out.println(user);
   }
}

輸出結果:

User(id=1, username=Tom)
User(id=2, username=Jerry)

Mybatis複雜對映

一對一

User實體

@Data
public class User {
​
    private Integer id;
​
    private String username;
​
    //該使用者所具有的訂單資訊
    private List<Order> orders;
​
    //該使用者所具有的角色資訊
    private List<Role> roles;
}

Order實體

@Data
public class Order {
​
    private Integer id;
​
    private String orderTime;
​
    private BigDecimal total;
​
    //表明該訂單屬於哪個使用者
    private User user;
}

Role實體

@Data
public class Role {
​
    private Integer id;
​
    private String roleName;
}
public interface IOrderMapper {
​
    /**
     * 查詢訂單的同時還查詢該訂單所屬的使用者
     * @return
     */
    public List<Order> findOrderAndUser();
}
<resultMap id="orderMap" type="com.yun.pojo.Order">
    <result property="id" column="id"></result>
    <result property="orderTime" column="order_time"></result>
    <result property="total" column="total"></result>
​
    <association property="user" javaType="com.yun.pojo.User">
        <result property="id" column="uid"></result>
        <result property="username" column="username"></result>
    </association>
</resultMap>
​
<!--resultMap:手動來設定實體屬性與表欄位的對映關係-->
<select id="findOrderAndUser" resultMap="orderMap">
    select * from orders o,user u  where o.uid = u.id
</select>
@Test
public void test1() throws IOException {
    //1.Resources工具類,組態檔的載入,把組態檔載入成位元組輸入流
    InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
    //2.解析了組態檔,並建立了sqlSessionFactory工廠
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //3.生產sqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    IOrderMapper mapper = sqlSession.getMapper(IOrderMapper.class);
    List<Order> orderAndUser = mapper.findOrderAndUser();
    orderAndUser.forEach(order -> {
        System.out.println(order);
    });
}

執行結果:

Order(id=1, orderTime=2022-05-01, total=1000.00, user=User(id=1, username=Tom, orders=null, roles=null))
Order(id=2, orderTime=2022-05-10, total=2000.00, user=User(id=2, username=Jerry, orders=null, roles=null))
Order(id=3, orderTime=2022-05-20, total=3000.00, user=User(id=2, username=Jerry, orders=null, roles=null))

一對多

public interface IUserMapper {
​
    /**
     * 查詢所有使用者資訊,同時查詢出每個使用者關聯的訂單資訊
     * @return
     */
    public List<User> findAll();
}
<resultMap id="userMap" type="com.yun.pojo.User">
    <id property="id" column="id"></id>
    <result property="username" column="username"></result>
​
    <collection property="orders" ofType="com.yun.pojo.Order">
        <id property="id" column="oid"></id>
        <result property="orderTime" column="order_time"></result>
        <result property="total" column="total"></result>
    </collection>
</resultMap>
​
<!--resultMap:手動來設定實體屬性與表欄位的對映關係-->
<select id="findAll" resultMap="userMap">
    select u.*,o.id oid,o.order_time,o.total,o.uid from user u left join orders o on u.id = o.uid
</select>
@Test
    public void test2() throws IOException {
        //1.Resources工具類,組態檔的載入,把組態檔載入成位元組輸入流
        InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
        //2.解析了組態檔,並建立了sqlSessionFactory工廠
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //3.生產sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);
        List<User> users = mapper.findAll();
        users.forEach(user -> {
            System.out.println(user.getUsername());
            System.out.println(user.getOrders());
        });
    }

執行結果:

Tom
[Order(id=1, orderTime=2022-05-01, total=1000.00, user=null)]
Jerry
[Order(id=2, orderTime=2022-05-10, total=2000.00, user=null), Order(id=3, orderTime=2022-05-20, total=3000.00, user=null)]

多對多

public interface IUserMapper {
​
    /**
     * 查詢所有使用者資訊,同事查詢出每個使用者關聯的角色資訊
     * @return
     */
    public List<User> findUserAndRole();
}
<resultMap id="userAndRoleMap" type="com.yun.pojo.User">
    <result property="id" column="userId"></result>
    <result property="username" column="username"></result>
​
    <collection property="roles" ofType="com.yun.pojo.Role">
        <result property="id" column="roleId"></result>
        <result property="roleName" column="rolename"></result>
    </collection>
</resultMap>
​
<select id="findUserAndRole" resultMap="userAndRoleMap">
    SELECT * FROM USER u
    LEFT JOIN sys_user_role sur ON u.id = sur.user_id
    LEFT JOIN sys_role sr ON sur.role_id = sr.id
</select>
@Test
public void test3() throws IOException {
    //1.Resources工具類,組態檔的載入,把組態檔載入成位元組輸入流
    InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
    //2.解析了組態檔,並建立了sqlSessionFactory工廠
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //3.生產sqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);
    List<User> users = mapper.findUserAndRole();
    users.forEach(user -> {
        System.out.println(user.getUsername());
        System.out.println(user.getRoles());
    });
}

執行結果:

Tom
[Role(id=null, roleName=董事長), Role(id=null, roleName=經理)]
Jerry
[Role(id=null, roleName=董事長), Role(id=null, roleName=經理)]

Mybatis註解開發

Mybasits常用註解:
@Insert: 實現新增
@Update: 實現更新
@Delete: 實現刪除
@Select: 實現查詢
@Result: 實現結果集封裝;他代替的是標籤<resultMap>,該註解中可以使用單個@Result註解,也可以使用@Result集合,
         使用格式:@Results({@Result(),@Result()}) 或 @Results(@Result())
@Results: 可以與@Result一起使用,封裝多個結果集
@One: 實現一對一結果集封裝
@Many: 實現一對多結果集封裝

測試案例

public interface IUserMapper {
​
    //新增使用者
    @Insert("insert into user values(#{id},#{username})")
    public void addUser(User user);
​
    //更新使用者
    @Update("update user set username = #{} where id = #{id}")
    public void updateUser(User user);
​
    //查詢使用者
    @Select("select * from user")
    public List<User> getAllUser();
​
    //刪除使用者
    @Delete("delete from user where id=#{id}")
    public void delUser(Integer id);
}
private IUserMapper mapper;
​
    @Before
    public void before() throws IOException {
        //1.Resources工具類,組態檔的載入,把組態檔載入成位元組輸入流
        InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
        //2.解析了組態檔,並建立了sqlSessionFactory工廠
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //3.生產sqlSession                                     true:事務自動提交
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        mapper = sqlSession.getMapper(IUserMapper.class);
    }
​
    @Test
    public void addUser(){
        User user = new User();
        user.setId(3);
        user.setUsername("jack");
        mapper.addUser(user);
    }
​
    @Test
    public void updateUser(){
        User user = new User();
        user.setId(3);
        user.setUsername("rose");
        mapper.updateUser(user);
    }
​
    @Test
    public void getAllUser(){
        List<User> userList = mapper.getAllUser();
        userList.forEach(item -> {
            System.out.println(item);
        });
    }
​
    @Test
    public void delUser(){
        mapper.delUser(3);
    }

執行結果:

Mybatis註解實現複雜對映開發

一對一

public interface IOrderMapper {
​
    /**
     * 查詢訂單的同時還查詢該訂單所屬的使用者
     * @return
     */
    @Results({
            @Result(property = "id",column = "id"),
            @Result(property = "orderTime",column = "order_time"),
            @Result(property = "total",column = "total"),
            @Result(property = "user",column = "uid",javaType = User.class,
                    one = @One(select = "com.yun.mapper.IUserMapper.getUserById")),
    })
    @Select("select * from orders")
    public List<Order> findOrderAndUser();
}
​
public interface IUserMapper {
    @Select("select * from user where id = #{id}")
    public User getUserById(Integer id);
}
@Test
    public void oneToOne(){
        List<Order> orderAndUser = orderMapper.findOrderAndUser();
        orderAndUser.forEach(item -> {
            System.out.println(item);
        });
    }

執行結果:

Order(id=1, orderTime=2022-05-01, total=1000.00, user=User(id=1, username=Tom, orders=null, roles=null))
Order(id=2, orderTime=2022-05-10, total=2000.00, user=User(id=2, username=Jerry, orders=null, roles=null))
Order(id=3, orderTime=2022-05-20, total=3000.00, user=User(id=2, username=Jerry, orders=null, roles=null))

一對多

public interface IUserMapper {
​
    /**
     * 查詢所有使用者資訊,同時查詢出每個使用者關聯的訂單資訊
     * @return
     */
    @Select("select * from user")
    @Results({
            @Result(property = "id",column = "id"),
            @Result(property = "username",column = "username"),
            @Result(property = "orders",column = "id",javaType = List.class,
                    many = @Many(select = "com.yun.mapper.IOrderMapper.getOrderByUid"))
    })
    public List<User> findAll();
}
​
public interface IOrderMapper {
    @Select("select * from orders where uid = #{uid}")
    public List<Order> getOrderByUid(Integer uid);
}
@Test
    public void oneToMore(){
        List<User> users = mapper.findAll();
        users.forEach(item -> {
            System.out.println(item);
        });
​
    }

執行結果:

User(id=1, username=Tom, orders=[Order(id=1, orderTime=null, total=1000.00, user=null)], roles=null)
User(id=2, username=Jerry, orders=[Order(id=2, orderTime=null, total=2000.00, user=null), Order(id=3, orderTime=null, total=3000.00, user=null)], roles=null)

多對多

public interface IUserMapper {
    
    /**
    * 查詢所有使用者資訊,同事查詢出每個使用者關聯的角色資訊
    * @return
    */
    @Select("select * from user")
    @Results({
            @Result(property = "id",column = "id"),
            @Result(property = "username",column = "username"),
            @Result(property = "roles",column = "id",javaType = List.class,
                    many = @Many(select ="com.yun.mapper.IRoleMapper.getAll"))
    })
    public List<User> findUserAndRole();
}
​
public interface IRoleMapper {
​
    @Select("select * from sys_role sr,sys_user_role sur where sr.id = sur.role_id and sur.user_id = #{uid}")
    public List<Role> getAll(Integer uid);
}
@Test
public void moreToMore(){
    
    List<User> users = mapper.findUserAndRole();
    users.forEach(item -> {
        System.out.println(item);
    });
}

執行結果:

User(id=1, username=Tom, orders=null, roles=[Role(id=1, roleName=董事長), Role(id=2, roleName=經理)])
User(id=2, username=Jerry, orders=null, roles=[Role(id=1, roleName=董事長), Role(id=2, roleName=經理)])

Mybatis快取

基礎概念

快取就是記憶體中的資料,常常來自對資料庫查詢結果的儲存,使用快取,我們可以避免頻繁的與資料庫進行互動,進而提高響應速度.

一級快取是SqlSession,在運算元據庫時需要構造sqlSession物件,在物件中有一個資料結構(HashMap)用於儲存快取資料.不同的sqlSession之間互不影響.

二級快取是mapper級別的快取,多個sqlSession去操作同一個mapper的sql語句,多個sqlSession可以共用二級快取,二級快取是跨sqlSession的.

一級快取

demo

public class CacheTest {
​
    private IUserMapper mapper;
    private SqlSession sqlSession;
​
    @Before
    public void before() throws IOException {
        //1.Resources工具類,組態檔的載入,把組態檔載入成位元組輸入流
        InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
        //2.解析了組態檔,並建立了sqlSessionFactory工廠
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //3.生產sqlSession                          true:事務自動提交
        sqlSession = sqlSessionFactory.openSession(true);
        mapper = sqlSession.getMapper(IUserMapper.class);
    }
​
    @Test
    public void test1() {
​
        //第一次查詢id為1的使用者
        User user1 = mapper.getUserById(1);
​
        //第二次查詢id為1的使用者
        User user2 = mapper.getUserById(1);
​
        System.out.println(user1 == user2);
    }
}

返回結果為 true ;測試一級快取是預設開啟的

結論:第一次發起查詢使用者id為1的使用者資訊,先去找快取中是否有id為1的使用者,如果沒有,則從資料庫查詢使用者資訊,得到使用者資訊並且將使用者資訊儲存到一級快取中,第二次發起查詢使用者id為1的使用者資訊,先去找快取中是否有id為1的使用者,如果快取中有,直接從快取中獲取使用者資訊;

現在我們變換一下上面的demo

public class CacheTest {
​
    private IUserMapper mapper;
    private SqlSession sqlSession;
​
    @Before
    public void before() throws IOException {
        //1.Resources工具類,組態檔的載入,把組態檔載入成位元組輸入流
        InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
        //2.解析了組態檔,並建立了sqlSessionFactory工廠
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //3.生產sqlSession                          true:事務自動提交
        sqlSession = sqlSessionFactory.openSession();
        mapper = sqlSession.getMapper(IUserMapper.class);
    }
​
    @Test
    public void test1() {
​
        //第一次查詢id為1的使用者
        User user1 = mapper.getUserById(1);
​
        User user = new User();
        user.setId(1);
        user.setUsername("Lucy");
        mapper.updateUser(user);
​
        sqlSession.commit();
​
        //第二次查詢id為1的使用者
        User user2 = mapper.getUserById(1);
​
        System.out.println(user1 == user2);
    }
}

返回結果為 false

結論:做增刪改操作,並進行了事物的提交,就會重新整理以及快取;或者還可以通過 sqlSession.clearCache()清楚快取;這樣做的目的就是為了讓快取中儲存的是最新的資訊,避免髒讀;

二級快取

二級快取的原理和一級快取原理一樣,第一次查詢會將資料放入快取中,然後第二次查詢則會直接從快取中獲取,但是一級快取是基於sqlSession的,而二級快取是基於mapper檔案的namespace,也就是說,多個sqlSession可以共用一個mapper中的二級快取,並且如果兩個mapper的namespace相同,即使是兩個mapper,那麼這兩個mapper執行sql查詢到的資料也將存在相同的二級快取區域中.

如何使用二級快取

首先在全域性組態檔sqlMapconfig.xml檔案加入如下程式碼

<settings>
        <setting name="cacheEnabled" value="true"/>
</settings>

注意,該設定需要放在properties標籤下,具體順序,可百度瞭解

其次,在xxxMapper.xml檔案中開啟快取(如果當前操作時基於註解開發的話,使用註解@CacheNamespace)

<cache></cache>

demo2

public class CacheTest {
​
    private IUserMapper mapper;
    private SqlSession sqlSession;
    private SqlSessionFactory sqlSessionFactory;
​
    @Before
    public void before() throws IOException {
        //1.Resources工具類,組態檔的載入,把組態檔載入成位元組輸入流
        InputStream inputStream = Resources.getResourceAsStream("sqlMApConfig.xml");
        //2.解析了組態檔,並建立了sqlSessionFactory工廠
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //3.生產sqlSession
        sqlSession = sqlSessionFactory.openSession();
        mapper = sqlSession.getMapper(IUserMapper.class);
    }
​
    @Test
    public void test2() {
​
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        SqlSession sqlSession3 = sqlSessionFactory.openSession();
​
        IUserMapper mapper1 = sqlSession1.getMapper(IUserMapper.class);
        IUserMapper mapper2 = sqlSession2.getMapper(IUserMapper.class);
        IUserMapper mapper3 = sqlSession3.getMapper(IUserMapper.class);
​
        User user1 = mapper1.getUserById(1);
        //清空一級快取
        sqlSession1.close();
        User user2 = mapper2.getUserById(1);
​
        System.out.println(user1 == user2);
    }
}

執行結果為 false

結論:通過debug斷點顯示,實際上,第二次查詢則會直接從快取中獲取使用者資訊了,不過二級快取快取的不是物件,而是快取的物件中的資料,所以查詢結果為false;

注意,二級快取底層還是HashMap結構,所以 po類需要實現序列化介面 ;因為二級快取資料儲存媒介多種多樣,不一定只存在記憶體中,有可能存在硬碟中,如果我們要在取這個快取的話,就需要反序列化了,所以mybatis中的pojo都去實現Serializable介面;

變換一下demo2

@Test
    public void test2() {
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        SqlSession sqlSession3 = sqlSessionFactory.openSession();
        IUserMapper mapper1 = sqlSession1.getMapper(IUserMapper.class);
        IUserMapper mapper2 = sqlSession2.getMapper(IUserMapper.class);
        IUserMapper mapper3 = sqlSession3.getMapper(IUserMapper.class);
        User user1 = mapper1.getUserById(1);
        //清空一級快取
        sqlSession1.close();
        User user = new User();
        user.setId(1);
        user.setUsername("Tom");
        mapper3.updateUser(user);
        sqlSession3.commit();
        User user2 = mapper2.getUserById(1);
        System.out.println(user1 == user2);
    }

返回結果為 false

結論:做增刪改操作,並進行了事物的提交,就會重新整理以及快取;這樣做的目的就是為了讓快取中儲存的是最新的資訊,避免髒讀;

此外mybatis中還可以設定useCache和flushCache等設定項;

useCache

是用來設定是否禁用二級快取的,在statement中設定useCache=false可以禁用當前select語句的二級快取,即每次查詢都會會發出sql去查詢,預設情況是true,即該sql使用二級快取,例如

<select id="findAll" useCache = "false" resultMap="userMap">
        select u.*,o.id oid,o.order_time,o.total,o.uid from user u left join orders o on u.id = o.uid
</select>

使用sql註解方式可以使用@Options(useCache = false)的方式

flushCache

在mapper的同一個namespace中,如果有其他的insert,update,delete運算元據後需要重新整理快取,如果不執行重新整理快取會出現髒讀,設定statememt設定中的

flushCache = "true"屬性,預設情況下為true,即重新整理快取,如果改成false則不會重新整理,使用快取時如果手動修改資料庫表中的查詢資料會出現髒讀 例如

<select id="findAll" flushCache = "true" useCache = "false" resultMap="userMap">
        select u.*,o.id oid,o.order_time,o.total,o.uid from user u left join orders o on u.id = o.uid
</select>

一般下執行完commit操作都需要重新整理快取,flushCache=true表示重新整理快取,這樣可以避免資料庫髒讀,所以我們不用設定,預設即可

至此,mybatis基礎概念及應用回顧完成!

到此這篇關於Mybatis基礎回顧與高階應用的文章就介紹到這了,更多相關Mybatis高階應用內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com