Mybatis學習筆記

1. HelloWorld

導入mybatisjava

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>

1.1 配置文件法

從XML中構建SqlSessionFactorymysql

<?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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/employee-mapper.xml"/>
    </mappers>
</configuration>

mapperspring

<?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">
<!--
 namespace: 名稱空間
 id: 惟一標識
 returnType: 返回值類型
 #{id} 從傳遞過來的參數中取出id值
-->
<mapper namespace="com.meituan.mybatis.bean.EmployeeMapper">
    <select id="selectEmp" resultType="com.meituan.mybatis.bean.Employee">
        select * from employee where id = #{id}
    </select>
</mapper>

JavaBeansql

public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private String gender;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", lastName='" + lastName + '\'' +
                ", email='" + email + '\'' +
                ", gender='" + gender + '\'' +
                '}';
    }
}

單元測試數據庫

/**
     *  1. 根據xml配置文件(全局配置文件)建立一個SqlSessionFactory對象
     *      有數據源一些運行環境信息    
     *  2.  sql映射文件,配置了每個sql,以及sql的封裝規則等
     *  3. 將sql映射文件註冊在全局配置文件中
     *  4. 寫代碼
     *      1) 根據全局配置文件獲得SqlSessionFactory
     *      2) 經過SqlSession工廠獲取到SqlSession,使用SqlSession執行增刪改查,一個SqlSession就是表明和數據庫的一次會話,用完關閉
     *      3) 使用sql的惟一標識(id)來告訴mybatis執行哪一個sql,sql所有保存在sql映射文件(mapper)中
     * @throws Exception
     */
    @Test
    public void test() throws Exception{
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //2. 獲取SqlSession實例,能直接執行已經映射的sql語句
        SqlSession sqlSession = sqlSessionFactory.openSession();

        /**
         * Retrieve a single row mapped from the statement key and parameter.
         * @param <T> the returned object type
         * @param statement Unique identifier matching the statement to use. 傳入惟一標識
         * @param parameter A parameter object to pass to the statement. 傳入參數
         * @return Mapped object
         */
        Employee employee = sqlSession.selectOne("com.meituan.mybatis.bean.EmployeeMapper.selectEmp", 1);
        System.out.println(employee);

    }

駝峯命名法問題:express

mybatis-config.xml配置文件配置時,要注意節點順序apache

<properties>...</properties>
<settings>...</settings>
<typeAliases>...</typeAliases>
<typeHandlers>...</typeHandlers>
<objectFactory>...</objectFactory>
<objectWrapperFactory>...</objectWrapperFactory>
<plugins>...</plugins>
<environments>...</environments>
<databaseIdProvider>...</databaseIdProvider>
<mappers>...</mappers>

增長設置編程

支持駝峯命名法
<setting name="mapUnderscoreToCamelCase" value="true"/>

1.2 接口式編程

配置文件數組

<?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">
<!--
 namespace: 名稱空間;接口式編程中,須指定爲接口的全類名
 id: 惟一標識
 returnType: 返回值類型
 #{id} 從傳遞過來的參數中取出id值;接口式編程中,id改成方法名
-->
<mapper namespace="com.meituan.mybatis.dao.EmployeeMapper">
    <select id="getEmpById" resultType="com.meituan.mybatis.bean.Employee">
        select * from employee where id = #{id}
    </select>
</mapper>

mapper接口緩存

@Mapper
public interface EmployeeMapper {

    public Employee getEmpById(Integer id);
}

單元測試

@Test
    public void test01() throws Exception {
        //1. 獲取SqlSessionFactory對象
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        
        //2. 獲取SqlSession對象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        
        try {
            //3. 獲取接口的實現類對象
            // mybatis會爲接口自動建立一個代理對象,代理對象去執行增刪改查方法
            EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
            Employee employee = mapper.getEmpById(1);
            System.out.println(employee);
        } finally {
            sqlSession.close();
        }

    }

    private SqlSessionFactory getSqlSessionFactory() throws Exception{
        String resources = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(resources);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        return sqlSessionFactory;
    }

