架構師之路——Mybatis系列第4篇:Mybatis使用詳解(2),一起跟上節奏!

Mybatis系列目標:從入門開始開始掌握一個高級開發所需要的Mybatis技能。

本文很全,所以文章有點長,大家耐心看完哦~

這是mybatis系列第4篇。

主要內容

1、idea創建本篇案例

  • 建庫建表

  • 創建項目

2、別名使用詳解(typeAliases)

  • 爲什麼需要使用別名

  • 別名3種用法詳解

  • 方式1:使用typeAlias元素註冊別名

  • 方式2:使用package元素批量註冊別名

  • 方式3:使用package結合@Alias批量註冊並指定別名的名稱

  • 別名不區分大小寫

  • mybatis內置的別名

  • 別名的原理

  • 別名使用建議

3、屬性配置詳解(properties)

  • 屬性配置的3種方式

  • 方式1:通過propertie元素配置屬性

  • 方式2:方式通過resource引用classpath中的屬性配置文件

  • 方式3:通過url引用外部屬性配置文件

  • 使用建議

  • 相關問題

4、mybatis中引入mapper的3種方式

  • 方式1:通過mapper元素resource屬性的方式註冊Mapper xml文件和Mapper接口

  • 方式2:通過mapper元素class屬性的方式註冊Mapper接口和Mapper xml文件

  • 方式3:通過package元素批量註冊Mapper接口和Mapper xml文件

  • 源碼解釋

  • 使用注意

idea創建案例

建庫建表

/*創建數據庫javacode2018*/
DROP DATABASE IF EXISTS `javacode2018`;
CREATE DATABASE `javacode2018`;
USE `javacode2018`;

/*創建表結構*/
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE t_user (
  id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主鍵,用戶id,自動增長',
  `name` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '姓名',
  `age` SMALLINT NOT NULL DEFAULT 1 COMMENT '年齡',
  `salary` DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '薪水',
  `sex` TINYINT NOT NULL DEFAULT 0 COMMENT '性別,0:未知,1:男,2:女'
) COMMENT '用戶表';

DROP TABLE IF EXISTS `t_order`;
CREATE TABLE t_order (
  id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主鍵,訂單id,自動增長',
  `user_id` BIGINT NOT NULL DEFAULT 0 COMMENT '用戶id',
  `price` DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '訂單金額'
) COMMENT '訂單表';

/*插入幾條測試數據*/
INSERT INTO t_user (`name`,`age`,`salary`,`sex`)
VALUES
  ('路人甲Java',30,50000,1),
  ('javacode2018',30,50000,1),
  ('張學友',56,500000,1),
  ('林志玲',45,88888.88,2);


INSERT INTO t_order (`user_id`,`price`)
VALUES
  (1,88.88),
  (2,666.66);

SELECT * FROM t_user;
SELECT * FROM t_order;

創建工程

整個mybatis系列的代碼採用maven模塊的方式管理的,可以在文章底部獲取,本次我們還是在上一篇的mybatis-series中進行開發,在這個項目中新建一個模塊chat03,模塊座標如下:

<groupId>com.javacode2018</groupId>
<artifactId>chat03</artifactId>
<version>1.0-SNAPSHOT</version>

下面我們通過mybatis快速來實現對t_user表增刪改查,這個在上一篇的chat02中已經詳細講解過了。

創建mybatis配置文件

chat03\src\main\resources\demo1目錄創建,mybatis-config.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>
    <typeAliases></typeAliases>
    <!-- 環境配置,可以配置多個環境 -->
    <environments default="chat04-demo1">
        <!-- 
            environment用來對某個環境進行配置
            id:環境標識,唯一
         -->
        <environment id="chat04-demo1">
            <!-- 事務管理器工廠配置 -->
            <transactionManager type="org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory"/>
            <!-- 數據源工廠配置,使用工廠來創建數據源 -->
            <dataSource type="org.apache.ibatis.datasource.pooled.PooledDataSourceFactory">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root123"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="demo1/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

創建UserMapper.xml文件

