Mybatis系列第4篇:Mybatis使用详解(2),一起跟上节奏!

Mybatis系列目标:从入门开始开始掌握一个高级开发所需要的Mybatis技能。

这是mybatis系列第4篇。

主要内容

1、idea创建本篇案例

  • 建库建表

  • 创建项目

2、别名使用详解(typeAliases)

  • 为什么需要使用别名

  • 别名3种用法详解

  • 方式1:使用typeAlias元素注册别名

  • 方式2:使用package元素批量注册别名

  • 方式3:使用package结合@Alias批量注册并指定别名的名称

  • 别名不区分大小写

  • mybatis内置的别名

  • 别名的原理

  • 别名使用建议

3、属性配置详解(properties)

  • 属性配置的3种方式

  • 方式1:通过propertie元素配置属性

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

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

  • 使用建议

  • 相关问题

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

  • 方式1:通过mapper元素resource属性的方式注册Mapper xml文件和Mapper接口

  • 方式2:通过mapper元素class属性的方式注册Mapper接口和Mapper xml文件

  • 方式3:通过package元素批量注册Mapper接口和Mapper xml文件

  • 源码解释

  • 使用注意

idea创建案例

建库建表

  1. /*创建数据库javacode2018*/
  2. DROP DATABASE IF EXISTS `javacode2018`;
  3. CREATE DATABASE `javacode2018`;
  4. USE `javacode2018`;
  5. /*创建表结构*/
  6. DROP TABLE IF EXISTS `t_user`;
  7. CREATE TABLE t_user (
  8.   id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键,用户id,自动增长',
  9.   `name` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '姓名',
  10.   `age` SMALLINT NOT NULL DEFAULT 1 COMMENT '年龄',
  11.   `salary` DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '薪水',
  12.   `sex` TINYINT NOT NULL DEFAULT 0 COMMENT '性别,0:未知,1:男,2:女'
  13. ) COMMENT '用户表';
  14. DROP TABLE IF EXISTS `t_order`;
  15. CREATE TABLE t_order (
  16.   id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键,订单id,自动增长',
  17.   `user_id` BIGINT NOT NULL DEFAULT 0 COMMENT '用户id',
  18.   `price` DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '订单金额'
  19. ) COMMENT '订单表';
  20. /*插入几条测试数据*/
  21. INSERT INTO t_user (`name`,`age`,`salary`,`sex`)
  22. VALUES
  23.   ('路人甲Java',30,50000,1),
  24.   ('javacode2018',30,50000,1),
  25.   ('张学友',56,500000,1),
  26.   ('林志玲',45,88888.88,2);
  27. INSERT INTO t_order (`user_id`,`price`)
  28. VALUES
  29.   (1,88.88),
  30.   (2,666.66);
  31. SELECT * FROM t_user;
  32. SELECT * FROM t_order;

创建工程

整个mybatis系列的代码采用maven模块的方式管理的,可以在文章底部获取,本次我们还是在上一篇的mybatis-series中进行开发,在这个项目中新建一个模块chat03,模块坐标如下:

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

下面我们通过mybatis快速来实现对t_user表增删改查,这个在上一篇的chat02中已经详细讲解过了。

创建mybatis配置文件

chat03\src\main\resources\demo1目录创建,mybatis-config.xml,如下:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3.         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4.         "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6.     <typeAliases></typeAliases>
  7.     <!-- 环境配置,可以配置多个环境 -->
  8.     <environments default="chat04-demo1">
  9.         <!-- 
  10.             environment用来对某个环境进行配置
  11.             id:环境标识,唯一
  12.          -->
  13.         <environment id="chat04-demo1">
  14.             <!-- 事务管理器工厂配置 -->
  15.             <transactionManager type="org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory"/>
  16.             <!-- 数据源工厂配置,使用工厂来创建数据源 -->
  17.             <dataSource type="org.apache.ibatis.datasource.pooled.PooledDataSourceFactory">
  18.                 <property name="driver" value="com.mysql.jdbc.Driver"/>
  19.                 <property name="url" value="jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8"/>
  20.                 <property name="username" value="root"/>
  21.                 <property name="password" value="root123"/>
  22.             </dataSource>
  23.         </environment>
  24.     </environments>
  25.     <mappers>
  26.         <mapper resource="demo1/mapper/UserMapper.xml"/>
  27.     </mappers>
  28. </configuration>
创建UserMapper.xml文件