1.3 總結

  1. 接口式編程

    原生: Dao ====> DaoImpl

    mybatis: Mapper ==== > xxMapper.xml

  2. SqlSession表明和數據庫的一次,用完必須關閉
  3. SqlSession和connection同樣,都是非線程安全的,每次使用都應該去獲取新的對象,不能寫爲成員變量。
  4. mapper接口沒有實現類,可是mybatis會爲這個接口生成一個代理對象
  5. 兩個重要的配置文件

    全局配置文件:mybatis-config.xml 包含數據庫鏈接池信息,事務管理器信息等系統運行環境

    sql映射文件:保存了每個sql語句的映射信息

2. Mybatis全局配置文件

<?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>

    <!--
        1. mybatis可使用properties來引入外部properties配置文件的內容
            屬性:resource:引入類路徑下的資源
                        url:引入網絡路徑或者磁盤路徑下的資源
    -->
    <properties resource="dbconfig.properties">
    </properties>

    <!--
        2. 這是 MyBatis 中極爲重要的調整設置,它們會改變 MyBatis 的運行時行爲。
            setting:用來設置每個設置項
                name:設置項名
                value:設置項取值
    -->
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    
    <!--
        3.  typeAliases 類型別名是爲 Java 類型設置一個短的名字。它只和 XML 配置有關,存在的意義僅在於用來減小類徹底限定名的冗餘。
            別名不區分大小寫
            typeAlias:爲某個java類型起別名
            type:指定要起別名的類型全類名;默認別名就是類名小寫:employee
            alias:指定新的別名
            
            <package> :爲某個包下的全部類批量起別名
                    每個在包 domain.blog 中的 Java Bean,在沒有註解的狀況下,會使用 Bean 的首字母小寫的非限定類名來做爲它的別名。 好比 domain.blog.Author 的別名爲 author;如有註解,則別名爲其註解值。
                    name:指定包名(爲當前包以及下面全部的後代包的每一類都起一個包名)
                    
                    若是子包下有相同的類名,mybatis會起相同的包名從而報錯。可使用@Alias給特定類指定別名
    -->
    <typeAliases>
        <!--<typeAlias type="com.meituan.mybatis.config.bean.Employee" alias="emp"></typeAlias>-->
        <package name="com.meituan.mybatis.config"/>
    </typeAliases>
    
    <!--
        4. typeHandler  不管是 MyBatis 在預處理語句(PreparedStatement)中設置一個參數時,仍是從結果集中取出一個值時, 都會用類型處理器將獲取的值以合適的方式轉換成 Java 類型。
    -->
    
    <!--
        5. plugins 插件
    -->
    
    <!--
        6. environment  
        MyBatis 能夠配置成適應多種環境,這種機制有助於將 SQL 映射應用於多種數據庫之中, 現實狀況下有多種理由須要這麼作。
        default指定使用某種環境來切換環境,能夠達到快速切換環境。
        environment配置一個具體的環境信息,id表明當前環境的惟一標識,必須有兩個標籤:
            transactionManager:事務管理器,
                    type指定事務管理器的類型 在 MyBatis 中有兩種類型的事務管理器(也就是 type=」[JDBC|MANAGED]」):
                        JDBC:typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
                        MANAGED:typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
                        支持自定義事務管理器,實現TransactionFactory接口便可
            dataSource:數據源
                type:數據源類型,有三種內建的數據源類型(也就是 type=」[UNPOOLED|POOLED|JNDI]」):
                支持自定義數據源,實現DataSourceFactory接口,type爲自定義數據源的全類名
    -->
    
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <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>
    
    <!--
        7. databaseIdProvider:  支持多數據庫廠商
    -->
    
    <!--
        8. mappers映射器
            mapper註冊一個sql映射
                resource:引用類路徑下的sql映射文件
                url:引用網絡路徑下或者磁盤路徑下的映射文件
                class:使用映射器接口實現類的徹底限定類名
                    1. 有sql映射文件,映射文件名必須和接口同名,而且放在與接口同一目錄下
                    2. 沒有sql映射文件,全部的sql都是利用註解寫在接口上
                package:將包內的映射器接口實現所有註冊爲映射器
    -->
    <mappers>
        <mapper resource="mapper/employee-mapper.xml"/>
    </mappers>
    
    <!--
        標籤的編寫是有順序的
    -->
</configuration>

3. Mybatis映射文件

MyBatis 的真正強大在於它的映射語句,也是它的魔力所在。因爲它的異常強大,映射器的 XML 文件就顯得相對簡單。若是拿它跟具備相同功能的 JDBC 代碼進行對比,你會當即發現省掉了將近 95% 的代碼。MyBatis 就是針對 SQL 構建的,而且比普通的方法作的更好。