chat03\src\main\resources\demo1\mapper目錄創建,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="com.javacode2018.chat03.demo1.UserMapper">
    <!-- insert用來定義一個插入操作
         id:操作的具體標識
         parameterType:指定插入操作接受的參數類型
     -->
    <insert id="insertUser" parameterType="com.javacode2018.chat03.demo1.UserModel" useGeneratedKeys="true">
        <![CDATA[
        INSERT INTO t_user (id,name,age,salary,sex) VALUES (#{id},#{name},#{age},#{salary},#{sex})
         ]]>
    </insert>

    <!-- update用來定義一個更新操作
         id:操作的具體標識
         parameterType:指定操作接受的參數類型
     -->
    <update id="updateUser" parameterType="com.javacode2018.chat03.demo1.UserModel">
        <![CDATA[
        UPDATE t_user SET name = #{name},age = #{age},salary = #{salary},sex = #{sex} WHERE id = #{id}
        ]]>
    </update>

    <!-- update用來定義一個刪除操作
         id:操作的具體標識
         parameterType:指定操作接受的參數類型
     -->
    <update id="deleteUser" parameterType="java.lang.Long">
        <![CDATA[
        DELETE FROM t_user WHERE id = #{id}
        ]]>
    </update>

    <!-- select用來定義一個查詢操作
         id:操作的具體標識
         resultType:指定查詢結果保存的類型
     -->
    <select id="getUserList" resultType="com.javacode2018.chat03.demo1.UserModel">
        <![CDATA[
        SELECT * FROM t_user
        ]]>
    </select>

</mapper>

創建UserModel類

chat03\src\main\java\com\javacode2018\chat03\demo1目錄創建UserModel.java,如下:

package com.javacode2018.chat03.demo1;

import lombok.*;

/**
 * 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class UserModel {
    private Long id;
    private String name;
    private Integer age;
    private Double salary;
    private Integer sex;
}

創建UserMapper接口

chat03\src\main\java\com\javacode2018\chat03\demo1目錄創建UserMapper.java,如下:

package com.javacode2018.chat03.demo1;

import java.util.List;

/**
 * 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
 */
public interface UserMapper {

    int insertUser(UserModel model);

    int updateUser(UserModel model);

    int deleteUser(Long userId);

    List<UserModel> getUserList();
}

引入logback日誌支持

chat03\src\main\resources目錄創建logback.xml,如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <logger name="com.javacode2018" level="debug" additivity="false">
        <appender-ref ref="STDOUT" />
    </logger>

</configuration>

創建測試用例UserMapperTest

chat03\src\test\java\com\javacode2018\chat03\demo1目錄創建UserMapperTest.java,如下:

package com.javacode2018.chat03.demo1;

import lombok.extern.slf4j.Slf4j;
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.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
 */
@Slf4j
public class UserMapperTest {
    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void before() throws IOException {
        //指定mybatis全局配置文件
        String resource = "demo1/mybatis-config.xml";
        //讀取全局配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //構建SqlSessionFactory對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Test
    public void getUserList() {
        try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            //執行查詢操作
            List<UserModel> userModelList = mapper.getUserList();
            userModelList.forEach(item -> {
                log.info("{}", item);
            });
        }
    }

}

代碼解釋一下:

上面的before()方法上面有個@Before註解,這個是junit提供的一個註解,通過junit運行每個@Test標註的方法之前,會先運行被@before標註的方法,before()方法中我們創建了SqlSessionFactory對象,所以其他的@Test標註的方法中可以直接使用sqlSessionFactory對象了。

項目結構如下圖

注意項目結構如下圖,跑起來有問題的可以對照一下。

運行一下測試用例看效果

運行一下UserMapperTest.getUserList()方法,輸出如下:

32:21.991 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
32:22.028 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
32:22.052 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
32:22.053 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
32:22.056 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
32:22.056 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=張學友, age=56, salary=500000.0, sex=1)
32:22.056 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=4, name=林志玲, age=45, salary=88888.88, sex=2)

上面是mybatis開發項目的一個玩轉的步驟,希望大家都能夠熟練掌握,下面我們來在這個示例的基礎上講解本章的知識點。

別名

爲什麼需要使用別名?

大家打開chat03\src\main\resources\demo1\mapper\UserMapper.xml文件看一下,是不是有很多下面這樣的代碼:

parameterType="com.javacode2018.chat03.demo1.UserModel"
resultType="com.javacode2018.chat03.demo1.UserModel"

parameterType是指定參數的類型,resultType是指定查詢結果返回值的類型,他們的值都是UserModel類完整的類名,比較長,mybatis支持我們給某個類型起一個別名,然後通過別名可以訪問到指定的類型。

別名的用法

使用別名之前需要先在mybatis中註冊別名,我們先說通過mybatis全局配置文件中註冊別名,通過mybatis配置文件註冊別名有3種方式。

方式1

使用typeAlias元素進行註冊

如下:

<typeAliases>
    <typeAlias type="玩轉的類型名稱" alias="別名" />
</typeAliases>

typeAliases元素中可以包含多個typeAlias子元素,每個typeAlias可以給一個類型註冊別名,有2個屬性需要指定:

type:完整的類型名稱

alias:別名

如上面給UserModel起了一個別名爲user

案例

給UserModel註冊一個別名user

chat03\src\main\resources\demo1\mapper\UserMapper.xml中加入下面配置:
<typeAliases>
    <typeAlias type="com.javacode2018.chat03.demo1.UserModel" alias="user" />
</typeAliases>

UserMapper.xml中使用別名,將chat03\src\main\resources\demo1\mapper\UserMapper.xml中getUserList的resultType的值改爲user,如下:

<select id="getUserList" resultType="user">
    <![CDATA[
    SELECT * FROM t_user
    ]]>
</select>

運行com.javacode2018.chat03.demo1.UserMapperTest#getUserList,如下:

07:35.477 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
07:35.505 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
07:35.527 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
07:35.527 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
07:35.529 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
07:35.529 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=張學友, age=56, salary=500000.0, sex=1)
07:35.529 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=4, name=林志玲, age=45, salary=88888.88, sex=2)

看到了麼,getUserList中我們使用的別名,運行是正常的,說明可以通過別名user直接訪問UserModel

方式2

通過packege元素批量註冊

上面我們通過typeAlias元素可以註冊一個別名,如果我們有很多類需要註冊,需要寫很多typeAlias配置。

mybatis爲我們提供了批量註冊別名的方式,通過package元素,如下:

<typeAliases>
    <package name="需要掃描的包"/>
</typeAliases>

這個也是在typeAliases元素下面,不過這次使用的是package元素,package有個name屬性,可以指定一個包名,mybatis會加載這個包以及子包中所有的類型,給這些類型都註冊別名,別名名稱默認會採用類名小寫的方式,如UserModel的別名爲usermodel

案例

下面我們將demo1/mybatis-config.xmltypeAliases元素的值改爲下面這樣:

<typeAliases>
    <package name="com.javacode2018.chat03.demo1"/>
</typeAliases>