chat03\src\main\resources\demo1\mapper目录创建,UserMapper.xml,如下:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3.         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4. <mapper namespace="com.javacode2018.chat03.demo1.UserMapper">
  5.     <!-- insert用来定义一个插入操作
  6.          id:操作的具体标识
  7.          parameterType:指定插入操作接受的参数类型
  8.      -->
  9.     <insert id="insertUser" parameterType="com.javacode2018.chat03.demo1.UserModel" useGeneratedKeys="true">
  10.         <![CDATA[
  11.         INSERT INTO t_user (id,name,age,salary,sex) VALUES (#{id},#{name},#{age},#{salary},#{sex})
  12.          ]]>
  13.     </insert>
  14.     <!-- update用来定义一个更新操作
  15.          id:操作的具体标识
  16.          parameterType:指定操作接受的参数类型
  17.      -->
  18.     <update id="updateUser" parameterType="com.javacode2018.chat03.demo1.UserModel">
  19.         <![CDATA[
  20.         UPDATE t_user SET name = #{name},age = #{age},salary = #{salary},sex = #{sex} WHERE id = #{id}
  21.         ]]>
  22.     </update>
  23.     <!-- update用来定义一个删除操作
  24.          id:操作的具体标识
  25.          parameterType:指定操作接受的参数类型
  26.      -->
  27.     <update id="deleteUser" parameterType="java.lang.Long">
  28.         <![CDATA[
  29.         DELETE FROM t_user WHERE id = #{id}
  30.         ]]>
  31.     </update>
  32.     <!-- select用来定义一个查询操作
  33.          id:操作的具体标识
  34.          resultType:指定查询结果保存的类型
  35.      -->
  36.     <select id="getUserList" resultType="com.javacode2018.chat03.demo1.UserModel">
  37.         <![CDATA[
  38.         SELECT * FROM t_user
  39.         ]]>
  40.     </select>
  41. </mapper>
创建UserModel类

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

  1. package com.javacode2018.chat03.demo1;
  2. import lombok.*;
  3. /**
  4.  * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
  5.  */
  6. @Getter
  7. @Setter
  8. @NoArgsConstructor
  9. @AllArgsConstructor
  10. @Builder
  11. @ToString
  12. public class UserModel {
  13.     private Long id;
  14.     private String name;
  15.     private Integer age;
  16.     private Double salary;
  17.     private Integer sex;
  18. }
创建UserMapper接口

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

  1. package com.javacode2018.chat03.demo1;
  2. import java.util.List;
  3. /**
  4.  * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
  5.  */
  6. public interface UserMapper {
  7.     int insertUser(UserModel model);
  8.     int updateUser(UserModel model);
  9.     int deleteUser(Long userId);
  10.     List<UserModel> getUserList();
  11. }
引入logback日志支持

chat03\src\main\resources目录创建logback.xml,如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3.     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  4.         <encoder>
  5.             <pattern>%d{mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
  6.         </encoder>
  7.     </appender>
  8.     <logger name="com.javacode2018" level="debug" additivity="false">
  9.         <appender-ref ref="STDOUT" />
  10.     </logger>
  11. </configuration>
创建测试用例UserMapperTest

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

  1. package com.javacode2018.chat03.demo1;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.apache.ibatis.io.Resources;
  4. import org.apache.ibatis.session.SqlSession;
  5. import org.apache.ibatis.session.SqlSessionFactory;
  6. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  7. import org.junit.Before;
  8. import org.junit.Test;
  9. import java.io.IOException;
  10. import java.io.InputStream;
  11. import java.util.List;
  12. /**
  13.  * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
  14.  */
  15. @Slf4j
  16. public class UserMapperTest {
  17.     private SqlSessionFactory sqlSessionFactory;
  18.     @Before
  19.     public void before() throws IOException {
  20.         //指定mybatis全局配置文件
  21.         String resource = "demo1/mybatis-config.xml";
  22.         //读取全局配置文件
  23.         InputStream inputStream = Resources.getResourceAsStream(resource);
  24.         //构建SqlSessionFactory对象
  25.         SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  26.         this.sqlSessionFactory = sqlSessionFactory;
  27.     }
  28.     @Test
  29.     public void getUserList() {
  30.         try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
  31.             UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  32.             //执行查询操作
  33.             List<UserModel> userModelList = mapper.getUserList();
  34.             userModelList.forEach(item -> {
  35.                 log.info("{}", item);
  36.             });
  37.         }
  38.     }
  39. }

代码解释一下:

上面的before()方法上面有个@Before注解,这个是junit提供的一个注解,通过junit运行每个@Test标注的方法之前,会先运行被@before标注的方法,before()方法中我们创建了SqlSessionFactory对象,所以其他的@Test标注的方法中可以直接使用sqlSessionFactory对象了。

项目结构如下图

注意项目结构如下图,跑起来有问题的可以对照一下。

运行一下测试用例看效果

运行一下UserMapperTest.getUserList()方法,输出如下:

  1. 32:21.991 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
  2. 32:22.028 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
  3. 32:22.052 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
  4. 32:22.053 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
  5. 32:22.056 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
  6. 32:22.056 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=张学友, age=56, salary=500000.0, sex=1)
  7. 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文件看一下,是不是有很多下面这样的代码:

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

parameterType是指定参数的类型,resultType是指定查询结果返回值的类型,他们的值都是UserModel类完整的类名,比较长,mybatis支持我们给某个类型起一个别名,然后通过别名可以访问到指定的类型。

别名的用法

使用别名之前需要先在mybatis中注册别名,我们先说通过mybatis全局配置文件中注册别名,通过mybatis配置文件注册别名有3种方式。

方式1

使用typeAlias元素进行注册

如下:

  1. <typeAliases>
  2.     <typeAlias type="玩转的类型名称" alias="别名" />
  3. </typeAliases>

typeAliases元素中可以包含多个typeAlias子元素,每个typeAlias可以给一个类型注册别名,有2个属性需要指定:

type:完整的类型名称

alias:别名

如上面给UserModel起了一个别名为user

案例

给UserModel注册一个别名user

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

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

  1. <select id="getUserList" resultType="user">
  2.     <![CDATA[
  3.     SELECT * FROM t_user
  4.     ]]>
  5. </select>

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

  1. 07:35.477 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
  2. 07:35.505 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
  3. 07:35.527 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
  4. 07:35.527 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
  5. 07:35.529 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
  6. 07:35.529 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=张学友, age=56, salary=500000.0, sex=1)
  7. 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元素,如下:

  1. <typeAliases>
  2.     <package name="需要扫描的包"/>
  3. </typeAliases>

这个也是在typeAliases元素下面,不过这次使用的是package元素,package有个name属性,可以指定一个包名,mybatis会加载这个包以及子包中所有的类型,给这些类型都注册别名,别名名称默认会采用类名小写的方式,如UserModel的别名为usermodel

案例

下面我们将demo1/mybatis-config.xmltypeAliases元素的值改为下面这样:

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

mybatis会给com.javacode2018.chat03.demo1包及子包中的所有类型注册别名,UserModel类在这个包中,会被注册,别名为usermodel

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

  1. <select id="getUserList" resultType="usermodel">
  2.     <![CDATA[
  3.     SELECT * FROM t_user
  4.     ]]>
  5. </select>

上面我们将返回值的类型resultType的值改为了usermodel

我们来运行com.javacode2018.chat03.demo1.UserMapperTest#getUserList,如下:

  1. 26:08.267 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
  2. 26:08.296 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
  3. 26:08.318 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
  4. 26:08.319 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
  5. 26:08.320 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
  6. 26:08.320 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=张学友, age=56, salary=500000.0, sex=1)
  7. 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同名的类,如下:

  1. package com.javacode2018.chat03.demo1.model;
  2. import lombok.*;
  3. /**
  4.  * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
  5.  */
  6. @Getter
  7. @Setter
  8. @NoArgsConstructor
  9. @AllArgsConstructor
  10. @Builder
  11. @ToString
  12. public class UserModel {
  13.     private Long id;
  14.     private String name;
  15.     private Integer age;
  16.     private Double salary;
  17.     private Integer sex;
  18. }

