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.xml
中typeAliases
元素的值改爲下面這樣:
<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.xml
的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>
修改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>
再來說一下這種方式的一些注意點:
-
一般情況下面我,我們會創建一個和Mapper xml中namespace同名的Mapper接口,Mapper接口會和Mapper xml文件進行綁定
-
mybatis加載mapper xml的時候,會去查找namespace對應的Mapper接口,然後進行註冊,我們可以通過Mapper接口的方式去訪問Mapper xml中的具體操作
-
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/classes
中demo2
包的內容,如下圖:
編譯之後的文件中少了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啓動的時候會按順序加載上面的標籤
總結
-
掌握別名註冊的3種方式,建議大家儘量少使用自定義別名
-
掌握屬性配置3種方式
-
掌握mapper註冊的3種方式及需要注意的地方