mybatis會給com.javacode2018.chat03.demo1包及子包中的所有類型註冊別名,UserModel類在這個包中,會被註冊,別名爲usermodel

UserMapper.xml中使用別名,將chat03\src\main\resources\demo1\mapper\UserMapper.xml中getUserList的resultType的值改爲usermodel,如下:

<select id="getUserList" resultType="usermodel">
    <![CDATA[
    SELECT * FROM t_user
    ]]>
</select>

上面我們將返回值的類型resultType的值改爲了usermodel

我們來運行com.javacode2018.chat03.demo1.UserMapperTest#getUserList,如下:

26:08.267 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
26:08.296 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
26:08.318 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
26:08.319 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
26:08.320 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
26:08.320 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=張學友, age=56, salary=500000.0, sex=1)
26:08.320 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=4, name=林志玲, age=45, salary=88888.88, sex=2)

看到了麼,getUserList中我們使用的別名usermodel,運行也是正常的。

方式3

package結合@Alias批量註冊並指定別名

方式2中通過package可以批量註冊別名,如果指定的包中包含了多個類名相同的類,會怎麼樣呢?

我們在com.javacode2018.chat03.demo1.model包中創建一個和UserModel同名的類,如下:

package com.javacode2018.chat03.demo1.model;

import lombok.*;