现在com.javacode2018.demo1包中有2个UserModel类了

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

  1. org.apache.ibatis.exceptions.PersistenceException: 
  2. ### Error building SqlSession.
  3. ### The error may exist in SQL Mapper Configuration
  4. ### 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'.
  5.     at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
  6.     at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80)
  7.     at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64)
  8.     at com.javacode2018.chat03.demo1.UserMapperTest.before(UserMapperTest.java:29)
  9.     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  10.     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  11.     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  12.     at java.lang.reflect.Method.invoke(Method.java:498)
  13.     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
  14.     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
  15.     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
  16.     at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
  17.     at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
  18.     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
  19.     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
  20.     at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
  21.     at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
  22.     at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
  23.     at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
  24.     at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
  25.     at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
  26.     at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
  27.     at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
  28.     at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
  29.     at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
  30.     at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
  31. 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'.
  32.     at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:121)
  33.     at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:98)
  34.     at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:78)
  35.     ... 24 more
  36. Caused by: org.apache.ibatis.type.TypeException: The alias 'UserModel' is already mapped to the value 'com.javacode2018.chat03.demo1.model.UserModel'.
  37.     at org.apache.ibatis.type.TypeAliasRegistry.registerAlias(TypeAliasRegistry.java:157)
  38.     at org.apache.ibatis.type.TypeAliasRegistry.registerAlias(TypeAliasRegistry.java:147)
  39.     at org.apache.ibatis.type.TypeAliasRegistry.registerAliases(TypeAliasRegistry.java:136)
  40.     at org.apache.ibatis.type.TypeAliasRegistry.registerAliases(TypeAliasRegistry.java:125)
  41.     at org.apache.ibatis.builder.xml.XMLConfigBuilder.typeAliasesElement(XMLConfigBuilder.java:164)
  42.     at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:109)
  43.     ... 26 more

报错了,2个类的类名一样了,默认都会使用usermodel作为别名,别名重复了mybatis会报错,那么此时我们怎么办呢?

package方式批量注册别名的时候,我们可以给类中添加一个@Alias注解来给这个类指定别名:

  1. @Alias("user")
  2. public class UserModel {
  3. }

当mybatis扫描类的时候,发现类上有Alias注解,会取这个注解的value作为别名,如果没有这个注解,会将类名小写作为别名,如同方式2。

案例

我们在com.javacode2018.chat03.demo1.UserModel类上加上下面注解:

  1. @Alias("use")
  2. public class UserModel {
  3. }

修改demo1/mapper/UserMapper.xml,将resultType的值设置为user

  1. <select id="getUserList" resultType="user">
  2.     <![CDATA[
  3.     SELECT * FROM t_user
  4.     ]]>
  5. </select>

再来运行com.javacode2018.chat03.demo1.UserMapperTest#getUserList,如下:

  1. 18:51.219 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
  2. 18:51.250 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
  3. 18:51.271 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
  4. 18:51.272 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
  5. 18:51.274 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
  6. 18:51.274 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=张学友, age=56, salary=500000.0, sex=1)
  7. 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,如下:

  1. <select id="getUserList" resultType="USER">
  2.     <![CDATA[
  3.     SELECT * FROM t_user
  4.     ]]>
  5. </select>

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

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

也是正常的,说明别名使用时是不区分大小写的。

mybatis内置的别名

mybatis默认为很多类型提供了别名,如下:

别名对应的实际类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

上面这些默认都是在org.apache.ibatis.type.TypeAliasRegistry类中进行注册的,这个类就是mybatis注册别名使用的,别名和具体的类型关联是放在这个类的一个map属性(typeAliases)中,贴一部分代码大家感受一下:

  1. public class TypeAliasRegistry {
  2.   private final Map<String, Class<?>> typeAliases = new HashMap<>();
  3.   public TypeAliasRegistry() {
  4.     registerAlias("string", String.class);
  5.     registerAlias("byte", Byte.class);
  6.     registerAlias("long", Long.class);
  7.     registerAlias("short", Short.class);
  8.     registerAlias("int", Integer.class);
  9.     registerAlias("integer", Integer.class);
  10.     registerAlias("double", Double.class);
  11.     registerAlias("float", Float.class);
  12.     registerAlias("boolean", Boolean.class);
  13.     registerAlias("byte[]", Byte[].class);
  14.     registerAlias("long[]", Long[].class);
  15.     registerAlias("short[]", Short[].class);
  16.     registerAlias("int[]", Integer[].class);
  17.     registerAlias("integer[]", Integer[].class);
  18.     registerAlias("double[]", Double[].class);
  19.     registerAlias("float[]", Float[].class);
  20.     registerAlias("boolean[]", Boolean[].class);
  21.     registerAlias("_byte"byte.class);
  22.     registerAlias("_long", long.class);
  23.     registerAlias("_short", short.class);
  24.     registerAlias("_int"int.class);
  25.     registerAlias("_integer"int.class);
  26.     registerAlias("_double", double.class);
  27.     registerAlias("_float", float.class);
  28.     registerAlias("_boolean", boolean.class);
  29.     registerAlias("_byte[]"byte[].class);
  30.     registerAlias("_long[]", long[].class);
  31.     registerAlias("_short[]", short[].class);
  32.     registerAlias("_int[]"int[].class);
  33.     registerAlias("_integer[]"int[].class);
  34.     registerAlias("_double[]", double[].class);
  35.     registerAlias("_float[]", float[].class);
  36.     registerAlias("_boolean[]", boolean[].class);
  37.     registerAlias("date", Date.class);
  38.     registerAlias("decimal", BigDecimal.class);
  39.     registerAlias("bigdecimal", BigDecimal.class);
  40.     registerAlias("biginteger", BigInteger.class);
  41.     registerAlias("object", Object.class);
  42.     registerAlias("date[]", Date[].class);
  43.     registerAlias("decimal[]", BigDecimal[].class);
  44.     registerAlias("bigdecimal[]", BigDecimal[].class);
  45.     registerAlias("biginteger[]", BigInteger[].class);
  46.     registerAlias("object[]", Object[].class);
  47.     registerAlias("map", Map.class);
  48.     registerAlias("hashmap", HashMap.class);
  49.     registerAlias("list", List.class);
  50.     registerAlias("arraylist", ArrayList.class);
  51.     registerAlias("collection", Collection.class);
  52.     registerAlias("iterator", Iterator.class);
  53.     registerAlias("ResultSet", ResultSet.class);
  54.   }
  55. }