3.1 獲取自增主鍵

<?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">
<!--
 namespace: 名稱空間
 id: 惟一標識
 returnType: 返回值類型
 #{id} 從傳遞過來的參數中取出id值
-->
<mapper namespace="com.meituan.mybatis.mapper.dao.EmployeeMapper">
    <select id="getEmpById" resultType="com.meituan.mybatis.mapper.bean.Employee">
        SELECT *
        FROM employee
        WHERE id = #{id}
    </select>

    <!--parameterType:能夠省略
        mysql支持自增主鍵,自增主鍵的獲取,mybatis也是利用statement.getGeneratedKeys(),
        useGeneratedKeys="true":使用自增主鍵獲取主鍵值策略
        keyProperty:指定對應的主鍵屬性,也就是mybatis獲取到主鍵值之後,將這個值封裝給JavaBean的哪一個屬性
    -->
    <insert id="addEmp" parameterType="com.meituan.mybatis.mapper.bean.Employee"
        useGeneratedKeys="true" keyProperty="id"
    >
        INSERT INTO employee (last_name, email, gender)
        VALUES (#{lastName}, #{email}, #{gender})
    </insert>

    <update id="updateEmp">
        UPDATE employee
        SET last_name = #{lastName}, email = #{email}, gender = #{gender}
        WHERE id = #{id}
    </update>

    <delete id="deleteEmpById">
        DELETE FROM employee WHERE id=#{id}
    </delete>
</mapper>

單元測試

/**
     * 1. mybatis容許增刪改直接定義如下類型返回值
     *      Integer Long Boolean
     *  2. 手動提交數據
     * @throws Exception
     */
    @Test
    public void test02() throws Exception {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        //1. 獲取到的SqlSession不會自動提交
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
            Employee employee = new Employee(null, "jerry", "jerry@tom.com", "2");
            System.out.println(employee);
            System.out.println("============");
            mapper.addEmp(employee);
            System.out.println(employee);
            //            employee.setLastName("jason");
//            employee.setId(3);
//            mapper.updateEmp(employee);
//            mapper.deleteEmpById(3);
            sqlSession.commit();
        } finally {
            sqlSession.close();
        }
    }

    private SqlSessionFactory getSqlSessionFactory() throws Exception{
        String resources = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(resources);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        return sqlSessionFactory;
    }

3.2 參數處理

1)單個參數:mybatis不會作特殊處理

2)多個參數

​ 異常:

org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2]

​ 操做:

​ 方法:public Employee getEmpByIdAndLastName(Integer id ,String lastName);

​ 取值:#{id},#{lastName}

​ mybatis會特殊處理,多個參數會被封裝成一個map

​ key:param1....paramN

​ value:傳入的參數值

​ #{}就是從map中獲取指定的key值,或者參數的索引也能夠

命名參數:

​ 明確指定封裝參數值map的key: @Param("id")

POJO:

​ 若是多個參數正好是業務邏輯的數據模型,能夠直接傳入POJO:

​ #{屬性名}:取出傳入的pojo的屬性值

Map:

若是多個參數不是業務模型中的數據,沒有對應的pojo,不常用,爲了方便,也能夠傳入map

若是多個參數不是業務模型中的數據,可是常用,推薦寫一個TO(Transfer Object) 數據傳輸對象

3)參數封裝擴展思考:

  1. public Employee getEmp(@Param("id"))Integer id, String lastName);

    取值:id==》#{id/param1} lastName===>#{param2}

  2. public Employee getEmp(Integer id, @Param("e") Employee emp);

    取值:id===》#{param1} lastName===》#{param2.LastName/e.lastName}

  3. 特別注意:若是是Collection(List、Set)類型或者數組

    也會特殊處理,也是把傳入的list或者數組封裝在map中

    ​ key:Collection(collection),若是是List還可使用(list)

    ​ 數組(array)

    public Employee getEmpById(List<Integer> ids);

    取值:取出第一個id的值:#{list[0]}

3.3 結合源碼,mybatis如何處理參數

ParamNameResolver解析參數封裝map

(1) names:(0=id, 1=lastName)

​ 1) 獲取每一個標註Param註解的參數param值:id,lastName,賦值給name