/**
 * 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class UserModel {
    private Long id;
    private String name;
    private Integer age;
    private Double salary;
    private Integer sex;
}

現在com.javacode2018.demo1包中有2個UserModel類了

運行com.javacode2018.chat03.demo1.UserMapperTest#getUserList,如下:

org.apache.ibatis.exceptions.PersistenceException: 
### Error building SqlSession.
### The error may exist in SQL Mapper Configuration
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.type.TypeException: The alias 'UserModel' is already mapped to the value 'com.javacode2018.chat03.demo1.model.UserModel'.

    at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
    at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80)
    at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64)
    at com.javacode2018.chat03.demo1.UserMapperTest.before(UserMapperTest.java:29)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.type.TypeException: The alias 'UserModel' is already mapped to the value 'com.javacode2018.chat03.demo1.model.UserModel'.
    at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:121)
    at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:98)
    at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:78)
    ... 24 more
Caused by: org.apache.ibatis.type.TypeException: The alias 'UserModel' is already mapped to the value 'com.javacode2018.chat03.demo1.model.UserModel'.
    at org.apache.ibatis.type.TypeAliasRegistry.registerAlias(TypeAliasRegistry.java:157)
    at org.apache.ibatis.type.TypeAliasRegistry.registerAlias(TypeAliasRegistry.java:147)
    at org.apache.ibatis.type.TypeAliasRegistry.registerAliases(TypeAliasRegistry.java:136)
    at org.apache.ibatis.type.TypeAliasRegistry.registerAliases(TypeAliasRegistry.java:125)
    at org.apache.ibatis.builder.xml.XMLConfigBuilder.typeAliasesElement(XMLConfigBuilder.java:164)
    at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:109)
    ... 26 more

報錯了,2個類的類名一樣了,默認都會使用usermodel作爲別名,別名重複了mybatis會報錯,那麼此時我們怎麼辦呢?

package方式批量註冊別名的時候,我們可以給類中添加一個@Alias註解來給這個類指定別名:

@Alias("user")
public class UserModel {
}

當mybatis掃描類的時候,發現類上有Alias註解,會取這個註解的value作爲別名,如果沒有這個註解,會將類名小寫作爲別名,如同方式2。

案例

我們在com.javacode2018.chat03.demo1.UserModel類上加上下面註解:

@Alias("use")
public class UserModel {
}

修改demo1/mapper/UserMapper.xml,將resultType的值設置爲user

<select id="getUserList" resultType="user">
    <![CDATA[
    SELECT * FROM t_user
    ]]>
</select>

再來運行com.javacode2018.chat03.demo1.UserMapperTest#getUserList,如下:

18:51.219 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
18:51.250 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
18:51.271 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
18:51.272 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
18:51.274 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
18:51.274 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=張學友, age=56, salary=500000.0, sex=1)
18:51.274 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=4, name=林志玲, age=45, salary=88888.88, sex=2)

輸出正常。

別名不區分大小寫

我們可以將上面UserMapper.xml中的use別名改成大寫的:USER,如下:

<select id="getUserList" resultType="USER">
    <![CDATA[
    SELECT * FROM t_user
    ]]>
</select>

然後再運行一下com.javacode2018.chat03.demo1.UserMapperTest#getUserList,如下:

42:49.474 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
42:49.509 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
42:49.527 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
42:49.528 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
42:49.530 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
42:49.530 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=張學友, age=56, salary=500000.0, sex=1)
42:49.531 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=4, name=林志玲, age=45, salary=88888.88, sex=2)

也是正常的,說明別名使用時是不區分大小寫的。

mybatis內置的別名

mybatis默認爲很多類型提供了別名,如下:

別名 對應的實際類型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

上面這些默認都是在org.apache.ibatis.type.TypeAliasRegistry類中進行註冊的,這個類就是mybatis註冊別名使用的,別名和具體的類型關聯是放在這個類的一個map屬性(typeAliases)中,貼一部分代碼大家感受一下:

public class TypeAliasRegistry {

  private final Map<String, Class<?>> typeAliases = new HashMap<>();

  public TypeAliasRegistry() {
    registerAlias("string", String.class);

    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);

    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);

    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);

    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);

    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);

    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);

    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);

    registerAlias("ResultSet", ResultSet.class);
  }
}

mybatis啓動的時候會加載全局配置文件,會將其轉換爲一個org.apache.ibatis.session.Configuration對象,存儲在內存中,Configuration類中也註冊了一些別名,代碼如下:

typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

上面有2行如下:

typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);

上面這2行,註冊了2個別名,別名和類型映射關係如下:

JDBC -> JdbcTransactionFactory
POOLED -> PooledDataSourceFactory

上面這2個對象,大家應該比較熟悉吧,mybatis全局配置文件(chat03\src\main\resources\demo1\mybatis-config.xml)中我們用到過,我們再去看一下,如下:

上面2個紅框的是不是就是上面註冊的2個類型,上面xml中我們寫的是完整類型名稱,我們可以將其改爲別名的方式也是可以的,如下:

 

我們來運行com.javacode2018.chat03.demo1.UserMapperTest#getUserList,看一下能否正常運行,輸出如下:

44:10.886 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
44:10.929 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
44:10.947 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
44:10.948 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
44:10.950 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
44:10.950 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=張學友, age=56, salary=500000.0, sex=1)
44:10.950 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=4, name=林志玲, age=45, salary=88888.88, sex=2)

很好,一切正常的。

別名的原理

mybatis允許我們給某種類型註冊一個別名,別名和類型之間會建立映射關係,這個映射關係存儲在一個map對象中,key爲別名的名稱,value爲具體的類型,當我們通過一個名稱訪問某種類型的時候,mybatis根據類型的名稱,先在別名和類型映射的map中按照key進行查找,如果找到了直接返回對應的類型,如果沒找到,會將這個名稱當做完整的類名去解析成Class對象,如果這2步解析都無法識別這種類型,就會報錯。

mybatis和別名相關的操作都位於org.apache.ibatis.type.TypeAliasRegistry類中,包含別名的註冊、解析等各種操作。

我們來看一下別名解析的方法,如下:

public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
      // issue #748
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      if (typeAliases.containsKey(key)) {
        value = (Class<T>) typeAliases.get(key);
      } else {
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
  }

有一個typeAliases對象,我們看一下其定義:

private final Map<String, Class<?>> typeAliases = new HashMap<>();

這個對象就是存放別名和具體類型映射關係的,從上面代碼中可以看出,通過傳入的參數解析對應的類型的時候,會先從typeAliases中查找,如果找不到會調用下面代碼:

value = (Class<T>) Resources.classForName(string);

上面這個方法裏面具體是使用下面代碼去通過名稱解析成類型的:

Class.forName(類名完整名稱)

Class.forName大家應該是很熟悉的,可以獲取一個字符串對應的Class對象,如果找不到這個對象,會報錯。

別名使用建議

別名的方式可以簡化類型的寫法,原本很長一串的UserModel對象,現在只用寫個user就行了,用起來是不是挺爽的?

從寫法上面來說,確實少幫我們省了一些代碼,但是從維護上面來講,不是很方便。

如Mapper xml直接寫別名,看代碼的時候,很難知道這個別名對應的具體類型,還需要我們去註冊的地方找一下,不是太方便,如果我們在idea中寫完整的類名,還可以按住Ctrl健,然後用鼠標左鍵點擊類型直接可以跳到對應的類定義中去,如果使用別名是無法導航過去的。

整體上來說開發和看代碼都不是太方便,只是寫法上比價簡單。

所以建議自定義的類儘量別使用別名,而對mybatis中內置的一些別名我們需要知道。

屬性配置文件詳解

大家看一下chat03\src\main\resources\demo1\mybatis-config.xml中下面這一部分的配置:

<dataSource type="POOLED">
    <property name="driver" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8"/>
    <property name="username" value="root"/>
    <property name="password" value="root123"/>
</dataSource>

這個連接數據庫的配置,我們是直接寫在mybatis全局配置文件中的,上面這是我們本地測試庫的db信息,上線之後,需要修改爲線上的db配置信息,db配置信息一般由運維去修改,讓運維去修改這個xml配置文件?

這樣不是太好,我們通常將一些需要運維修改的配置信息(如:db配置、郵件配置、redis配置等等各種配置)放在一個properties配文件中,然後上線時,只需要運維去修改這個配置文件就可以了,根本不用他們去修改和代碼相關的文件。

mybatis也支持我們通過外部properties文件來配置一些屬性信息。

mybatis配置屬性信息有3種方式。

方式1:property元素中定義屬性

屬性定義

mybatis全局配置文件中通過properties元素來定義屬性信息,如下:

<configuration>
    <properties>
        <property name="屬性名稱" value="屬性對應的值"/>
    </properties>
</configuration>

上面通過property元素的方式進行配置屬性信息:

name:屬性的名稱

value:屬性的值。

如:

<property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>

使用${屬性名稱}引用屬性的值

屬性已經定義好了,我們可以通過${屬性名稱}引用定義好的屬性的值,如:

<property name="driver" value="${jdbc.driver}"/>

案例

我們在demo1/mapper/mybatis-config.xmlconfiguration元素中加入下面配置:

<properties>
    <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
    <property name="jdbc.url" value="jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8"/>
    <property name="jdbc.username" value="root"/>
    <property name="jdbc.password" value="root123"/>
</properties>

修改datasource的配置:

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

運行com.javacode2018.chat03.demo1.UserMapperTest#getUserList,如下:

40:22.274 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
40:22.307 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
40:22.330 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
40:22.331 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
40:22.332 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
40:22.332 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=張學友, age=56, salary=500000.0, sex=1)
40:22.332 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=4, name=林志玲, age=45, salary=88888.88, sex=2)

運行正常。

方式2:resource引入配置文件

方式1中,我們的配置文件還是寫在全局配置文件中,mybatis支持從外部引入配置文件,可以把配置文件寫在其他外部文件中,然後進行引入。

引入classes路徑中的配置文件

<configuration>
    <properties resource="配置文件路徑"/>
</configuration>

properties元素有個resource屬性,值爲配置文件相對於classes的路徑,配置文件我們一般放在src/main/resource目錄,這個目錄的文件編譯之後會放在classes路徑中。

案例

下面我們將上面db的配置放在外部的config.properties文件中。

chat03\src\main\resources\demo1目錄新建一個配置文件config.properties,內容如下:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8
jdbc.username=root
jdbc.password=root123

demo1/mapper/mybatis-config.xml中引入上面配置文件:

<!-- 引入外部配置文件 -->
<properties resource="demo1/mapper/config.properties"/>

目前demo1/mapper/mybatis-config.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 resource="demo1/config.properties"/>
    <typeAliases>
        <package name="com.javacode2018.chat03.demo1"/>
    </typeAliases>
    <!-- 環境配置,可以配置多個環境 -->
    <environments default="chat04-demo1">
        <!-- 
            environment用來對某個環境進行配置
            id:環境標識,唯一
         -->
        <environment id="chat04-demo1">
            <!-- 事務管理器工廠配置 -->
            <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>

    <mappers>
        <mapper resource="demo1/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

運行com.javacode2018.chat03.demo1.UserMapperTest#getUserList,如下:

57:40.405 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
57:40.436 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
57:40.454 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
57:40.455 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
57:40.457 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
57:40.457 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=張學友, age=56, salary=500000.0, sex=1)
57:40.457 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=4, name=林志玲, age=45, salary=88888.88, sex=2)

運行正常。

方式3:url的方式引入遠程配置文件

mybatis還提供了引入遠程配置文件的方式,如下:

<properties url="遠程配置文件的路徑" />

這次還是使用properties元素,不過使用的是url屬性,如:

<properties url="http://itsoku.com/properties/config.properties" />

這種方式的案例就不提供了,有興趣的可以自己去玩玩。

屬性配置文件使用建議

上面我們說了3中方式,第2中方式是比較常見的做法,建議大家可以使用第二種方式來引入外部資源配置文件。

問題

如果3種方式如果我們都寫了,mybatis會怎麼走?

下面我們修改一下resources/demo1/mybatis-config.xml,使用第一種方式定義屬性,如下:

<properties>
    <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
    <property name="jdbc.url" value="jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8"/>
    <property name="jdbc.username" value="root"/>
    <property name="jdbc.password" value="root"/>
</properties>

password的值改爲了root,正確的是root123,運行測試用例,報錯如下:

org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)
### The error may exist in demo1/mapper/UserMapper.xml
### The error may involve com.javacode2018.chat03.demo1.UserMapper.getUserList
### The error occurred while executing a query
### Cause: java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)

    at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:149)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
    at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:147)
    at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:80)
    at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:93)
    at com.sun.proxy.$Proxy6.getUserList(Unknown Source)
    at com.javacode2018.chat03.demo1.UserMapperTest.getUserList(UserMapperTest.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)

提示密碼錯誤。

下面我們將第2種方式也加入,修改配置:

<properties resource="demo1/config.properties">
    <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
    <property name="jdbc.url" value="jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8"/>
    <property name="jdbc.username" value="root"/>
    <property name="jdbc.password" value="root"/>
</properties>

再運行一下測試用例,如下:

18:59.436 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
18:59.462 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
18:59.481 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
18:59.482 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
18:59.485 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
18:59.485 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=張學友, age=56, salary=500000.0, sex=1)
18:59.485 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=4, name=林志玲, age=45, salary=88888.88, sex=2)

這次正常了。

可以看出方式1和方式2都存在的時候,方式2的配置會覆蓋方式1的配置。

mybatis這塊的源碼在org.apache.ibatis.builder.xml.XMLConfigBuilder#propertiesElement方法中,如下:

private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      Properties defaults = context.getChildrenAsProperties();
      String resource = context.getStringAttribute("resource");
      String url = context.getStringAttribute("url");
      if (resource != null && url != null) {
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      parser.setVariables(defaults);
      configuration.setVariables(defaults);
    }
  }

從上面代碼中也可以看出,如果方式2和方式3都存在的時候,方式3會失效,mybatis會先讀取方式1的配置,然後讀取方式2或者方式3的配置,會將1中相同的配置給覆蓋。

mybatis中引入mapper的3種方式

mapper xml文件是非常重要的,我們寫的sql基本上都在裏面,使用mybatis開發項目的時候,和mybatis相關的大部分代碼就是寫sql,基本上都是和mapper xml打交道。

編寫好的mapper xml需要讓mybatis知道,我們怎麼讓mybatis知道呢?

可以通過mybatis全局配置文件進行引入,主要有3種方式。

方式1:使用mapper resouce屬性註冊mapper xml文件

目前我們所涉及到的各種例子都是採用的這種方式,使用下面的方法進行引入:

<mappers>
    <mapper resource="Mapper xml的路徑(相對於classes的路徑)"/>
</mappers>

再來說一下這種方式的一些注意點:

  1. 一般情況下面我,我們會創建一個和Mapper xml中namespace同名的Mapper接口,Mapper接口會和Mapper xml文件進行綁定

  2. mybatis加載mapper xml的時候,會去查找namespace對應的Mapper接口,然後進行註冊,我們可以通過Mapper接口的方式去訪問Mapper xml中的具體操作

  3. Mapper xml和Mapper 接口配合的方式是比較常見的做法,也是強烈建議大家使用的

方式2:使用mapper class屬性註冊Mapper接口

引入Mapper接口

mybatis全局配置文件中引入mapper接口,如下:

<mappers>
        <mapper class="接口的完整類名" />
</mappers>

這種情況下,mybais會去加載class對應的接口,然後還會去加載和這個接口同一個目錄的同名的xml文件。

如:

<mappers>
        <mapper class="com.javacode2018.chat03.demo1.UserMapper" />
</mappers>

上面這種寫法,mybatis會自動去註冊UserMapper接口,還會去查找下面的文件:

com/javacode2018/chat03/demo1/UserMapper.xml

大家以後開發項目的時候估計也會看到這種寫法,Mapper接口Mapper xml文件放在同一個包中。

案例

下面我們重新創建一個案例,都放在demo2包中。

新建com.javacode2018.chat03.demo2.UserModel,如下:

package com.javacode2018.chat03.demo2;

import lombok.*;
import org.apache.ibatis.type.Alias;

/**
 * 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class UserModel {
    private Long id;
    private String name;
    private Integer age;
    private Double salary;
    private Integer sex;
}

新建com.javacode2018.chat03.demo2.UserMapper,如下:

package com.javacode2018.chat03.demo2;

import java.util.List;

/**
 * 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
 */