mybatis启动的时候会加载全局配置文件,会将其转换为一个org.apache.ibatis.session.Configuration对象,存储在内存中,Configuration类中也注册了一些别名,代码如下:

  1. typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
  2. typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
  3. typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
  4. typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
  5. typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
  6. typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
  7. typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
  8. typeAliasRegistry.registerAlias("LRU", LruCache.class);
  9. typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
  10. typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
  11. typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
  12. typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
  13. typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
  14. typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
  15. typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
  16. typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
  17. typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
  18. typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
  19. typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
  20. typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
  21. typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
  22. typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

上面有2行如下:

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

上面这2行,注册了2个别名,别名和类型映射关系如下:

  1. JDBC -> JdbcTransactionFactory
  2. POOLED -> PooledDataSourceFactory

上面这2个对象,大家应该比较熟悉吧,mybatis全局配置文件(chat03\src\main\resources\demo1\mybatis-config.xml)中我们用到过,我们再去看一下,如下:

上面2个红框的是不是就是上面注册的2个类型,上面xml中我们写的是完整类型名称,我们可以将其改为别名的方式也是可以的,如下:

我们来运行com.javacode2018.chat03.demo1.UserMapperTest#getUserList,看一下能否正常运行,输出如下:

  1. 44:10.886 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
  2. 44:10.929 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
  3. 44:10.947 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
  4. 44:10.948 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
  5. 44:10.950 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
  6. 44:10.950 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=张学友, age=56, salary=500000.0, sex=1)
  7. 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类中,包含别名的注册、解析等各种操作。

我们来看一下别名解析的方法,如下:

  1. public <T> Class<T> resolveAlias(String string) {
  2.     try {
  3.       if (string == null) {
  4.         return null;
  5.       }
  6.       // issue #748
  7.       String key = string.toLowerCase(Locale.ENGLISH);
  8.       Class<T> value;
  9.       if (typeAliases.containsKey(key)) {
  10.         value = (Class<T>) typeAliases.get(key);
  11.       } else {
  12.         value = (Class<T>) Resources.classForName(string);
  13.       }
  14.       return value;
  15.     } catch (ClassNotFoundException e) {
  16.       throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
  17.     }
  18.   }

有一个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中下面这一部分的配置:

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

这个连接数据库的配置,我们是直接写在mybatis全局配置文件中的,上面这是我们本地测试库的db信息,上线之后,需要修改为线上的db配置信息,db配置信息一般由运维去修改,让运维去修改这个xml配置文件?

这样不是太好,我们通常将一些需要运维修改的配置信息(如:db配置、邮件配置、redis配置等等各种配置)放在一个properties配文件中,然后上线时,只需要运维去修改这个配置文件就可以了,根本不用他们去修改和代码相关的文件。

mybatis也支持我们通过外部properties文件来配置一些属性信息。

mybatis配置属性信息有3种方式。

方式1:property元素中定义属性

属性定义

mybatis全局配置文件中通过properties元素来定义属性信息,如下:

  1. <configuration>
  2.     <properties>
  3.         <property name="属性名称" value="属性对应的值"/>
  4.     </properties>
  5. </configuration>

上面通过property元素的方式进行配置属性信息:

name:属性的名称

value:属性的值。

如:

<property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
使用${属性名称}引用属性的值

属性已经定义好了,我们可以通过${属性名称}引用定义好的属性的值,如:

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

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

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

修改datasource的配置:

  1. <dataSource type="POOLED">
  2.     <property name="driver" value="${jdbc.driver}"/>
  3.     <property name="url" value="${jdbc.url}"/>
  4.     <property name="username" value="${jdbc.username}"/>
  5.     <property name="password" value="${jdbc.password}"/>
  6. </dataSource>

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

  1. 40:22.274 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
  2. 40:22.307 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
  3. 40:22.330 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
  4. 40:22.331 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
  5. 40:22.332 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
  6. 40:22.332 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=张学友, age=56, salary=500000.0, sex=1)
  7. 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路径中的配置文件
  1. <configuration>
  2.     <properties resource="配置文件路径"/>
  3. </configuration>

properties元素有个resource属性,值为配置文件相对于classes的路径,配置文件我们一般放在src/main/resource目录,这个目录的文件编译之后会放在classes路径中。

案例

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

chat03\src\main\resources\demo1目录新建一个配置文件config.properties,内容如下:

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

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

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

目前demo1/mapper/mybatis-config.xml文件内容如下:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3.         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4.         "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6.     <!-- 引入外部配置文件 -->
  7.     <properties resource="demo1/config.properties"/>
  8.     <typeAliases>
  9.         <package name="com.javacode2018.chat03.demo1"/>
  10.     </typeAliases>
  11.     <!-- 环境配置,可以配置多个环境 -->
  12.     <environments default="chat04-demo1">
  13.         <!-- 
  14.             environment用来对某个环境进行配置
  15.             id:环境标识,唯一
  16.          -->
  17.         <environment id="chat04-demo1">
  18.             <!-- 事务管理器工厂配置 -->
  19.             <transactionManager type="JDBC"/>
  20.             <!-- 数据源工厂配置,使用工厂来创建数据源 -->
  21.             <dataSource type="POOLED">
  22.                 <property name="driver" value="${jdbc.driver}"/>
  23.                 <property name="url" value="${jdbc.url}"/>
  24.                 <property name="username" value="${jdbc.username}"/>
  25.                 <property name="password" value="${jdbc.password}"/>
  26.             </dataSource>
  27.         </environment>
  28.     </environments>
  29.     <mappers>
  30.         <mapper resource="demo1/mapper/UserMapper.xml"/>
  31.     </mappers>
  32. </configuration>

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

  1. 57:40.405 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
  2. 57:40.436 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
  3. 57:40.454 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
  4. 57:40.455 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
  5. 57:40.457 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
  6. 57:40.457 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=张学友, age=56, salary=500000.0, sex=1)
  7. 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,使用第一种方式定义属性,如下:

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

