1. 程式人生 > >Mybatis(3、延遲載入、查詢快取、與ehcache整合、逆向工程、與spring整合)

Mybatis(3、延遲載入、查詢快取、與ehcache整合、逆向工程、與spring整合)

版權宣告:本文為博主原創文章,未經博主允許不得轉載。    https://blog.csdn.net/www1056481167/article/details/70597788
延遲載入
延遲載入:先從單表查詢、需要時再從關聯表去關聯查詢,大大提高 資料庫效能,因為查詢單表要比關聯查詢多張錶速度要快。

使用association實現延遲載入
需要先定義連個mapper的方法對應的statements。
1、只查詢訂單資訊

在查詢訂單的statement中使用association去延遲載入(執行)下邊的statement(關聯查詢使用者資訊)

<!-- 查詢訂單關聯使用者,使用者資訊需要延遲載入 -->
<select id="findOrdersUserLazyLoading" resultMap="OrdersUserLazyLoadingResultMap">
    SELECT * FROM orders
</select>
2、關聯查詢使用者資訊

      通過上邊查詢到的訂單資訊中user_id去關聯查詢使用者資訊

         使用UserMapper.xml中的findUserById
<select id="findUserById" parameterType="int" resultType="user">
        SELECT * FROM USER WHERE id=#{value}
</select>

上邊先去執行findOrdersUserLazyLoading,當需要去查詢使用者的時候先去查詢findUserBuyId,通過resultMap的定義將延遲載入執行配置起來。

延遲載入resultMap
使用association中的select指定延遲載入去執行的statement中的id

        <!-- 延遲載入的resultMap -->
    <resultMap type="cn.itcast.mybatis.po.Orders" id="OrdersUserLazyLoadingResultMap">
            <!--對訂單資訊進行對映配置  -->
            <id column="id" property="id"/>
            <result column="user_id" property="userId"/>
            <result column="number" property="number"/>
            <result column="createtime" property="createtime"/>
            <result column="note" property="note"/>
            <!-- 實現對使用者資訊進行延遲載入
            select:指定延遲載入需要執行的statement的id(是根據user_id查詢使用者資訊的statement)
            要使用userMapper.xml中findUserById完成根據使用者id(user_id)使用者資訊的查詢,
            如果findUserById不在本mapper中需要前邊加namespace
            column:訂單資訊中關聯使用者資訊查詢的列,是user_id
            關聯查詢的sql理解為:
            SELECT
                orders.*, (
                    SELECT
                        username
                    FROM
                        USER
                    WHERE
                        orders.user_id = USER .id
                ) username,
                (
                    SELECT
                        sex
                    FROM
                        USER
                    WHERE
                        orders.user_id = USER .id
                ) sex
            FROM
                orders
             -->
            <association property="user"  javaType="cn.itcast.mybatis.po.User"
             select="cn.itcast.mybatis.mapper.UserMapper.findUserById" column="user_id">
            <!-- 實現對使用者資訊進行延遲載入 -->
            </association>
    </resultMap>
Mapper.java
// 查詢訂單關聯查詢使用者,使用者資訊是延遲載入
public List<Orders> findOrdersUserLazyLoading() throws Exception;
測試
1、執行上邊mapper方法(findOrdersUserLazyLoading),內部去呼叫cn.itcast.mybatis.mapper.OrdersMapperCustom中的findOrdersUserLazyLoading只查詢orders資訊(單表)。

2、在程式中去遍歷上一步驟查詢出的List<Orders>,當我們呼叫Orders中的getUser方法時,開始進行延遲載入。

3、延遲載入,去呼叫UserMapper.xml中findUserbyId這個方法獲取使用者資訊。
延遲載入的配置
mybatis預設沒有開啟延遲載入,需要在SqlMapConfig.xml中setting配置。

在mybatis核心配置檔案中配置:

lazyLoadingEnabled、aggressiveLazyLoading

mybatis.xml的配置

<settings>
        <!-- 開啟延遲載入 的開關 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 將積極載入改為消極載入即按需要載入 -->
        <setting name="aggressiveLazyLoading" value="false"/>
</settings>

查詢快取
一級快取
mybatis提供查詢快取,用於減輕資料壓力,提高資料庫效能。

mybaits提供一級快取,和二級快取。


一級快取是SqlSession級別的快取。在操作資料庫時需要構造 sqlSession物件,在物件中有一個數據結構(HashMap)用於儲存快取資料。不同的sqlSession之間的快取資料區域(HashMap)是互相不影響的。 

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

為什麼要用快取?

如果快取中有資料就不用從資料庫中獲取,大大提高系統性能。
工作原理:

一級快取的測試
@Test
    public void testCache1() throws Exception{
        SqlSession sqlSession = sqlSessionFactory.openSession();//建立代理物件
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);        
        //下邊查詢使用一個SqlSession
        //第一次發起請求,查詢id為1的使用者
        User user1 = userMapper.findUserById(1);
        System.out.println(user1);        