public interface UserMapper {

    List<UserModel> getUserList();
}

chat03\src\main\java\com\javacode2018\chat03\demo2中創建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="com.javacode2018.chat03.demo2.UserMapper">
    <!-- select用來定義一個查詢操作
         id:操作的具體標識
         resultType:指定查詢結果保存的類型
     -->
    <select id="getUserList" resultType="com.javacode2018.chat03.demo2.UserModel">
        <![CDATA[
        SELECT * FROM t_user
        ]]>
    </select>

</mapper>

下面重點來了。

創建mybatis全局配置文件,在chat03\src\main\resources\demo2目錄中創建mybatis-config.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>
        <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
        <property name="jdbc.url" value="jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8"/>
        <property name="jdbc.username" value="root"/>
        <property name="jdbc.password" value="root123"/>
    </properties>
    <!-- 環境配置,可以配置多個環境 -->
    <environments default="chat04-demo2">
        <!-- 
            environment用來對某個環境進行配置
            id:環境標識,唯一
         -->
        <environment id="chat04-demo2">
            <!-- 事務管理器工廠配置 -->
            <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>

</configuration>

chat03\src\test\java目錄創建測試用例com.javacode2018.chat03.demo2.UserMapperTest,如下:

package com.javacode2018.chat03.demo2;