password的值改为了root,正确的是root123,运行测试用例,报错如下:

  1. org.apache.ibatis.exceptions.PersistenceException: 
  2. ### Error querying database.  Cause: java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)
  3. ### The error may exist in demo1/mapper/UserMapper.xml
  4. ### The error may involve com.javacode2018.chat03.demo1.UserMapper.getUserList
  5. ### The error occurred while executing a query
  6. ### Cause: java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)
  7.     at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
  8.     at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:149)
  9.     at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
  10.     at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:147)
  11.     at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:80)
  12.     at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:93)
  13.     at com.sun.proxy.$Proxy6.getUserList(Unknown Source)
  14.     at com.javacode2018.chat03.demo1.UserMapperTest.getUserList(UserMapperTest.java:38)
  15.     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  16.     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  17.     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  18.     at java.lang.reflect.Method.invoke(Method.java:498)
  19.     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
  20.     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
  21.     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
  22.     at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
  23.     at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
  24.     at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
  25.     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
  26.     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
  27.     at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
  28.     at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
  29.     at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
  30.     at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
  31.     at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
  32.     at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
  33.     at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
  34.     at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
  35.     at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
  36.     at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
  37.     at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
  38. Caused by: java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)

提示密码错误。

下面我们将第2种方式也加入,修改配置:

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

再运行一下测试用例,如下:

  1. 18:59.436 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
  2. 18:59.462 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - ==> Parameters: 
  3. 18:59.481 [main] DEBUG c.j.c.demo1.UserMapper.getUserList - <==      Total: 4
  4. 18:59.482 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
  5. 18:59.485 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
  6. 18:59.485 [main] INFO  c.j.chat03.demo1.UserMapperTest - UserModel(id=3, name=张学友, age=56, salary=500000.0, sex=1)
  7. 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方法中,如下:

  1. private void propertiesElement(XNode context) throws Exception {
  2.     if (context != null) {
  3.       Properties defaults = context.getChildrenAsProperties();
  4.       String resource = context.getStringAttribute("resource");
  5.       String url = context.getStringAttribute("url");
  6.       if (resource != null && url != null) {
  7.         throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
  8.       }
  9.       if (resource != null) {
  10.         defaults.putAll(Resources.getResourceAsProperties(resource));
  11.       } else if (url != null) {
  12.         defaults.putAll(Resources.getUrlAsProperties(url));
  13.       }
  14.       Properties vars = configuration.getVariables();
  15.       if (vars != null) {
  16.         defaults.putAll(vars);
  17.       }
  18.       parser.setVariables(defaults);
  19.       configuration.setVariables(defaults);
  20.     }
  21.   }

从上面代码中也可以看出,如果方式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文件

目前我们所涉及到的各种例子都是采用的这种方式,使用下面的方法进行引入:

  1. <mappers>
  2.     <mapper resource="Mapper xml的路径(相对于classes的路径)"/>
  3. </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接口,如下:

  1. <mappers>
  2.         <mapper class="接口的完整类名" />
  3. </mappers>

这种情况下,mybais会去加载class对应的接口,然后还会去加载和这个接口同一个目录的同名的xml文件。

如:

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

上面这种写法,mybatis会自动去注册UserMapper接口,还会去查找下面的文件:

com/javacode2018/chat03/demo1/UserMapper.xml

大家以后开发项目的时候估计也会看到这种写法,Mapper接口Mapper xml文件放在同一个包中。

案例

下面我们重新创建一个案例,都放在demo2包中。

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

  1. package com.javacode2018.chat03.demo2;
  2. import lombok.*;
  3. import org.apache.ibatis.type.Alias;
  4. /**
  5.  * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
  6.  */
  7. @Getter
  8. @Setter
  9. @NoArgsConstructor
  10. @AllArgsConstructor
  11. @Builder
  12. @ToString
  13. public class UserModel {
  14.     private Long id;
  15.     private String name;
  16.     private Integer age;
  17.     private Double salary;
  18.     private Integer sex;
  19. }

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

  1. package com.javacode2018.chat03.demo2;
  2. import java.util.List;
  3. /**
  4.  * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
  5.  */
  6. public interface UserMapper {
  7.     List<UserModel> getUserList();
  8. }

chat03\src\main\java\com\javacode2018\chat03\demo2中创建UserMapper.xml,如下:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3.         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4. <mapper namespace="com.javacode2018.chat03.demo2.UserMapper">
  5.     <!-- select用来定义一个查询操作
  6.          id:操作的具体标识
  7.          resultType:指定查询结果保存的类型
  8.      -->
  9.     <select id="getUserList" resultType="com.javacode2018.chat03.demo2.UserModel">
  10.         <![CDATA[
  11.         SELECT * FROM t_user
  12.         ]]>
  13.     </select>
  14. </mapper>

下面重点来了。

创建mybatis全局配置文件,在chat03\src\main\resources\demo2目录中创建mybatis-config.xml,如下:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3.         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4.         "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6.     <properties>
  7.         <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
  8.         <property name="jdbc.url" value="jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8"/>
  9.         <property name="jdbc.username" value="root"/>
  10.         <property name="jdbc.password" value="root123"/>
  11.     </properties>
  12.     <!-- 环境配置,可以配置多个环境 -->
  13.     <environments default="chat04-demo2">
  14.         <!-- 
  15.             environment用来对某个环境进行配置
  16.             id:环境标识,唯一
  17.          -->
  18.         <environment id="chat04-demo2">
  19.             <!-- 事务管理器工厂配置 -->
  20.             <transactionManager type="JDBC"/>
  21.             <!-- 数据源工厂配置,使用工厂来创建数据源 -->
  22.             <dataSource type="POOLED">
  23.                 <property name="driver" value="${jdbc.driver}"/>
  24.                 <property name="url" value="${jdbc.url}"/>
  25.                 <property name="username" value="${jdbc.username}"/>
  26.                 <property name="password" value="${jdbc.password}"/>
  27.             </dataSource>
  28.         </environment>
  29.     </environments>
  30. </configuration>