​ 2)每次解析一個參數給map中保存信息:key是索引值, value是name的值

​ name的值:

​ 標註了param註解,註解的值

​ 沒有標註:

​ 一、全局配置:useActualParamName,name=參數名(要求JDK1.8)

​ 二、name=map.size() 至關於當前元素的索引

​ names:{0=id, 1=lastName}

public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
      //1. 參數爲null直接返回
    if (args == null || paramCount == 0) {
      return null;
      //2. 若是隻有一個元素而且沒有param註解:args[0],單個參數直接返回
    } else if (!hasParamAnnotation && paramCount == 1) {
      return args[names.firstKey()];
      //3. 多個元素或者有Param標註
    } else {
      final Map<String, Object> param = new ParamMap<Object>();
      int i = 0;
      // 4. 遍歷names,構造器的時候就已經肯定
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        //names的value做爲新map的key,nameskey做爲取值的參考
        //eg:{id=args[0], lastName=args[1]},所以能夠在映射文件中取到相應的值
        param.put(entry.getValue(), args[entry.getKey()]);
        // add generic param names (param1, param2, ...)
        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }

3.4 參數值的獲取

#{}:能夠獲取map中的值或者pojo對象屬性的值

${}:能夠獲取map中的值或者pojo對象屬性的值

區別:#{}是以預編譯的形式,將參數設置到sql語句中,PreparedStatement

​ ${}:取出的值直接拼裝在sql語句中,會有安全問題

​ 大多狀況下,取參數的值都應該使用#{},在某些狀況下,原生jdbc不支持佔位符的地方可使用${}進行取值,

好比分表;按年份分表拆分 select from 2017_salary能夠寫爲 select from ${year}_salary

3.5 #{}取值規則

更豐富的用法

規定參數的一些規則:

​ javaType、jdbcType、mode(存儲過程)、numericScale、resultMap、typeHandler、jdbcTypeName、expression

jdbcType參演須要在某種特定的條件下被設置

​ 在數據爲null的時候,有些數據庫可能沒法識別mybatis對null的默認處理,如oracle,mybatis對全部的null都映射爲原生Jdbc的OTHER類型, oracle沒法處理,mysql能夠處理

一、#{email, jdbcType=OTHER}

二、全局配置文件mybatis-config.xml中:<setting name="jdbcTypeForNull" value="NULL" />

3.6 Select返回List、Map

  • 返回List
<!--resultType:若是返回的是一個集合,要寫集合中元素的類型-->
    <select id="getEmpsByLastNameLike" resultType="com.meituan.mybatis.mapper.bean.Employee">
        SELECT * FROM employee WHERE last_name LIKE #{lastName}
    </select>
  • 返回Map,key就是列名,值是對應的值
<!--map是mybatis自定義的別名-->
<select id="getEmpByIdReturnMap" resultType="map">
    SELECT * FROM employee WHERE id=#{id}
</select>
  • 多條紀錄封裝成一個map,Map<Integer, Employee> 鍵是這條紀錄的主鍵,值是記錄封裝的JavaBean
<select id="getEmpByLastNameLikeReturnMap" resultType="com.meituan.mybatis.mapper.bean.Employee">
        SELECT * FROM employee WHERE last_name LIKE #{lastName}
    </select>
@MapKey("id")
public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName);

3.7 自定義結果映射封裝規則

resultMap 元素是 MyBatis 中最重要最強大的元素。它可讓你從 90% 的 JDBC ResultSets 數據提取代碼中解放出來, 並在一些情形下容許你作一些 JDBC 不支持的事情。 實際上,在對複雜語句進行聯合映射的時候,它極可能能夠代替數千行的同等功能的代碼。 ResultMap 的設計思想是,簡單的語句不須要明確的結果映射,而複雜一點的語句只須要描述它們的關係就好了。

<resultMap id="myEmp" type="com.meituan.mybatis.mapper.bean.Employee">
        <!--column指定哪一列, property指定對應JavaBean屬性
            id:指定主鍵列的封裝規則,會在底層優化規則
        -->
        <id column="id" property="id"/>
        <!--定義普通列的封裝規則-->
        <result column="last_name" property="lastName"/>
        <!--其餘不指定的列會自動封裝,推薦只要寫resultMap,就將全列的映射規則都寫上-->
        <result column="email" property="email"/>
        <result column="gender" property="gender"/>
    </resultMap>
    
    <select id="getEmpById" resultMap="myEmp">
        SELECT *
        FROM employee
        WHERE id = #{id}
    </select>