import lombok.extern.slf4j.Slf4j;
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.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
 */
@Slf4j
public class UserMapperTest {
    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void before() throws IOException {
        //指定mybatis全局配置文件
        String resource = "demo2/mybatis-config.xml";
        //讀取全局配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //構建SqlSessionFactory對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Test
    public void getUserList() {
        try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            //執行查詢操作
            List<UserModel> userModelList = mapper.getUserList();
            userModelList.forEach(item -> {
                log.info("{}", item);
            });
        }
    }

}

注意這次上面使用的是demo2/mybatis-config.xml配置文件。

我們先來看一下項目結構,4個文件:

注意一下UserMapper接口所在的包中有個同名的UserMapper.xml文件,這個如果按照方式2中所說的,會自動加載。

下面我們來運行一下com.javacode2018.chat03.demo2.UserMapperTest#getUserList,輸出:

org.apache.ibatis.binding.BindingException: Type interface com.javacode2018.chat03.demo2.UserMapper is not known to the MapperRegistry.

    at org.apache.ibatis.binding.MapperRegistry.getMapper(MapperRegistry.java:47)
    at org.apache.ibatis.session.Configuration.getMapper(Configuration.java:779)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.getMapper(DefaultSqlSession.java:291)
    at com.javacode2018.chat03.demo2.UserMapperTest.getUserList(UserMapperTest.java:36)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

從輸出中可以看到,UserMapper找不到。

我們去看一下demo2/mybatis-config.xml這個配置文件,這個文件中需要使用方式2引入UserMapper接口,在demo2/mybatis-config.xml中加入下面配置:

<mappers>
    <mapper class="com.javacode2018.chat03.demo2.UserMapper" />
</mappers>

再運行一下,還是報錯,如下,還是找不到對應的UserMapper:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.javacode2018.chat03.demo2.UserMapper.getUserList

    at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:235)
    at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:53)

還是有問題,我們看一下target/classesdemo2包的內容,如下圖:

編譯之後的文件中少了UserMapper.xml,這個和maven有關,maven編譯src/java代碼的時候,默認只會對java文件進行編譯然後放在target/classes目錄,需要在chat03/pom.xml中加入下面配置:

<build>
    <resources>
        <resource>
            <directory>${project.basedir}/src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
        <resource>
            <directory>${project.basedir}/src/main/resources</directory>
            <includes>
                <include>**/*</include>
            </includes>
        </resource>
    </resources>
</build>

最終chat03/pom.xml內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mybatis-series</artifactId>
        <groupId>com.javacode2018</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>chat03</artifactId>

    <dependencies>
        <!-- mybatis依賴 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
        </dependency>
        <!-- mysql 驅動 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- lombok支持 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- 單元測試junit支持 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <!-- 引入logback用來輸出日誌 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>${project.basedir}/src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>${project.basedir}/src/main/resources</directory>
                <includes>
                    <include>**/*</include>
                </includes>
            </resource>
        </resources>
    </build>