//        如果sqlSession去執行commit操作(執行插入、更新、刪除),清空SqlSession中的一級快取,這樣做的目的為了讓快取中儲存的是最新的資訊,避免髒讀。        
        //更新user1的資訊
        user1.setUsername("測試使用者22");
        userMapper.updateUser(user1);
        //執行commit操作去清空快取
        sqlSession.commit();        
        //第二次發起請求,查詢id為1的使用者
        User user2 = userMapper.findUserById(1);
        System.out.println(user2);
        
        sqlSession.close();
        
    }
應用:

正式開發,是將mybatis和spring進行整合開發,事務控制在service中。

一個service方法中包括很多mapper方法呼叫。

service{

      //開始執行時,開啟事務,建立SqlSession物件

      //第一次呼叫mapper的方法findUserById(1)

      //第二次呼叫mapper的方法findUserById(1),從一級快取中取資料

      //方法結束,sqlSession關閉

}

如果是執行兩次service呼叫查詢相同的使用者資訊,不走一級快取,因為session方法結束,sqlSession就關閉,一級快取就清空。


二級快取
原理:

首先開啟mybatis的二級快取。

sqlSession1去查詢使用者id為1的使用者資訊,查詢到使用者資訊會將查詢資料儲存到二級快取中。

如果SqlSession3去執行相同 mapper下sql,執行commit提交,清空該 mapper下的二級快取區域的資料。

sqlSession2去查詢使用者id為1的使用者資訊,去快取中找是否存在資料,如果存在直接從快取中取出資料。

二級快取與一級快取區別,二級快取的範圍更大,多個sqlSession可以共享一個UserMapper的二級快取區域。

UserMapper有一個二級快取區域(按namespace分) ,其它mapper也有自己的二級快取區域(按namespace分)。

每一個namespace的mapper都有一個二快取區域,兩個mapper的namespace如果相同,這兩個mapper執行sql查詢到資料將存在相同 的二級快取區域中。
開啟二級快取

mybaits的二級快取是mapper範圍級別,除了在SqlMapConfig.xml設定二級快取的總開關,還要在具體的mapper.xml中開啟二級快取。

1、在核心配置檔案SqlMapConfig.xml中加入

                <!-- 開啟二級快取 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>
2、在UserMapper.xml中開啟二級快取,UserMapper.xml下的sql執行完成會儲存到它的快取區域(hashMap)

<mapper namespace="test">
     <!—開啟本namespace的二級快取-->
     <cache/>
呼叫pojo實現序列化介面

public class User implements Serializable {    
    //屬性名和資料庫表的欄位對應
    private int id;
    private String username;// 使用者姓名
    private String sex;// 性別
    private Date birthday;// 生日
    private String address;// 地址

為了將快取資料提取出執行反序列化操作,因為二級快取資料儲存介質多種多樣,不一樣的記憶體。

測試方法

// 二級快取測試
    @Test
    public void testCache2() throws Exception {
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        SqlSession sqlSession3 = sqlSessionFactory.openSession();
        // 建立代理物件
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        // 第一次發起請求,查詢id為1的使用者
        User user1 = userMapper1.findUserById(1);
        System.out.println(user1);        
        //這裡執行關閉操作,將sqlsession中的資料寫到二級快取區域
        sqlSession1.close();        
        //使用sqlSession3執行commit()操作
        UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
        User user  = userMapper3.findUserById(1);
        user.setUsername("張明明");
        userMapper3.updateUser(user);
        //執行提交,清空UserMapper下邊的二級快取
        sqlSession3.commit();
        sqlSession3.close();
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
        // 第二次發起請求,查詢id為1的使用者
        User user2 = userMapper2.findUserById(1);
        System.out.println(user2);
        sqlSession2.close();
    }
useCache配置

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

<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

總結:針對每次查詢都需要最新的資料sql,要設定成useCache=false,禁用二級快取。

重新整理快取

在mapper的同一個namespace中,如果有其它insert、update、delete操作資料後需要重新整理快取,如果不執行重新整理快取會出現髒讀。

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

如下:
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
總結:一般下執行完commit操作都需要重新整理快取,flushCache=true表示重新整理快取,這樣可以避免資料庫髒讀。

mybatis整合ehcache
依賴jar
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache-core</artifactId>
    <version>2.6.6</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.0.0</version>
</dependency>
ehcache:是一個分散式的快取框架

目的:為了提高系統併發,效能、一般對系統進行分散式部署(叢集部署方式)。

分散式方法
mybatis提供了一個cache介面,如果要實現自己的快取邏輯,實現cache介面開發即可。

mybatis和ehcache整合,mybatis和ehcache整合包中提供了一個cache介面的實現類。

mybatis預設實現cache類


整合ehcache
配置mapper中cache中的type為ehcache對cache介面的實現類。
<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">
<!-- 開啟mapper的namespace下的二級快取
type:指定cache介面的實現類的型別,mybatis預設使用PerpetualCache要和ehcache整合,
需要配置type為ehcache實現cache介面型別 
-->
 <cache type="cn.itcast.mybatis.util.PerpetualCache"/>
加入ehcache配置檔案
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <diskStore path="F:\develop\ehcache" />
    <defaultCache 
        maxElementsInMemory="1000" 
        maxElementsOnDisk="10000000"
        eternal="false" 
        overflowToDisk="false" 
        timeToIdleSeconds="120"
        timeToLiveSeconds="120" 
        diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>
spring和mybatis整合
在applicationContext.xml配置sqlSessionFactory和資料來源。

<!-- 載入配置檔案 -->
    <context:property-placeholder location="classpath:db.properties" />
    <!-- 資料來源,使用dbcp -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="maxActive" value="10" />
        <property name="maxIdle" value="5" />
    </bean>
    <!-- sqlSessinFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 載入mybatis的配置檔案 -->
        <property name="configLocation" value="mybatis/SqlMapConfig.xml" />
        <!-- 資料來源 -->
        <property name="dataSource" ref="dataSource" />
    </bean>
原始dao開發
dao介面
public interface UserDao {
    // 根據id查詢使用者資訊
    public User findUserById(int id) throws Exception;
}
實現類
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
    @Override
    public User findUserById(int id) throws Exception {
        //繼承SqlSessionDaoSupport,通過this.getSqlSession()得到sqlSessoin
        SqlSession sqlSession = this.getSqlSession();
        User user = sqlSession.selectOne("test.findUserById", id);
        return user;
    }
}
dao配置
<!-- 原始dao介面 -->    
    <bean id="userDao" class="cn.itcast.ssm.dao.UserDaoImpl">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