3.8 關聯查詢

  • 第一種resultMap的寫法:
<!--
        場景一:
            查詢Employee的同時查詢員工所在的部門
    -->
    <resultMap id="myDifEmp" type="com.meituan.mybatis.mapper.bean.Employee">
        <id column="id" property="id" />
        <result column="last_name" property="lastName"/>
        <result column="email" property="email"/>
        <result column="gender" property="gender"/>
        <result column="did" property="dept.id"/>
        <result column="dept_name" property="dept.departmentName"/>
    </resultMap>
    <select id="getEmpAndDept" resultMap="myDifEmp">
        SELECT e.id id, e.last_name lastName, e.email email, e.gender gender, e.d_id d_id, d.id did , d.dept_name dept_name from employee e, department d 
        WHERE e.d_id = d.id AND e.id=#{id}
    </select>
  • 第二種resultMap的寫法
<resultMap id="myDifEmp2" type="com.meituan.mybatis.mapper.bean.Employee">
        <id column="id" property="id" />
        <result column="last_name" property="lastName"/>
        <result column="email" property="email"/>
        <result column="gender" property="gender"/>
        <!--
        association能夠指定聯合的JavaBean對象
            property ="dept" 指定哪一個屬性是聯合的對象
            javaType:指定這個屬性對象的類型
        -->

        <association property="dept" javaType="com.meituan.mybatis.mapper.bean.Department">
            <id column="did" property="id"/>
            <result column="dept_name" property="departmentName"/>
        </association>
    </resultMap>
  • 使用association進行分步查詢
<resultMap id="myEmpByStep" type="com.meituan.mybatis.mapper.bean.Employee">
        <id column="id" property="id" />
        <result column="last_name" property="lastName"/>
        <result column="email" property="email"/>
        <result column="gender" property="gender"/>
        <!--association定義關聯對象的封裝規則
            select:代表當前屬性是調用select指定的方法查出的結果
            column:指定將哪一列的值傳給這個方法
        -->
        <association property="dept" select="com.meituan.mybatis.mapper.dao.DepartmentMapper.getDeptById"
            column="d_id"
        ></association>
    </resultMap>

    <select id="getEmpByIdStep" resultMap="myEmpByStep">
        SELECT * FROM employee WHERE id=#{id}
    </select>

其中association中select的部分爲

<mapper namespace="com.meituan.mybatis.mapper.dao.DepartmentMapper">
    <select id="getDeptById" resultType="com.meituan.mybatis.mapper.bean.Department">
      SELECT* FROM department WHERE id=#{id}
    </select>
    
</mapper>

3.9 延遲加載

<!--可使用延遲加載
    Employee===》Dept
        每次查詢Employee對象的時候,都將部門信息一塊兒查詢出來,而須要是:
         部門信息在使用的時候再去查詢
         分步查詢的基礎之上,加上兩個配置,便可實現延遲加載
-->

mybatis-config.xml

<!--顯示指定每一個須要更改的配置的值,即便是默認的,以防版本更替帶來的問題-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>

測試

@Test
    public void test07() throws Exception {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        SqlSession sqlSession = sqlSessionFactory.openSession();

        try {
            Employee employee = sqlSession.selectOne("com.meituan.mybatis.mapper.dao.EmployeeMapperPlus.getEmpByIdStep", 1);
            System.out.println(employee.getLastName());
            System.out.println(employee.getDept().getDeptName());
        } finally {
            sqlSession.close();
        }
    }

輸出:

在兩次輸出中出現一段sql查詢

14:23:18.093 [main] DEBUG com.meituan.mybatis.mapper.dao.EmployeeMapperPlus.getEmpByIdStep - ==>  Preparing: SELECT * FROM employee WHERE id=? 
14:23:18.133 [main] DEBUG com.meituan.mybatis.mapper.dao.EmployeeMapperPlus.getEmpByIdStep - ==> Parameters: 1(Integer)
14:23:18.227 [main] DEBUG com.meituan.mybatis.mapper.dao.EmployeeMapperPlus.getEmpByIdStep - <==      Total: 1
tom
14:23:18.228 [main] DEBUG com.meituan.mybatis.mapper.dao.DepartmentMapper.getDeptById - ==>  Preparing: SELECT* FROM department WHERE id=? 
14:23:18.228 [main] DEBUG com.meituan.mybatis.mapper.dao.DepartmentMapper.getDeptById - ==> Parameters: 2(Integer)
14:23:18.269 [main] DEBUG com.meituan.mybatis.mapper.dao.DepartmentMapper.getDeptById - <==      Total: 1
銷售部