chat03\src\test\java目录创建测试用例com.javacode2018.chat03.demo2.UserMapperTest,如下:

  1. package com.javacode2018.chat03.demo2;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.apache.ibatis.io.Resources;
  4. import org.apache.ibatis.session.SqlSession;
  5. import org.apache.ibatis.session.SqlSessionFactory;
  6. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  7. import org.junit.Before;
  8. import org.junit.Test;
  9. import java.io.IOException;
  10. import java.io.InputStream;
  11. import java.util.List;
  12. /**
  13.  * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
  14.  */
  15. @Slf4j
  16. public class UserMapperTest {
  17.     private SqlSessionFactory sqlSessionFactory;
  18.     @Before
  19.     public void before() throws IOException {
  20.         //指定mybatis全局配置文件
  21.         String resource = "demo2/mybatis-config.xml";
  22.         //读取全局配置文件
  23.         InputStream inputStream = Resources.getResourceAsStream(resource);
  24.         //构建SqlSessionFactory对象
  25.         SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  26.         this.sqlSessionFactory = sqlSessionFactory;
  27.     }
  28.     @Test
  29.     public void getUserList() {
  30.         try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
  31.             UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  32.             //执行查询操作
  33.             List<UserModel> userModelList = mapper.getUserList();
  34.             userModelList.forEach(item -> {
  35.                 log.info("{}", item);
  36.             });
  37.         }
  38.     }
  39. }

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

我们先来看一下项目结构,4个文件:

注意一下UserMapper接口所在的包中有个同名的UserMapper.xml文件,这个如果按照方式2中所说的,会自动加载。

下面我们来运行一下com.javacode2018.chat03.demo2.UserMapperTest#getUserList,输出:

  1. org.apache.ibatis.binding.BindingException: Type interface com.javacode2018.chat03.demo2.UserMapper is not known to the MapperRegistry.
  2.     at org.apache.ibatis.binding.MapperRegistry.getMapper(MapperRegistry.java:47)
  3.     at org.apache.ibatis.session.Configuration.getMapper(Configuration.java:779)
  4.     at org.apache.ibatis.session.defaults.DefaultSqlSession.getMapper(DefaultSqlSession.java:291)
  5.     at com.javacode2018.chat03.demo2.UserMapperTest.getUserList(UserMapperTest.java:36)
  6.     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  7.     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  8.     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  9.     at java.lang.reflect.Method.invoke(Method.java:498)
  10.     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
  11.     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
  12.     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
  13.     at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
  14.     at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
  15.     at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
  16.     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
  17.     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
  18.     at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
  19.     at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
  20.     at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
  21.     at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
  22.     at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
  23.     at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
  24.     at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
  25.     at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
  26.     at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
  27.     at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
  28.     at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

从输出中可以看到,UserMapper找不到。

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

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

再运行一下,还是报错,如下,还是找不到对应的UserMapper:

  1. org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.javacode2018.chat03.demo2.UserMapper.getUserList
  2.     at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:235)
  3.     at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:53)

还是有问题,我们看一下target/classesdemo2包的内容,如下图:

编译之后的文件中少了UserMapper.xml,这个和maven有关,maven编译src/java代码的时候,默认只会对java文件进行编译然后放在target/classes目录,需要在chat03/pom.xml中加入下面配置:

  1. <build>
  2.     <resources>
  3.         <resource>
  4.             <directory>${project.basedir}/src/main/java</directory>
  5.             <includes>
  6.                 <include>**/*.xml</include>
  7.             </includes>
  8.         </resource>
  9.         <resource>
  10.             <directory>${project.basedir}/src/main/resources</directory>
  11.             <includes>
  12.                 <include>**/*</include>
  13.             </includes>
  14.         </resource>
  15.     </resources>
  16. </build>

最终chat03/pom.xml内容如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5.     <parent>
  6.         <artifactId>mybatis-series</artifactId>
  7.         <groupId>com.javacode2018</groupId>
  8.         <version>1.0-SNAPSHOT</version>
  9.     </parent>
  10.     <modelVersion>4.0.0</modelVersion>
  11.     <artifactId>chat03</artifactId>
  12.     <dependencies>
  13.         <!-- mybatis依赖 -->
  14.         <dependency>
  15.             <groupId>org.mybatis</groupId>
  16.             <artifactId>mybatis</artifactId>
  17.         </dependency>
  18.         <!-- mysql 驱动 -->
  19.         <dependency>
  20.             <groupId>mysql</groupId>
  21.             <artifactId>mysql-connector-java</artifactId>
  22.         </dependency>
  23.         <!-- lombok支持 -->
  24.         <dependency>
  25.             <groupId>org.projectlombok</groupId>
  26.             <artifactId>lombok</artifactId>
  27.         </dependency>
  28.         <!-- 单元测试junit支持 -->
  29.         <dependency>
  30.             <groupId>junit</groupId>
  31.             <artifactId>junit</artifactId>
  32.         </dependency>
  33.         <!-- 引入logback用来输出日志 -->
  34.         <dependency>
  35.             <groupId>ch.qos.logback</groupId>
  36.             <artifactId>logback-classic</artifactId>
  37.         </dependency>
  38.     </dependencies>
  39.     <build>
  40.         <resources>
  41.             <resource>
  42.                 <directory>${project.basedir}/src/main/java</directory>
  43.                 <includes>
  44.                     <include>**/*.xml</include>
  45.                 </includes>
  46.             </resource>
  47.             <resource>
  48.                 <directory>${project.basedir}/src/main/resources</directory>
  49.                 <includes>
  50.                     <include>**/*</include>
  51.                 </includes>
  52.             </resource>
  53.         </resources>
  54.     </build>
  55. </project>

加了这个之后UserMapper.xml就会被放到target的classes中去了,如下图:

为什么maven中需要加上面配置,这块大家可以去看公众号中maven系列的文章,里面有详细介绍,maven的相关东西,后面还会经常用到,对这块不熟悉的,建议尽快把maven系列的所有文章都看一遍,以免后面学习的过程中掉队。