</project>

加了這個之後UserMapper.xml就會被放到target的classes中去了,如下圖:

爲什麼maven中需要加上面配置,這塊大家可以去看公衆號中maven系列的文章,裏面有詳細介紹,maven的相關東西,後面還會經常用到,對這塊不熟悉的,建議儘快把maven系列的所有文章都看一遍,以免後面學習的過程中掉隊。

我們再次運行一下測試用例com.javacode2018.chat03.demo2.UserMapperTest#getUserList,效果如下:

24:37.814 [main] DEBUG c.j.c.demo2.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
24:37.852 [main] DEBUG c.j.c.demo2.UserMapper.getUserList - ==> Parameters: 
24:37.875 [main] DEBUG c.j.c.demo2.UserMapper.getUserList - <==      Total: 4
24:37.876 [main] INFO  c.j.chat03.demo2.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
24:37.879 [main] INFO  c.j.chat03.demo2.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
24:37.879 [main] INFO  c.j.chat03.demo2.UserMapperTest - UserModel(id=3, name=張學友, age=56, salary=500000.0, sex=1)
24:37.879 [main] INFO  c.j.chat03.demo2.UserMapperTest - UserModel(id=4, name=林志玲, age=45, salary=88888.88, sex=2)

這次正常了。

源碼

方式2對應的源碼大家可以去看下面這個方法:

org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement

方法中會去加載mapper元素中class屬性指定的Mapper接口,然後進行註冊,隨後會在接口同目錄中查找同名的mapper xml文件,將解析這個xml文件,如果mapper xml文件不存在,也不會報錯,源碼還是比較簡單的,大家可以去看一下,加深理解。

方式3:使用package元素批量註冊Mapper接口

批量註冊Mapper接口

上面說2種方式都是一個個註冊mapper的,如果我們寫了很多mapper,是否能夠批量註冊呢?

mybatis提供了掃描包批量註冊的方式,需要在mybatis全局配置文件中加入下面配置:

<mappers>
    <package name="需要掃描的包" />
</mappers>

mybatis會掃描package元素中name屬性指定的包及子包中的所有接口,將其當做Mapper 接口進行註冊,所以一般我們會創建一個mapper包,裏面放Mapper接口同名的Mapper xml文件

大家來看一個案例,理解一下。

案例

這個案例中將對t_user、t_order兩個表進行查詢操作,採用方式3中的package批量引入mapper 接口和xml文件。

所有代碼放在demo3包中,大家先看下文件所在的目錄:

創建UserModel類,如下:

package com.javacode2018.chat03.demo3.model;

import lombok.*;

/**
 * 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class UserModel {
    private Long id;
    private String name;
    private Integer age;
    private Double salary;
    private Integer sex;
}

創建OrderModel類,如下:

package com.javacode2018.chat03.demo3.model;

import lombok.*;

/**
 * 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class OrderModel {
    private Long id;
    private Long user_id;
    private Double price;
}

創建UserMapper接口,如下:

package com.javacode2018.chat03.demo3.mapper;

import com.javacode2018.chat03.demo3.model.UserModel;

import java.util.List;

/**
 * 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
 */
public interface UserMapper {

    List<UserModel> getList();
}

創建OrderMapper接口,如下:

package com.javacode2018.chat03.demo3.mapper;

import com.javacode2018.chat03.demo3.model.OrderModel;
import com.javacode2018.chat03.demo3.model.UserModel;

import java.util.List;

/**
 * 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
 */
public interface OrderMapper {

    List<OrderModel> getList();
}

創建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="com.javacode2018.chat03.demo3.mapper.OrderMapper">
    <!-- select用來定義一個查詢操作
         id:操作的具體標識
         resultType:指定查詢結果保存的類型
     -->
    <select id="getList" resultType="com.javacode2018.chat03.demo3.model.OrderModel">
        <![CDATA[
        SELECT * FROM t_order
        ]]>
    </select>

</mapper>

上面我們寫了一個查詢t_user數據的sql

創建OrderMapper.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="com.javacode2018.chat03.demo3.mapper.OrderMapper">
    <!-- select用來定義一個查詢操作
         id:操作的具體標識
         resultType:指定查詢結果保存的類型
     -->
    <select id="getList" resultType="com.javacode2018.chat03.demo3.model.OrderModel">
        <![CDATA[
        SELECT * FROM t_order
        ]]>
    </select>

</mapper>

上面我們寫了一個查詢t_order數據的sql

創建resources/demo3/mybatis-config.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>
        <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
        <property name="jdbc.url" value="jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8"/>
        <property name="jdbc.username" value="root"/>
        <property name="jdbc.password" value="root123"/>
    </properties>
    <!-- 環境配置,可以配置多個環境 -->
    <environments default="chat04-demo3">
        <!-- 
            environment用來對某個環境進行配置
            id:環境標識,唯一
         -->
        <environment id="chat04-demo3">
            <!-- 事務管理器工廠配置 -->
            <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>

    <mappers>
        <package name="com.javacode2018.chat03.demo3.mapper"/>
    </mappers>
</configuration>

注意這次我們使用package來讓mybatis加載com.javacode2018.chat03.demo3.mapper包下面所有的Mapper接口和Mapper xml文件。

創建測試用例Demo3Test,如下:

package com.javacode2018.chat03.demo3;