3.10 collection定義關聯集合封裝規則

  • 嵌套結果集的方式
<!--場景二
    查詢部門的時候將部門對應的全部員工信息也查詢出來
-->
    <resultMap id="MyDept" type="com.meituan.mybatis.mapper.bean.Department">
        <id column="did" property="id" />
        <result column="dept_name" property="deptName"/>
        <!--collection定義關聯集合類型的屬性的封裝規則-->
        <collection property="emps" ofType="com.meituan.mybatis.mapper.bean.Employee">
            <!--定義這個今本中元素的封裝規則-->
            <id column="eid" property="id"/>
            <result column="last_name" property="lastName"/>
            <result column="email" property="email"/>
            <result column="gender" property="gender"/>
        </collection>
    </resultMap>
    <select id="getDeptByIdPlus" resultMap="MyDept">
        SELECT d.id did, d.dept_name dept_name, e.id eid, e.last_name last_name,
            e.email email, e.gender gender
        FROM department d 
        LEFT JOIN employee e 
        ON d.id=e.d_id
        WHERE d.id=#{id}
    </select>
  • 分步方式
<resultMap id="MyDeptStep" type="com.meituan.mybatis.mapper.bean.Department">
        <id column="id" property="id"/>
        <result column="departmentName" property="deptName"/>
        <collection property="emps" select="com.meituan.mybatis.mapper.dao.EmployeeMapper.getEmpByDeptId"
            column="id"
        ></collection>
    </resultMap>
    <select id="getDeptByIdStep" resultMap="MyDeptStep">
        SELECT id,dept_name departmentName FROM department WHERE id=#{id}
    </select>
  • 擴展:

    多列傳值

    ​ 封裝成map傳遞

    ​ column="{key1=val1, key2=val2}"

    fetchType="lazy",表示使用延遲加載

    - lazy:延遲加載
    • eager:當即加載

3.11 descriminator 鑑別器

<discriminator javaType="int" column="draft">
  <case value="1" resultType="DraftPost"/>
</discriminator>

有時一個單獨的數據庫查詢也許返回不少不一樣 (可是但願有些關聯) 數據類型的結果集。 鑑別器元素就是被設計來處理這個狀況的, 還有包括類的繼承層次結構。 鑑別器很是容易理 解,由於它的表現很像 Java 語言中的 switch 語句。

定義鑑別器指定了 column 和 javaType 屬性。 列是 MyBatis 查找比較值的地方。 JavaType 是須要被用來保證等價測試的合適類型(儘管字符串在不少情形下都會有用)。好比:

<resultMap id="vehicleResult" type="Vehicle">
  <id property="id" column="id" />
  <result property="vin" column="vin"/>
  <result property="year" column="year"/>
  <result property="make" column="make"/>
  <result property="model" column="model"/>
  <result property="color" column="color"/>
  <discriminator javaType="int" column="vehicle_type">
    <case value="1" resultMap="carResult"/>
    <case value="2" resultMap="truckResult"/>
    <case value="3" resultMap="vanResult"/>
    <case value="4" resultMap="suvResult"/>
  </discriminator>
</resultMap>

4. 動態sql

MyBatis 的強大特性之一即是它的動態 SQL。 mybatis支持OGNL表達式。

4.1 if where trim

<!--
        查詢員工,需求:攜帶了哪一個字段查詢條件就整個這個字段的值
    -->
    <select id="getEmpsByConditionIf" resultType="com.meituan.mybatis.dynamic.bean.Employee">
        SELECT * FROM employee WHERE
        <!--
          test:判斷表達式(OGNL)
          c: if test
         從參數中取值進行判斷
        碰見特殊符號應該去寫轉義字符
        &&須要寫爲&amp;
        "須要寫爲&quot;
        -->
        <if test="id != null">
            id=#{id}
        </if>
        <if test="lastName != null and lastName!=''">
            AND last_name LIKE #{lastName}
        </if>
        <if test="email!=null and email.trim()!=&quot;&quot;">
            AND email=#{email}
        </if>
        <if test="gender==0 or gender == 1">
            AND gender=#{gender}
        </if>
    </select>