我们再次运行一下测试用例com.javacode2018.chat03.demo2.UserMapperTest#getUserList,效果如下:

  1. 24:37.814 [main] DEBUG c.j.c.demo2.UserMapper.getUserList - ==>  Preparing: SELECT * FROM t_user 
  2. 24:37.852 [main] DEBUG c.j.c.demo2.UserMapper.getUserList - ==> Parameters: 
  3. 24:37.875 [main] DEBUG c.j.c.demo2.UserMapper.getUserList - <==      Total: 4
  4. 24:37.876 [main] INFO  c.j.chat03.demo2.UserMapperTest - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
  5. 24:37.879 [main] INFO  c.j.chat03.demo2.UserMapperTest - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
  6. 24:37.879 [main] INFO  c.j.chat03.demo2.UserMapperTest - UserModel(id=3, name=张学友, age=56, salary=500000.0, sex=1)
  7. 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全局配置文件中加入下面配置:

  1. <mappers>
  2.     <package name="需要扫描的包" />
  3. </mappers>

mybatis会扫描package元素中name属性指定的包及子包中的所有接口,将其当做Mapper 接口进行注册,所以一般我们会创建一个mapper包,里面放Mapper接口同名的Mapper xml文件

大家来看一个案例,理解一下。

案例

这个案例中将对t_user、t_order两个表进行查询操作,采用方式3中的package批量引入mapper 接口和xml文件。

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

创建UserModel类,如下:

  1. package com.javacode2018.chat03.demo3.model;
  2. import lombok.*;
  3. /**
  4.  * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
  5.  */
  6. @Getter
  7. @Setter
  8. @NoArgsConstructor
  9. @AllArgsConstructor
  10. @Builder
  11. @ToString
  12. public class UserModel {
  13.     private Long id;
  14.     private String name;
  15.     private Integer age;
  16.     private Double salary;
  17.     private Integer sex;
  18. }

创建OrderModel类,如下:

  1. package com.javacode2018.chat03.demo3.model;
  2. import lombok.*;
  3. /**
  4.  * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
  5.  */
  6. @Getter
  7. @Setter
  8. @NoArgsConstructor
  9. @AllArgsConstructor
  10. @Builder
  11. @ToString
  12. public class OrderModel {
  13.     private Long id;
  14.     private Long user_id;
  15.     private Double price;
  16. }

创建UserMapper接口,如下:

  1. package com.javacode2018.chat03.demo3.mapper;
  2. import com.javacode2018.chat03.demo3.model.UserModel;
  3. import java.util.List;
  4. /**
  5.  * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
  6.  */
  7. public interface UserMapper {
  8.     List<UserModel> getList();
  9. }

创建OrderMapper接口,如下:

  1. package com.javacode2018.chat03.demo3.mapper;
  2. import com.javacode2018.chat03.demo3.model.OrderModel;
  3. import com.javacode2018.chat03.demo3.model.UserModel;
  4. import java.util.List;
  5. /**
  6.  * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
  7.  */
  8. public interface OrderMapper {
  9.     List<OrderModel> getList();
  10. }

创建UserMapper.xml,如下:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3.         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4. <mapper namespace="com.javacode2018.chat03.demo3.mapper.OrderMapper">
  5.     <!-- select用来定义一个查询操作
  6.          id:操作的具体标识
  7.          resultType:指定查询结果保存的类型
  8.      -->
  9.     <select id="getList" resultType="com.javacode2018.chat03.demo3.model.OrderModel">
  10.         <![CDATA[
  11.         SELECT * FROM t_order
  12.         ]]>
  13.     </select>
  14. </mapper>

上面我们写了一个查询t_user数据的sql

创建OrderMapper.xml,如下:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3.         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4. <mapper namespace="com.javacode2018.chat03.demo3.mapper.OrderMapper">
  5.     <!-- select用来定义一个查询操作
  6.          id:操作的具体标识
  7.          resultType:指定查询结果保存的类型
  8.      -->
  9.     <select id="getList" resultType="com.javacode2018.chat03.demo3.model.OrderModel">
  10.         <![CDATA[
  11.         SELECT * FROM t_order
  12.         ]]>
  13.     </select>
  14. </mapper>

上面我们写了一个查询t_order数据的sql

创建resources/demo3/mybatis-config.xml配置文件,如下:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3.         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4.         "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6.     <properties>
  7.         <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
  8.         <property name="jdbc.url" value="jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8"/>
  9.         <property name="jdbc.username" value="root"/>
  10.         <property name="jdbc.password" value="root123"/>
  11.     </properties>
  12.     <!-- 环境配置,可以配置多个环境 -->
  13.     <environments default="chat04-demo3">
  14.         <!-- 
  15.             environment用来对某个环境进行配置
  16.             id:环境标识,唯一
  17.          -->
  18.         <environment id="chat04-demo3">
  19.             <!-- 事务管理器工厂配置 -->
  20.             <transactionManager type="JDBC"/>
  21.             <!-- 数据源工厂配置,使用工厂来创建数据源 -->
  22.             <dataSource type="POOLED">
  23.                 <property name="driver" value="${jdbc.driver}"/>
  24.                 <property name="url" value="${jdbc.url}"/>
  25.                 <property name="username" value="${jdbc.username}"/>
  26.                 <property name="password" value="${jdbc.password}"/>
  27.             </dataSource>
  28.         </environment>
  29.     </environments>
  30.     <mappers>
  31.         <package name="com.javacode2018.chat03.demo3.mapper"/>
  32.     </mappers>
  33. </configuration>

注意这次我们使用package来让mybatis加载com.javacode2018.chat03.demo3.mapper包下面所有的Mapper接口和Mapper xml文件。

创建测试用例Demo3Test,如下:

  1. package com.javacode2018.chat03.demo3;
  2. import com.javacode2018.chat03.demo3.mapper.OrderMapper;
  3. import com.javacode2018.chat03.demo3.mapper.UserMapper;
  4. import com.javacode2018.chat03.demo3.model.OrderModel;
  5. import com.javacode2018.chat03.demo3.model.UserModel;
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.apache.ibatis.io.Resources;
  8. import org.apache.ibatis.session.SqlSession;
  9. import org.apache.ibatis.session.SqlSessionFactory;
  10. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  11. import org.junit.Before;
  12. import org.junit.Test;
  13. import java.io.IOException;
  14. import java.io.InputStream;
  15. import java.util.List;
  16. /**
  17.  * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
  18.  */
  19. @Slf4j
  20. public class Demo3Test {
  21.     private SqlSessionFactory sqlSessionFactory;
  22.     @Before
  23.     public void before() throws IOException {
  24.         //指定mybatis全局配置文件
  25.         String resource = "demo3/mybatis-config.xml";
  26.         //读取全局配置文件
  27.         InputStream inputStream = Resources.getResourceAsStream(resource);
  28.         //构建SqlSessionFactory对象
  29.         SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  30.         this.sqlSessionFactory = sqlSessionFactory;
  31.     }
  32.     @Test
  33.     public void test() {
  34.         try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
  35.             UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  36.             //执行查询操作
  37.             List<UserModel> userModelList = userMapper.getList();
  38.             userModelList.forEach(item -> {
  39.                 log.info("{}", item);
  40.             });
  41.             log.info("----------------------------------");
  42.             OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
  43.             //执行查询操作
  44.             List<OrderModel> orderModelList = orderMapper.getList();
  45.             orderModelList.forEach(item -> {
  46.                 log.info("{}", item);
  47.             });
  48.         }
  49.     }
  50. }