import com.javacode2018.chat03.demo3.mapper.OrderMapper;
import com.javacode2018.chat03.demo3.mapper.UserMapper;
import com.javacode2018.chat03.demo3.model.OrderModel;
import com.javacode2018.chat03.demo3.model.UserModel;
import lombok.extern.slf4j.Slf4j;
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.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * 公衆號:路人甲Java,工作10年的前阿里P7分享Java、算法、數據庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
 */
@Slf4j
public class Demo3Test {
    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void before() throws IOException {
        //指定mybatis全局配置文件
        String resource = "demo3/mybatis-config.xml";
        //讀取全局配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //構建SqlSessionFactory對象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Test
    public void test() {
        try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //執行查詢操作
            List<UserModel> userModelList = userMapper.getList();
            userModelList.forEach(item -> {
                log.info("{}", item);
            });

            log.info("----------------------------------");
            OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
            //執行查詢操作
            List<OrderModel> orderModelList = orderMapper.getList();
            orderModelList.forEach(item -> {
                log.info("{}", item);
            });
        }
    }

}

運行com.javacode2018.chat03.demo3.Demo3Test#test,輸出如下:

48:39.280 [main] DEBUG c.j.c.d.mapper.UserMapper.getList - ==>  Preparing: SELECT * FROM t_user 
48:39.315 [main] DEBUG c.j.c.d.mapper.UserMapper.getList - ==> Parameters: 
48:39.339 [main] DEBUG c.j.c.d.mapper.UserMapper.getList - <==      Total: 4
48:39.340 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
48:39.343 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
48:39.343 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=3, name=張學友, age=56, salary=500000.0, sex=1)
48:39.343 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=4, name=林志玲, age=45, salary=88888.88, sex=2)
48:39.343 [main] INFO  c.j.chat03.demo3.Demo3Test - ----------------------------------
48:39.344 [main] DEBUG c.j.c.d.mapper.OrderMapper.getList - ==>  Preparing: SELECT * FROM t_order 
48:39.345 [main] DEBUG c.j.c.d.mapper.OrderMapper.getList - ==> Parameters: 
48:39.351 [main] DEBUG c.j.c.d.mapper.OrderMapper.getList - <==      Total: 2
48:39.351 [main] INFO  c.j.chat03.demo3.Demo3Test - OrderModel(id=1, user_id=1, price=88.88)
48:39.351 [main] INFO  c.j.chat03.demo3.Demo3Test - OrderModel(id=2, user_id=2, price=666.66)

這種批量的方式是不是用着挺爽的,不過有點不是太好,mapper xml和mapper接口放在了一個目錄中,目錄中既有java代碼又有xml文件,看起來也挺彆扭的,其實你們可以這樣:

一般我們將配置文件放在resource目錄,我們可以在resource目錄中創建下面子目錄:

com/javacode2018/chat03/demo3/mapper

然後將com.javacode2018.chat03.demo3.mapper中的2個xml文件移到上面新創建的目錄中去,如下圖:

在去運行一下com.javacode2018.chat03.demo3.Demo3Test#test,輸出如下:

56:22.669 [main] DEBUG c.j.c.d.mapper.UserMapper.getList - ==>  Preparing: SELECT * FROM t_user 
56:22.700 [main] DEBUG c.j.c.d.mapper.UserMapper.getList - ==> Parameters: 
56:22.721 [main] DEBUG c.j.c.d.mapper.UserMapper.getList - <==      Total: 4
56:22.722 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
56:22.725 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
56:22.725 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=3, name=張學友, age=56, salary=500000.0, sex=1)
56:22.725 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=4, name=林志玲, age=45, salary=88888.88, sex=2)
56:22.725 [main] INFO  c.j.chat03.demo3.Demo3Test - ----------------------------------
56:22.727 [main] DEBUG c.j.c.d.mapper.OrderMapper.getList - ==>  Preparing: SELECT * FROM t_order 
56:22.727 [main] DEBUG c.j.c.d.mapper.OrderMapper.getList - ==> Parameters: 
56:22.732 [main] DEBUG c.j.c.d.mapper.OrderMapper.getList - <==      Total: 2
56:22.732 [main] INFO  c.j.chat03.demo3.Demo3Test - OrderModel(id=1, user_id=1, price=88.88)
56:22.732 [main] INFO  c.j.chat03.demo3.Demo3Test - OrderModel(id=2, user_id=2, price=666.66)

也是可以的。

源碼

方式3的源碼和方式2的源碼在一個地方:

org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement

方法中會去掃描指定的包中所有的接口,會將接口作爲Mapper接口進行註冊,然後還會找這些接口同名的Xml文件,將其註冊爲Mapper xml文件,相對於對方式2循環的方式。

使用注意

方式3會掃描指定包中所有的接口,把這些接口作爲Mapper接口進行註冊,掃描到的類型只要是接口就會被註冊,所以指定的包中通常我們只放Mapper接口,避免存放一些不相干的類或者接口。

關於配置和源碼

本次講解到的一些配置都是在mybatis全局配置文件中進行配置的,這些元素配置是有先後順序的,具體元素是在下面的dtd文件中定義的:

http://mybatis.org/dtd/mybatis-3-config.dtd

建議大家去看一下這個dtd配置文件。

Mybatis解析這個配置文件的入口是在下面的方法中:

org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration

代碼的部分實現如下:

  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

可以看到mybatis啓動的時候會按順序加載上面的標籤

總結

  1. 掌握別名註冊的3種方式,建議大家儘量少使用自定義別名

  2. 掌握屬性配置3種方式

  3. 掌握mapper註冊的3種方式及需要注意的地方

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章