測試程式碼
public class UserDaoImplTest {
    private ApplicationContext applicationContext;
    //在setUp這個方法得到spring容器
    @Before
    public void setUp() throws Exception {
        applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml");
    }
    @Test
    public void testFindUserById() throws Exception {
        UserDao userDao = (UserDao) applicationContext.getBean("userDao");
        //呼叫userDao的方法
        User user = userDao.findUserById(1);
        System.out.println(user);
    }
}
mapper代理開發
mapper.xml和mapper.java

通過MapperScannerConfigurer進行掃描(建議使用)
<!-- mapper批量掃描,從mapper包中掃描出mapper介面,自動建立代理物件並且在spring容器中註冊 
    遵循規範:將mapper.java和mapper.xml對映檔名稱保持一致,且在一個目錄 中
    自動掃描出來的mapper的bean的id為mapper類名(首字母小寫)
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 指定掃描的包名 
        如果掃描多個包,每個包中間使用半形逗號分隔
        -->
        <property name="basePackage" value="cn.itcast.ssm.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>        
    </bean>    
測試程式碼
@Test
    public void testFindUserById() throws Exception {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
                "classpath:spring/applicationContext.xml");
        UserMapper userMapper = (UserMapper) applicationContext
                .getBean("userMapper");
        User user = userMapper.findUserById(1);
        System.out.println(user);
    }
逆向工程
下載依賴
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.2</version>
</dependency>

配置檔案:
修改generatorConfig.xml的配置資訊
主要修改的內容有:
1、jdbcConnection的資料庫連線資訊。
2、sqlMapGenerator的targetPackage,mapper.xml對映檔案生成存放的位置
3、table將需要生成的表的表名都配置到配置檔案中
<?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/mybatis" userId="root"
            password="mysql">
        </jdbcConnection>
        <!-- 預設false,把JDBC DECIMAL 和 NUMERIC 型別解析為 Integer,為 true時把JDBC DECIMAL 
            和 NUMERIC 型別解析為java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>
 
        <!-- targetProject:生成PO類的位置 -->
        <javaModelGenerator targetPackage="cn.itcast.ssm.po"
            targetProject=".\src">
            <!-- enableSubPackages:是否讓schema作為包的字尾 -->
            <property name="enableSubPackages" value="false" />
            <!-- 從資料庫返回的值被清理前後的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- targetProject:mapper對映檔案生成的位置 **需要配置**-->
        <sqlMapGenerator targetPackage="cn.itcast.ssm.mapper"
            targetProject=".\src">
            <!-- enableSubPackages:是否讓schema作為包的字尾-->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- targetPackage:mapper介面生成的位置**需要配置** -->
        <javaClientGenerator type="XMLMAPPER"
            targetPackage="cn.itcast.ssm.mapper" targetProject=".\src">
            <!-- enableSubPackages:是否讓schema作為包的字尾 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>
        <!-- 指定資料庫表**需要配置**-->
        <table tableName="items"></table>
    </context>
</generatorConfiguration>
執行生成的程式
@Test
    public void aa() throws Exception {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("generatorConfig.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);
    }

生成後的程式碼

--------------------- 
作者:Koma-forever 
來源:CSDN 
原文:https://blog.csdn.net/www1056481167/article/details/70597788 
版權宣告:本文為博主原創文章,轉載請附上博文連結!