运行com.javacode2018.chat03.demo3.Demo3Test#test,输出如下:

  1. 48:39.280 [main] DEBUG c.j.c.d.mapper.UserMapper.getList - ==>  Preparing: SELECT * FROM t_user 
  2. 48:39.315 [main] DEBUG c.j.c.d.mapper.UserMapper.getList - ==> Parameters: 
  3. 48:39.339 [main] DEBUG c.j.c.d.mapper.UserMapper.getList - <==      Total: 4
  4. 48:39.340 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
  5. 48:39.343 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
  6. 48:39.343 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=3, name=张学友, age=56, salary=500000.0, sex=1)
  7. 48:39.343 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=4, name=林志玲, age=45, salary=88888.88, sex=2)
  8. 48:39.343 [main] INFO  c.j.chat03.demo3.Demo3Test - ----------------------------------
  9. 48:39.344 [main] DEBUG c.j.c.d.mapper.OrderMapper.getList - ==>  Preparing: SELECT * FROM t_order 
  10. 48:39.345 [main] DEBUG c.j.c.d.mapper.OrderMapper.getList - ==> Parameters: 
  11. 48:39.351 [main] DEBUG c.j.c.d.mapper.OrderMapper.getList - <==      Total: 2
  12. 48:39.351 [main] INFO  c.j.chat03.demo3.Demo3Test - OrderModel(id=1, user_id=1, price=88.88)
  13. 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,输出如下:

  1. 56:22.669 [main] DEBUG c.j.c.d.mapper.UserMapper.getList - ==>  Preparing: SELECT * FROM t_user 
  2. 56:22.700 [main] DEBUG c.j.c.d.mapper.UserMapper.getList - ==> Parameters: 
  3. 56:22.721 [main] DEBUG c.j.c.d.mapper.UserMapper.getList - <==      Total: 4
  4. 56:22.722 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=1, name=路人甲Java, age=30, salary=50000.0, sex=1)
  5. 56:22.725 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=2, name=javacode2018, age=30, salary=50000.0, sex=1)
  6. 56:22.725 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=3, name=张学友, age=56, salary=500000.0, sex=1)
  7. 56:22.725 [main] INFO  c.j.chat03.demo3.Demo3Test - UserModel(id=4, name=林志玲, age=45, salary=88888.88, sex=2)
  8. 56:22.725 [main] INFO  c.j.chat03.demo3.Demo3Test - ----------------------------------
  9. 56:22.727 [main] DEBUG c.j.c.d.mapper.OrderMapper.getList - ==>  Preparing: SELECT * FROM t_order 
  10. 56:22.727 [main] DEBUG c.j.c.d.mapper.OrderMapper.getList - ==> Parameters: 
  11. 56:22.732 [main] DEBUG c.j.c.d.mapper.OrderMapper.getList - <==      Total: 2
  12. 56:22.732 [main] INFO  c.j.chat03.demo3.Demo3Test - OrderModel(id=1, user_id=1, price=88.88)
  13. 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

代码的部分实现如下:

  1.   private void parseConfiguration(XNode root) {
  2.     try {
  3.       //issue #117 read properties first
  4.       propertiesElement(root.evalNode("properties"));
  5.       Properties settings = settingsAsProperties(root.evalNode("settings"));
  6.       loadCustomVfs(settings);
  7.       loadCustomLogImpl(settings);
  8.       typeAliasesElement(root.evalNode("typeAliases"));
  9.       pluginElement(root.evalNode("plugins"));
  10.       objectFactoryElement(root.evalNode("objectFactory"));
  11.       objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
  12.       reflectorFactoryElement(root.evalNode("reflectorFactory"));
  13.       settingsElement(settings);
  14.       // read it after objectFactory and objectWrapperFactory issue #631
  15.       environmentsElement(root.evalNode("environments"));
  16.       databaseIdProviderElement(root.evalNode("databaseIdProvider"));
  17.       typeHandlerElement(root.evalNode("typeHandlers"));
  18.       mapperElement(root.evalNode("mappers"));
  19.     } catch (Exception e) {
  20.       throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  21.     }
  22.   }

可以看到mybatis启动的时候会按顺序加载上面的标签,大家可以去看一下源码,研究一下,下篇继续深入mybatis其他知识点。

总结

  1. 掌握别名注册的3种方式,建议大家尽量少使用自定义别名

  2. 掌握属性配置3种方式

  3. 掌握mapper注册的3种方式及需要注意的地方

案例代码获取方式

扫码添加微信备注:mybatis案例,即可获取

MyBatis系列

  1. MyBatis系列第1篇:MyBatis未出世之前我们那些痛苦的经历

  2. MyBatis系列第2篇:入门篇,带你感受一下mybatis独特的魅力!

  3. MyBatis系列第3篇:Mybatis使用详解(1)

更多好文章

  1. Java高并发系列(共34篇)

  2. MySql高手系列(共27篇)

  3. Maven高手系列(共10篇)

  4. 聊聊db和缓存一致性常见的实现方式

  5. 接口幂等性这么重要,它是什么?怎么实现?

感谢大家的阅读,也欢迎您把这篇文章分享给更多的朋友一起阅读!谢谢!

路人甲java

▲长按图片识别二维码关注

路人甲Java:工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!来源:https://itsoku.blog.csdn.net/article/details/103470997

posted @ 2022-04-23 02:55  程序员小明1024  阅读(76)  评论(0编辑  收藏  举报