查詢時,某些條件缺失(如id),則可能致使sql拼裝出現問題

解決方案:

一、WHERE 1=1

WHERE 1=1 
<if test = "">
    AND ***
</if>

二、mybatis推薦的的方案:採用<where>標籤,將全部的查詢條件包括在內,mybatis就會將where標籤中拼裝的sql,多出來的and或者or去掉;where只會去掉前面多出來的and或者or

<where>
    <!--
              test:判斷表達式(OGNL)
              c: if test
             從參數中取值進行判斷
            碰見特殊符號應該去寫轉義字符
            &&須要寫爲&amp;
            "須要寫爲&quot;
            -->
    <if test="id != null">
        id=#{id}
    </if>
    <if test="lastName != null and lastName!=''">
        AND last_name LIKE #{lastName}
    </if>
    <if test="email!=null and email.trim()!=&quot;&quot;">
        AND email=#{email}
    </if>
    <if test="gender==0 or gender == 1">
        AND gender=#{gender}
    </if>
</where>

三、trim標籤的使用

<select id="getEmpsByConditionTrim" resultType="com.meituan.mybatis.dynamic.bean.Employee">
        SELECT * FROM employee
        <!---
         prefix="" 前綴:trim標籤體中是整個字符串拼串後的結果,prefix會給拼串後的整個字符串加一個前綴
         prefixOverrides=""  前綴覆蓋:去掉整個字符串前面多餘的字符
         suffix=""  後綴:整個串加後綴
         suffixOverrides=""  去掉整個串後面的字符
        -->
        <trim prefix="where" suffixOverrides="and">
            <if test="id != null">
                id=#{id} AND
            </if>
            <if test="lastName != null and lastName!=''">
                 last_name LIKE #{lastName} AND
            </if>
            <if test="email!=null and email.trim()!=&quot;&quot;">
                email=#{email} AND
            </if>
            <if test="gender==0 or gender == 1">
                gender=#{gender}
            </if>
        </trim>
    </select>

4.2 choose set標籤

choose 分支選擇,至關於帶了break的switch-case

<!-- 
        若是帶了id就用id查,若是帶了lastName就用lastName查
    -->
    <select id="getEmpsByConditionChoose" resultType="com.meituan.mybatis.dynamic.bean.Employee">
        SELECT * FROM employee
        <where>
<!--             若是帶了id就用id查,若是帶了lastName就用lastName查-->
            <choose>
                <when test="id!=null">
                    id=#{id}
                </when>
                <when test="lastName!=null">
                    last_name LIKE #{lastName}
                </when>
                <when test="email!=null">
                    email=#{email}
                </when>
                <otherwise>
                    1=1
                </otherwise>
            </choose>
        </where>
    </select>

set標籤用於update,能夠去掉多掉多餘的「,」

4.3 foreach

<select id="getEmpsByConditionForeach" resultType="com.meituan.mybatis.dynamic.bean.Employee">
      SELECT * FROM employee WHERE id IN
        <!--
            collection:指定要遍歷的集合
                list類型的參數會特殊處理封裝在map中,map的key就叫list
            item:將遍歷出的元素賦值給指定的變量
                #{變量名}就能取出變量的值也就是當前遍歷出的元素
            separator:每一個元素之間的分隔符
            open:遍歷出的全部結果拼接一個開始的字符
            close:遍歷出的全部結果拼接一個結束的字符
            index:索引。遍歷list的時候是索引
                        遍歷map的時候index表示的就是map的key,item就是map的值
        -->
        <foreach collection="ids" item="item_id" separator="," open="(" close=")">
            #{item_id}
        </foreach>

    </select>

批量插入:

