Java充电社
专辑
博文
联系我
本人继续续收门徒,亲手指导
MyBatis教程
-> MyBatis使用详解(2)
1、MyBatis未出世之前我们那些痛苦的经历
2、MyBatis入门,你确定mybatis你玩的很溜?
3、MyBatis使用详解(-)
4、MyBatis使用详解(2)
5、详解传参的几种方式、原理、源码解析
6、增删改知识点汇总及主键获取3种方式详解
7、种查询详解
8、自动映射,使用需谨慎
9、延迟加载、鉴别器、继承
10、动态SQL,这么多种你都会么?
11、类型处理器
12、掌握缓存为查询提速!
上一篇:MyBatis使用详解(-)
下一篇:详解传参的几种方式、原理、源码解析
<div style="display:none"></div> ## Mybatis系列第4篇:Mybatis使用详解(2) ### 主要内容 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创建案例 #### 建库建表 ```java /*创建数据库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`,模块坐标如下: ```xml <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 <?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 <?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`,如下: ```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`,如下: ```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 <?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`,如下: ```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`对象了。 ##### 项目结构如下图 注意项目结构如下图,跑起来有问题的可以对照一下。 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/75/62ff9a8f-181e-4ea1-a3b9-ca6c73cc238a.png) ##### 运行一下测试用例看效果 运行一下`UserMapperTest.getUserList()`方法,输出如下: ```java 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文件看一下,是不是有很多下面这样的代码: ```java parameterType="com.javacode2018.chat03.demo1.UserModel" resultType="com.javacode2018.chat03.demo1.UserModel" ``` parameterType是指定参数的类型,resultType是指定查询结果返回值的类型,他们的值都是`UserModel`类完整的类名,比较长,mybatis支持我们给某个类型起一个别名,然后通过别名可以访问到指定的类型。 #### 别名的用法 使用别名之前需要先在mybatis中注册别名,我们先说通过mybatis全局配置文件中注册别名,通过mybatis配置文件注册别名有3种方式。 #### 方式1 ##### 使用typeAlias元素进行注册 如下: ```xml <typeAliases> <typeAlias type="玩转的类型名称" alias="别名" /> </typeAliases> ``` **typeAliases元素中可以包含多个typeAlias子元素,每个typeAlias可以给一个类型注册别名,有2个属性需要指定:** **type:完整的类型名称** **alias:别名** 如上面给`UserModel`起了一个别名为`user`。 ##### 案例 给UserModel注册一个别名`user` ```java chat03\src\main\resources\demo1\mapper\UserMapper.xml中加入下面配置: ``` ```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`,如下: ```xml <select id="getUserList" resultType="user"> <![CDATA[ SELECT * FROM t_user ]]> </select> ``` 运行`com.javacode2018.chat03.demo1.UserMapperTest#getUserList`,如下: ```java 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`元素,如下: ```xml <typeAliases> <package name="需要扫描的包"/> </typeAliases> ``` 这个也是在typeAliases元素下面,不过这次使用的是`package`元素,package有个name属性,可以指定一个包名,mybatis会加载这个包以及子包中所有的类型,给这些类型都注册别名,别名名称默认会采用类名小写的方式,如`UserModel`的别名为`usermodel` ##### 案例 下面我们将`demo1/mybatis-config.xml`中`typeAliases`元素的值改为下面这样: ```xml <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`,如下: ```xml <select id="getUserList" resultType="usermodel"> <![CDATA[ SELECT * FROM t_user ]]> </select> ``` 上面我们将返回值的类型resultType的值改为了`usermodel` 我们来运行`com.javacode2018.chat03.demo1.UserMapperTest#getUserList`,如下: ```java 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同名的类,如下: ```java 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; } ``` ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/75/34f190de-0b8b-4ddb-9e82-9ecabbf01a87.png) 现在`com.javacode2018.demo1`包中有2个UserModel类了 运行`com.javacode2018.chat03.demo1.UserMapperTest#getUserList`,如下: ```java 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`注解来给这个类指定别名: ```java @Alias("user") public class UserModel { } ``` 当mybatis扫描类的时候,发现类上有`Alias`注解,会取这个注解的`value`作为别名,如果没有这个注解,会将类名小写作为别名,如同方式2。 ##### 案例 我们在`com.javacode2018.chat03.demo1.UserModel`类上加上下面注解: ```java @Alias("use") public class UserModel { } ``` 修改`demo1/mapper/UserMapper.xml`,将`resultType`的值设置为`user`: ```xml <select id="getUserList" resultType="user"> <![CDATA[ SELECT * FROM t_user ]]> </select> ``` 再来运行`com.javacode2018.chat03.demo1.UserMapperTest#getUserList`,如下: ```java 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`,如下: ```java 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)中,贴一部分代码大家感受一下: ```java 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`类中也注册了一些别名,代码如下: ```java 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行如下: ```java typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class); ``` 上面这2行,注册了2个别名,别名和类型映射关系如下: ```java JDBC -> JdbcTransactionFactory POOLED -> PooledDataSourceFactory ``` 上面这2个对象,大家应该比较熟悉吧,mybatis全局配置文件(`chat03\src\main\resources\demo1\mybatis-config.xml`)中我们用到过,我们再去看一下,如下: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/75/f68e8a5b-ffdf-4cf4-99cf-20028b70b008.png) 上面2个红框的是不是就是上面注册的2个类型,上面xml中我们写的是完整类型名称,我们可以将其改为别名的方式也是可以的,如下: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/75/bb4f4627-a3b0-41ba-aeed-0f8dfb069f35.png) 我们来运行`com.javacode2018.chat03.demo1.UserMapperTest#getUserList`,看一下能否正常运行,输出如下: ```java 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`类中,包含别名的注册、解析等各种操作。** 我们来看一下别名解析的方法,如下: ```java 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`对象,我们看一下其定义: ```java private final Map<String, Class<?>> typeAliases = new HashMap<>(); ``` 这个对象就是存放别名和具体类型映射关系的,从上面代码中可以看出,通过传入的参数解析对应的类型的时候,会先从`typeAliases`中查找,如果找不到会调用下面代码: ```java value = (Class<T>) Resources.classForName(string); ``` 上面这个方法里面具体是使用下面代码去通过名称解析成类型的: ```java Class.forName(类名完整名称) ``` Class.forName大家应该是很熟悉的,可以获取一个字符串对应的Class对象,如果找不到这个对象,会报错。 #### 别名使用建议 别名的方式可以简化类型的写法,原本很长一串的`UserModel`对象,现在只用写个`user`就行了,用起来是不是挺爽的? 从写法上面来说,确实少帮我们省了一些代码,但是从维护上面来讲,不是很方便。 如Mapper xml直接写别名,看代码的时候,很难知道这个别名对应的具体类型,还需要我们去注册的地方找一下,不是太方便,如果我们在idea中写完整的类名,还可以按住`Ctrl`健,然后用鼠标左键点击`类型`直接可以跳到对应的类定义中去,如果使用别名是无法导航过去的。 整体上来说开发和看代码都不是太方便,只是写法上比价简单。 **所以建议自定义的类尽量别使用别名,而对mybatis中内置的一些别名我们需要知道。** ### 属性配置文件详解 大家看一下chat03\src\main\resources\demo1\mybatis-config.xml中下面这一部分的配置: ```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元素来定义属性信息,如下: ```xml <configuration> <properties> <property name="属性名称" value="属性对应的值"/> </properties> </configuration> ``` 上面通过property元素的方式进行配置属性信息: name:属性的名称 value:属性的值。 如: ```xml <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/> ``` ##### 使用${属性名称}引用属性的值 属性已经定义好了,我们可以通过`${属性名称}`引用定义好的属性的值,如: ```xml <property name="driver" value="${jdbc.driver}"/> ``` ##### 案例 我们在`demo1/mapper/mybatis-config.xml`的`configuration`元素中加入下面配置: ```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="root123"/> </properties> ``` 修改`datasource`的配置: ```xml <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`,如下: ```java 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路径中的配置文件 ```xml <configuration> <properties resource="配置文件路径"/> </configuration> ``` properties元素有个`resource`属性,值为`配置文件相对于classes的路径`,配置文件我们一般放在`src/main/resource`目录,这个目录的文件编译之后会放在`classes`路径中。 ##### 案例 下面我们将上面db的配置放在外部的config.properties文件中。 在`chat03\src\main\resources\demo1`目录新建一个配置文件`config.properties`,内容如下: ```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`中引入上面配置文件: ```xml <!-- 引入外部配置文件 --> <properties resource="demo1/mapper/config.properties"/> ``` 目前`demo1/mapper/mybatis-config.xml`文件内容如下: ```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`,如下: ```java 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还提供了引入远程配置文件的方式,如下: ```xml <properties url="远程配置文件的路径" /> ``` 这次还是使用`properties`元素,不过使用的是`url`属性,如: ```xml <properties url="http://itsoku.com/properties/config.properties" /> ``` 这种方式的案例就不提供了,有兴趣的可以自己去玩玩。 #### 属性配置文件使用建议 上面我们说了3中方式,第2中方式是比较常见的做法,建议大家可以使用第二种方式来引入外部资源配置文件。 #### 问题 **如果3种方式如果我们都写了,mybatis会怎么走?** 下面我们修改一下`resources/demo1/mybatis-config.xml`,使用第一种方式定义属性,如下: ```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`,运行测试用例,报错如下: ```java 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种方式也加入,修改配置: ```xml <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> ``` 再运行一下测试用例,如下: ```java 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`方法中,如下: ```java 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文件 目前我们所涉及到的各种例子都是采用的这种方式,使用下面的方法进行引入: ```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接口,如下: ```xml <mappers> <mapper class="接口的完整类名" /> </mappers> ``` **这种情况下,mybais会去加载`class`对应的接口,然后还会去加载和这个接口同一个目录的同名的xml文件。** 如: ```xml <mappers> <mapper class="com.javacode2018.chat03.demo1.UserMapper" /> </mappers> ``` 上面这种写法,mybatis会自动去注册`UserMapper`接口,还会去查找下面的文件: ```java com/javacode2018/chat03/demo1/UserMapper.xml ``` 大家以后开发项目的时候估计也会看到这种写法,`Mapper接口`和`Mapper xml`文件放在同一个包中。 ##### 案例 下面我们重新创建一个案例,都放在demo2包中。 新建`com.javacode2018.chat03.demo2.UserModel`,如下: ```java 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`,如下: ```java 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 <?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 <?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`,如下: ```java 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个文件: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/75/183d6922-83d6-482b-9ef0-67b5a7079f09.png) 注意一下`UserMapper`接口所在的包中有个同名的`UserMapper.xml`文件,这个如果按照方式2中所说的,会自动加载。 下面我们来运行一下`com.javacode2018.chat03.demo2.UserMapperTest#getUserList`,输出: ```java 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`中加入下面配置: ```xml <mappers> <mapper class="com.javacode2018.chat03.demo2.UserMapper" /> </mappers> ``` 再运行一下,还是报错,如下,还是找不到对应的UserMapper: ```java 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`包的内容,如下图: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/75/f1877477-fe6b-436b-91a0-929c8b9f937e.png) 编译之后的文件中少了`UserMapper.xml`,这个和maven有关,maven编译src/java代码的时候,默认只会对java文件进行编译然后放在target/classes目录,需要在`chat03/pom.xml`中加入下面配置: ```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 <?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中去了,如下图: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/75/eb4ab01c-407e-4e56-8ba8-647028dff0b4.png) > 为什么maven中需要加上面配置,这块大家可以去看公众号中maven系列的文章,里面有详细介绍,maven的相关东西,后面还会经常用到,对这块不熟悉的,建议尽快把maven系列的所有文章都看一遍,以免后面学习的过程中掉队。 我们再次运行一下测试用例`com.javacode2018.chat03.demo2.UserMapperTest#getUserList`,效果如下: ```java 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对应的源码大家可以去看下面这个方法: ```java org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement ``` 方法中会去加载`mapper`元素中`class`属性指定的Mapper接口,然后进行注册,随后会在接口同目录中查找同名的mapper xml文件,将解析这个xml文件,如果mapper xml文件不存在,也不会报错,源码还是比较简单的,大家可以去看一下,加深理解。 #### 方式3:使用package元素批量注册Mapper接口 ##### 批量注册Mapper接口 上面说2种方式都是一个个注册mapper的,如果我们写了很多mapper,是否能够批量注册呢? mybatis提供了扫描包批量注册的方式,需要在mybatis全局配置文件中加入下面配置: ```xml <mappers> <package name="需要扫描的包" /> </mappers> ``` mybatis会扫描`package元素中name属性指定的包及子包中的所有接口`,将其当做`Mapper 接口`进行注册,所以一般我们会创建一个`mapper`包,里面放`Mapper接口`和`同名的Mapper xml文件`。 大家来看一个案例,理解一下。 ##### 案例 这个案例中将对`t_user、t_order`两个表进行查询操作,采用方式3中的package批量引入mapper 接口和xml文件。 所有代码放在demo3包中,大家先看下文件所在的目录: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/75/9481255c-8eb1-4764-af14-9c17efd39019.png) 创建`UserModel`类,如下: ```java 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`类,如下: ```java 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`接口,如下: ```java 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`接口,如下: ```java 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`,如下: ```java <?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 <?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 <?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`,如下: ```java 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`,输出如下: ```java 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`目录中创建下面子目录: ```java com/javacode2018/chat03/demo3/mapper ``` 然后将`com.javacode2018.chat03.demo3.mapper`中的2个xml文件移到上面新创建的目录中去,如下图: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/75/3dc2b54d-1378-4d56-af99-30916b3e6e9f.png) 在去运行一下`com.javacode2018.chat03.demo3.Demo3Test#test`,输出如下: ```java 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的源码在一个地方: ```java org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement ``` 方法中会去扫描指定的包中所有的接口,会将接口作为Mapper接口进行注册,然后还会找这些接口同名的Xml文件,将其注册为Mapper xml文件,相对于对方式2循环的方式。 #### 使用注意 **方式3会扫描指定包中所有的接口,把这些接口作为Mapper接口进行注册,扫描到的类型只要是接口就会被注册,所以指定的包中通常我们只放Mapper接口,避免存放一些不相干的类或者接口。** ### 关于配置和源码 本次讲解到的一些配置都是在mybatis全局配置文件中进行配置的,这些元素配置是有先后顺序的,具体元素是在下面的dtd文件中定义的: ```java http://mybatis.org/dtd/mybatis-3-config.dtd ``` 建议大家去看一下这个dtd配置文件。 Mybatis解析这个配置文件的入口是在下面的方法中: ```java org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration ``` 代码的部分实现如下: ```java 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启动的时候会按顺序加载上面的标签,大家可以去看一下源码,研究一下,下篇继续深入mybatis其他知识点。 ### 总结 1. 掌握别名注册的3种方式,建议大家尽量少使用自定义别名 2. 掌握属性配置3种方式 3. 掌握mapper注册的3种方式及需要注意的地方 ### 案例代码 ```java 链接:https://pan.baidu.com/s/1vt-MAX3oJOu9gyxZAhKkbg 提取码:i8op ``` <a style="display:none" target="_blank" href="https://mp.weixin.qq.com/s/_S1DD2JADnXvpexxaBwLLg" style="color:red; font-size:20px; font-weight:bold">继续收门徒,亲手带,月薪 4W 以下的可以来找我</a> ## 最新资料 1. <a href="https://mp.weixin.qq.com/s?__biz=MzkzOTI3Nzc0Mg==&mid=2247484964&idx=2&sn=c81bce2f26015ee0f9632ddc6c67df03&scene=21#wechat_redirect" target="_blank">尚硅谷 Java 学科全套教程(总 207.77GB)</a> 2. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484192&idx=1&sn=505f2faaa4cc911f553850667749bcbb&scene=21#wechat_redirect" target="_blank">2021 最新版 Java 微服务学习线路图 + 视频</a> 3. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484573&idx=1&sn=7f3d83892186c16c57bc0b99f03f1ffd&scene=21#wechat_redirect" target="_blank">阿里技术大佬整理的《Spring 学习笔记.pdf》</a> 4. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484544&idx=2&sn=c1dfe907cfaa5b9ae8e66fc247ccbe84&scene=21#wechat_redirect" target="_blank">阿里大佬的《MySQL 学习笔记高清.pdf》</a> 5. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485167&idx=1&sn=48d75c8e93e748235a3547f34921dfb7&scene=21#wechat_redirect" target="_blank">2021 版 java 高并发常见面试题汇总.pdf</a> 6. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485664&idx=1&sn=435f9f515a8f881642820d7790ad20ce&scene=21#wechat_redirect" target="_blank">Idea 快捷键大全.pdf</a> ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/1/2883e86e-3eff-404a-8943-0066e5e2b454.png)
#custom-toc-container