<insert id="addEmpsByConditionForeach">
        INSERT INTO employee (last_name, email, gender, d_id) VALUES
        <foreach collection="list" item="emp" separator=",">
            (#{emp.lastName}, #{emp.email}, #{emp.gender}, #{emp.dept.id})
        </foreach>
    </insert>

注意:list類型的參數會特殊處理封裝在map中,map的key就叫list。若是指望此時傳入的參數名由本身定製,能夠

一、@Param("*")

二、將list傳入本身的map中

4.4 mybatis內置參數

mybatis默認有兩個內因參數

一、 _parameter:表明整個參數

​ 單個參數:_parameter就是這個參數

​ 多個參數:參數會被封裝爲一個map;_parameter就是表明這個map

二、 _databaseId: 若是配置了databaseIdProvider標籤,

​ _databaseId就是表明當前數據庫的別名

4.5 bind

bind能夠將OGNL表達式的值綁定到一個變量中,方便後來引用,例如:

<bind name="_lastName" value="'%'+lastName+'%'">

4.6 sql 抽取可重用的片斷

使用sql標籤訂義可重用片斷

<sql id="insertColumn">
    employee_id, last_name, email
</sql>

使用include標籤引用可重用片斷

<include refid"insertColumn"></include>

5. 緩存機制

緩存能夠極大的提高查詢效率。mybatis默認定義了兩級緩存:

  • 默認狀況下,只有一級緩存(SqlSession級別緩存,也稱爲本地緩存)開啓。
  • 二級緩存須要手動開啓和配置,是基於namespace級別的緩存,也稱全局緩存。
  • 爲了提升擴展性,mybatis定義了緩存接口Cache。能夠經過實現cache接口來自定義二級緩存

5.1 一級緩存

與數據庫同一次會話期間查詢到的數據會放在本地緩存中,之後若是須要獲取相同的數據,直接從緩存中拿,不必再去查詢數據庫。mybatis默認開啓一級緩存。

一級緩存失效狀況

  • sqlSession不一樣
  • sqlSession相同,查詢條件不一樣(當前一級緩存中尚未這個數據)
  • sqlSession相同,兩次查詢期間執行了增刪改(可能會致使當前數據失效)
  • 手動清空了緩存sqlSession.clearCache();

5.2 二級緩存

基於namespace級別的緩存,一個namespace對應一個二級緩存

工做機制:

一、一個會話(session),查詢一條ovry,這個數據就會被放在當前會話的一級緩存中

二、 若是當前會話關閉,一級緩存中的數據會被保存到二級緩存中;新的會話查詢信息,能夠參照二級緩存

三、 不一樣namespace查出的數據會放在本身對應的緩存中(map)

四、 查出的數據會默認先放在一級緩存中,只有會話提交或者關閉之後,一級緩存中的數據纔會轉移到二級緩存中

使用:

一、開啓全局二級緩存配置,<setting name="cacheEnabled" value="true"/>

二、在某個namespace中進行配置

<cache  eviction="FIFO" flushInterval="60000"  size="512" readOnly="true"/>
  • eviction

    一、LRU – 最近最少使用的:移除最長時間不被使用的對象。

    二、FIFO – 先進先出:按對象進入緩存的順序來移除它們。

    三、SOFT – 軟引用:移除基於垃圾回收器狀態和軟引用規則的對象。

    四、WEAK – 弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的對象。

    默認LRU

  • flushInterval:緩存刷新間隔

    緩存多久清空一次,默認不清空,單位毫秒

  • readOnly:是否只讀

    true:只讀,mybatis認爲全部從緩存中獲取數據的操做都是隻讀操做,不會修改數據。mybatis爲了加快獲取速度,直接會將數據在緩存中的引用交給用戶。特色:不安全,速度快

    false:不僅讀。mybatis認爲獲取的數據可能會被修改,mybatis會利用序列化&反序列的技術克隆一份新的數據給用戶。特色:安全,速度慢

  • size:緩存多少個元素
  • type:指定自定義緩存的全類名,實現Cache接口便可

三、 pojo須要實現序列化接口

5.3 和緩存有關的設置和屬性

一、<setting name="cacheEnabled" value="true"/>開啓或關閉緩存

二、 每一個select標籤都有useCache屬性,true爲使用,false爲不使用(一級緩存依然使用,二級緩存不使用)

三、每一個增刪改標籤的flushCache屬性值爲true,即增刪改執行完成後應付清除緩存,包括一級、二級緩存。查詢標籤默認flushCache="false",當設置爲true時,每次查詢以前都會清空緩存,緩存沒有被使用

四、sqlSession.clearCache();只是清除一級緩存

五、localCacheScope:本地緩存做用域(一級緩存),可選擇SESSION、STATEMENT。STATEMENT能夠禁用緩存

5.4 緩存原理

image

相關文章
相關標籤/搜索