Java充电社
专辑
博文
联系我
本人继续续收门徒,亲手指导
MyBatis教程
-> MyBatis入门,你确定mybatis你玩的很溜?
1、MyBatis未出世之前我们那些痛苦的经历
2、MyBatis入门,你确定mybatis你玩的很溜?
3、MyBatis使用详解(-)
4、MyBatis使用详解(2)
5、详解传参的几种方式、原理、源码解析
6、增删改知识点汇总及主键获取3种方式详解
7、种查询详解
8、自动映射,使用需谨慎
9、延迟加载、鉴别器、继承
10、动态SQL,这么多种你都会么?
11、类型处理器
12、掌握缓存为查询提速!
上一篇:MyBatis未出世之前我们那些痛苦的经历
下一篇:MyBatis使用详解(-)
<div style="display:none"></div> ## Mybatis系列第2篇:mybatis入门,你确定mybatis你玩的很溜? ### 本篇技术栈 1. mysql5.7.25 2. maven3.6.1 3. jdk1.8 4. idea ### 本篇主要内容 1. 通过一个案例感受一下mybatis的强大之处 2. mybatis开发项目的具体步骤 3. 介绍mybatis中主要的几个对象 我们先来一个案例,让大家感受一下mybatis是多么的牛逼,我相信大家看了案例之后,会强烈的激发大家学习mybatis的兴趣。 ### 案例:原来ibatis是这么强大 下面的案例,大家先不用关系代码为什么这么写,先感受一下效果,后面我们再来细说代码,**案例代码文章尾部有获取方式**。 #### 准备数据库 mysql中运行下面脚本: ```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 '薪水' ) COMMENT '用户表'; SELECT * FROM t_user; ``` > 上面脚本中,创建了一个`javacode2018`数据库,然后创建了一个用户表,里面有4个字段,id为主键自动增长。 #### 我们的需求 对t_user表,我们有以下这些需求: 1. 实现一个通用的插入操作:支持动态插入,可以根据传入的字段的值,动态生成所需要的各种insert语句 2. 批量插入功能 3. 实现一个通用的更新操作:支持动态更新操作,可以根据传入的字段,动态生成所需要的各种update语句 4. 实现一个通用的查询操作:支持各种组合条件查询、支撑排序、分页、支持返回列的控制等各种复杂的查询需求 下面我们就来一个案例,将上面这些需求通过mybatis实现,先见证一下mybatis的强大之处。 #### 创建maven项目 idea中创建maven项目,这个操作在maven系列中已经说过很多次了,以后大部分项目都是通过maven来玩的,maven这方面玩的不是太溜的,可以跳到文章尾部去看一下maven系列的文章。 项目采用maven中聚合及继承的方式来管理。 ##### 创建父项目 先创建父项目`mybatis-series`,父项目的坐标信息: ```xml <groupId>com.javacode2018</groupId> <artifactId>mybatis-series</artifactId> <version>1.0-SNAPSHOT</version> ``` ##### 创建子项目 创建一个子模块`chat01`,子模块的坐标信息: ```xml <groupId>com.javacode2018</groupId> <artifactId>chat01</artifactId> <version>1.0-SNAPSHOT</version> ``` ##### 项目结构 如下图: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/73/5b1087f4-29ae-4222-922d-20e17fb9d4f8.png) #### 引入mybatis依赖 `mybatis-series/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"> <modelVersion>4.0.0</modelVersion> <groupId>com.javacode2018</groupId> <artifactId>mybatis-series</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>chat01</module> </modules> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- 配置maven编译的时候采用的编译器版本 --> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> <!-- 指定源代码是什么版本的,如果源码和这个版本不符将报错,maven中执行编译的时候会用到这个配置,默认是1.5,这个相当于javac命令后面的-source参数 --> <maven.compiler.source>1.8</maven.compiler.source> <!-- 该命令用于指定生成的class文件将保证和哪个版本的虚拟机进行兼容,maven中执行编译的时候会用到这个配置,默认是1.5,这个相当于javac命令后面的-target参数 --> <maven.compiler.target>1.8</maven.compiler.target> <mybatis.version>3.5.3</mybatis.version> <mysql.version>5.1.47</mysql.version> <lombok.version>1.18.10</lombok.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement> </project> ``` `chat01/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>chat01</artifactId> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </dependency> </dependencies> </project> ``` 上面我们引入了mybatis需要的包、mysql jdbc驱动、lombok、单元测试需要的junit包、日志输出需要的logback包。 这里lombok可能大家没有用过,这个东西可以自动帮我们生成javabean的一些代码,比如get、set方法,可以节省开发编写代码的量,这个以后有空了写一篇文章来介绍。 #### 配置logback mybatis在运行过程中会输出一些日志,比如sql信息、sql的参数信息、执行的结果等信息,mybatis中会通过logback输出出来。 在`chat01/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> ``` #### 创建mybatis相关文件 ##### user.xml `chat01/src/main/resources`目录中新建`user.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.mybatis.chat01.UserMapper"> <!-- 插入 --> <insert id="insert" parameterType="com.javacode2018.mybatis.chat01.UserModel" keyProperty="id" useGeneratedKeys="true"> <![CDATA[ INSERT INTO `t_user` ]]> <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id!=null"> <![CDATA[ `id`, ]]> </if> <if test="name!=null"> <![CDATA[ `name`, ]]> </if> <if test="age!=null"> <![CDATA[ `age`, ]]> </if> <if test="salary!=null"> <![CDATA[ `salary`, ]]> </if> </trim> <![CDATA[ VALUES ]]> <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id!=null"> <![CDATA[ #{id}, ]]> </if> <if test="name!=null"> <![CDATA[ #{name}, ]]> </if> <if test="age!=null"> <![CDATA[ #{age}, ]]> </if> <if test="salary!=null"> <![CDATA[ #{salary}, ]]> </if> </trim> </insert> <!-- 批量插入 --> <insert id="insertBatch" parameterType="map"> <![CDATA[ INSERT INTO `t_user` (`id`, `name`, `age`, `salary`) VALUES ]]> <foreach collection="list" separator="," item="item"> (#{item.id}, #{item.name}, #{item.age}, #{item.salary}) </foreach> </insert> <!-- 更新 --> <update id="update" parameterType="com.javacode2018.mybatis.chat01.UserModel"> <![CDATA[ UPDATE `t_user` ]]> <set> <if test="name!=null"> <![CDATA[ `name` = #{name}, ]]> </if> <if test="age!=null"> <![CDATA[ `age` = #{age}, ]]> </if> <if test="salary!=null"> <![CDATA[ `salary` = #{salary}, ]]> </if> </set> <where> <if test="id!=null"> <![CDATA[ AND `id` = #{id} ]]> </if> </where> </update> <!-- 更新 --> <update id="updateByMap" parameterType="map"> <![CDATA[ UPDATE `t_user` ]]> <set> <if test="name!=null"> <![CDATA[ `name` = #{name}, ]]> </if> <if test="age!=null"> <![CDATA[ `age` = #{age}, ]]> </if> <if test="salary!=null"> <![CDATA[ `salary` = #{salary}, ]]> </if> </set> <where> <if test="id!=null"> <![CDATA[ AND `id` = #{id} ]]> </if> </where> </update> <!-- 删除 --> <delete id="delete" parameterType="map"> <![CDATA[ DELETE FROM `t_user` ]]> <where> <if test="id!=null"> <![CDATA[ AND `id` = #{id} ]]> </if> </where> </delete> <!-- 查询记录 --> <select id="getModelList" parameterType="map" resultType="com.javacode2018.mybatis.chat01.UserModel"> <![CDATA[ SELECT ]]> <choose> <when test="tableColumnList!=null and tableColumnList.size() >= 1"> <foreach collection="tableColumnList" item="item" separator=","> <![CDATA[ ${item} ]]> </foreach> </when> <otherwise> <![CDATA[ `id`, `name`, `age`, `salary` ]]> </otherwise> </choose> <![CDATA[ FROM `t_user` a ]]> <where> <if test="id!=null and id.toString()!=''"> <![CDATA[ AND a.`id` = #{id} ]]> </if> <if test="idList!=null and idList.size() >= 1"> <![CDATA[ AND a.`id` IN ]]> <foreach collection="idList" item="item" open="(" separator="," close=")"> <![CDATA[ #{item} ]]> </foreach> </if> <if test="name!=null and name.toString()!=''"> <![CDATA[ AND a.`name` = #{name} ]]> </if> <if test="age!=null and age.toString()!=''"> <![CDATA[ AND a.`age` = #{age} ]]> </if> <if test="salary!=null and salary.toString()!=''"> <![CDATA[ AND a.`salary` = #{salary} ]]> </if> <if test="nameLike!=null and nameLike.toString()!=''"> <![CDATA[ AND a.`name` like '%${nameLike}%' ]]> </if> <if test="salaryGte!=null and salaryGte.toString()!=''"> <![CDATA[ AND a.`salary` >= #{salaryGte} ]]> </if> </where> <if test="sort!=null and sort.toString()!=''"> <![CDATA[ order by ${sort} ]]> </if> <if test="skip!=null and pageSize!=null"> <![CDATA[ LIMIT #{skip},#{pageSize} ]]> </if> </select> </mapper> ``` ##### mybatis-config.xml `chat01/src/main/resources`目录中新建`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="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"/> </properties> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/user.xml"/> </mappers> </configuration> ``` ##### UserMapper接口 ```java package com.javacode2018.mybatis.chat01; import java.util.List; import java.util.Map; /** * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活! */ public interface UserMapper { /** * 插入用户信息 * * @param userModel * @return */ void insert(UserModel userModel); /** * 批量插入用户信息 * * @param userModelList */ void insertBatch(List<UserModel> userModelList); /** * 更新用户信息 * * @param userModel * @return */ int update(UserModel userModel); /** * 通过map来更新用户记录 * * @param map * @return */ int updateByMap(Map<String, Object> map); /** * 通过map来删除用户记录 * * @param map * @return */ int delete(Map<String, Object> map); /** * 查询用户列表 * * @param map * @return */ List<UserModel> getModelList(Map<String, Object> map); } ``` ##### UserModel类 ```java package com.javacode2018.mybatis.chat01; 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; } ``` > 这个类上面的注解都是都是lombok中的,通过这些注解,lombok可以帮助我们自动生成上面4个字段的get方法、set方法、无参构造方法、有参有参构造方法、builder模式构建对象的代码、重写toString方法,这些都在代码编译为字节码之前会写进去,通过lombok代码是不是精简了很多,最后生成的代码大家可以反编译一下UserModel.class去看一下,感受一下,此处我们就不贴出来了。 ##### UserUtil类 ```java package com.javacode2018.mybatis.chat01; 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 java.io.IOException; /** * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活! */ @Slf4j public class UserUtil { private static SqlSessionFactory sqlSessionFactory = build(); public static SqlSessionFactory build() { try { return new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); } catch (IOException e) { log.error(e.getMessage(), e); throw new RuntimeException(e); } } @FunctionalInterface public interface SessionCall<O> { O call(SqlSession session) throws Exception; } @FunctionalInterface public interface MapperCall<T, O> { O call(T mapper) throws Exception; } public static <T, O> O callMapper(Class<T> tClass, MapperCall<T, O> mapper) throws Exception { return call(session -> mapper.call(session.getMapper(tClass))); } public static <O> O call(SessionCall<O> sessionCall) throws Exception { try (SqlSession session = sqlSessionFactory.openSession(true);) { return sessionCall.call(session); } } } ``` #### 创建单元测试类UserMapperTest `chat01\src\test\java\com\javacode2018\mybatis\chat01`中创建`UserMapperTest`,代码如下: ```java package com.javacode2018.mybatis.chat01; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import java.util.*; import java.util.stream.Collectors; /** * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活! */ @Slf4j public class UserMapperTest { //动态插入 @Test public void insert() throws Exception { UserModel userModel1 = UserModel.builder().name("路人甲Java").build(); UserUtil.callMapper(UserMapper.class, mapper -> { mapper.insert(userModel1); return null; }); log.info("插入结果:{}", this.getModelById(userModel1.getId())); log.info("---------------------"); UserModel userModel2 = UserModel.builder().name("路人").age(30).salary(50000.00).build(); UserUtil.callMapper(UserMapper.class, mapper -> { mapper.insert(userModel2); return null; }); log.info("插入结果:{}", this.getModelById(userModel2.getId())); } //批量插入 @Test public void insertBatch() throws Exception { List<UserModel> userModelList = new ArrayList<>(); for (int i = 1; i <= 5; i++) { userModelList.add(UserModel.builder().name("路人甲Java-" + i).age(30 + i).salary(10000.00 * i).build()); userModelList.add(UserModel.builder().name("javacode2018-" + i).age(30 + i).salary(10000.00 * i).build()); } UserUtil.callMapper(UserMapper.class, mapper -> { mapper.insertBatch(userModelList); return null; }); List<UserModel> userModelList1 = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(null)); log.info("结果:{}", userModelList1); } //根据用户id删除数据 @Test public void delete() throws Exception { Map<String, Object> map = new HashMap<>(); //需要删除的用户id map.put("id", 1); Integer count = UserUtil.callMapper(UserMapper.class, mapper -> mapper.delete(map)); log.info("删除行数:{}", count); } //动态更新 @Test public void update() throws Exception { //将userId=2的name修改为:路人 Long userId1 = 2L; Integer count = UserUtil.callMapper(UserMapper.class, mapper -> mapper.update(UserModel.builder().id(userId1).name("ready").build())); log.info("更新行数:{}", count); log.info("---------------------"); //将userId=3的name修改为:路人,薪水为:1000.88 Long userId2 = 3L; count = UserUtil.callMapper(UserMapper.class, mapper -> mapper.update(UserModel.builder().id(userId2).name("ready").salary(1000.88D).build())); log.info("更新行数:{}", count); } //按用户id查询 public UserModel getModelById(Long userId) throws Exception { //查询指定id的数据 Map<String, Object> map = new HashMap<>(); map.put("id", userId); return UserUtil.callMapper(UserMapper.class, mapper -> { List<UserModel> userModelList = mapper.getModelList(map); if (userModelList.size() == 1) { return userModelList.get(0); } return null; }); } //查询所有数据 @Test public void getModelList1() throws Exception { List<UserModel> userModelList = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(null)); log.info("结果:{}", userModelList); } //查询多个用户id对应的数据 @Test public void getModelListByIds() throws Exception { List<Integer> idList = Arrays.asList(2, 3, 4).stream().collect(Collectors.toList()); Map<String, Object> map = new HashMap<>(); map.put("idList", idList); List<UserModel> userModelList = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(map)); log.info("结果:{}", userModelList); } //多条件 & 指定返回的列 @Test public void getModelList2() throws Exception { //查询姓名中包含路人甲java以及薪资大于3万的用户id、姓名 Map<String, Object> map = new HashMap<>(); map.put("nameLike", "路人甲java"); map.put("salaryGte", 30000.00D); //需要返回的列 List<String> tableColumnList = new ArrayList<>(); tableColumnList.add("id"); tableColumnList.add("name"); map.put("tableColumnList", tableColumnList); List<UserModel> userModelList = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(map)); log.info("结果:{}", userModelList); } //条件过滤 & 排序 & 分页查询数据 & 只返回用户id、salary @Test public void getPage() throws Exception { //查询姓名中包含路人甲java以及薪资大于3万的用户id,按照薪资倒叙,每页5条取第1页 Map<String, Object> map = new HashMap<>(); map.put("nameLike", "路人甲java"); map.put("salaryGte", 30000.00D); //加入排序参数 map.put("sort", "salary desc"); //加入分页参数 int page = 1; int pageSize = 5; map.put("skip", (page - 1) * pageSize); map.put("pageSize", pageSize); //加入需要返回的列 List<String> tableColumnList = new ArrayList<>(); tableColumnList.add("id"); tableColumnList.add("salary"); map.put("tableColumnList", tableColumnList); List<UserModel> userModelList = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(map)); log.info("结果:{}", userModelList); } } ``` #### 项目最终结构如下 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/73/d496572c-0875-4dda-8d74-729bae9e5104.png) #### 用例:动态插入 运行`UserMapperTest#insert`,输出如下: ``` 37:58.556 [main] DEBUG c.j.mybatis.chat01.UserMapper.insert - ==> Preparing: INSERT INTO `t_user` ( `name` ) VALUES ( ? ) 37:58.605 [main] DEBUG c.j.mybatis.chat01.UserMapper.insert - ==> Parameters: 路人甲Java(String) 37:58.613 [main] DEBUG c.j.mybatis.chat01.UserMapper.insert - <== Updates: 1 37:58.641 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Preparing: SELECT `id`, `name`, `age`, `salary` FROM `t_user` a WHERE a.`id` = ? 37:58.641 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Parameters: 1(Long) 37:58.663 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - <== Total: 1 37:58.664 [main] INFO c.j.mybatis.chat01.UserMapperTest - 插入结果:UserModel(id=1, name=路人甲Java, age=1, salary=0.0) 37:58.667 [main] INFO c.j.mybatis.chat01.UserMapperTest - --------------------- 37:58.668 [main] DEBUG c.j.mybatis.chat01.UserMapper.insert - ==> Preparing: INSERT INTO `t_user` ( `name`, `age`, `salary` ) VALUES ( ?, ?, ? ) 37:58.675 [main] DEBUG c.j.mybatis.chat01.UserMapper.insert - ==> Parameters: 路人(String), 30(Integer), 50000.0(Double) 37:58.679 [main] DEBUG c.j.mybatis.chat01.UserMapper.insert - <== Updates: 1 37:58.681 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Preparing: SELECT `id`, `name`, `age`, `salary` FROM `t_user` a WHERE a.`id` = ? 37:58.681 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Parameters: 2(Long) 37:58.683 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - <== Total: 1 37:58.683 [main] INFO c.j.mybatis.chat01.UserMapperTest - 插入结果:UserModel(id=2, name=路人, age=30, salary=50000.0) ``` `UserMapperTest#insert`这个方法主要有4步操作: 步骤1:插入一条用户记录,用户记录只有name字段有值 步骤2:去db中查询步骤1中插入的记录 步骤3:插入一条用户记录,这次插入的记录所有字段都指定了值 步骤4:去db中查询步骤3中插入的记录 **重点来了:**大家认真看一下`UserMapperTest#insert`方法的代码,两个插入调用都是`mapper.insert`方法,传入的都是`UserModel`对象,唯一不同的是这个对象构建的时候字段的值不一样,最后再认真看一下上面输出的sql,产生的2个insert也是不一样的,这个`mapper.insert`方法可以根据`UserModel`对象字段是否有值来组装我们需要的sql,是不是很牛逼,这就是动态插入。 #### 用例:批量插入 运行`UserMapperTest#insertBatch`,输出如下: ```java 38:12.425 [main] DEBUG c.j.m.chat01.UserMapper.insertBatch - ==> Preparing: INSERT INTO `t_user` (`id`, `name`, `age`, `salary`) VALUES (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?) 38:12.476 [main] DEBUG c.j.m.chat01.UserMapper.insertBatch - ==> Parameters: null, 路人甲Java-1(String), 31(Integer), 10000.0(Double), null, javacode2018-1(String), 31(Integer), 10000.0(Double), null, 路人甲Java-2(String), 32(Integer), 20000.0(Double), null, javacode2018-2(String), 32(Integer), 20000.0(Double), null, 路人甲Java-3(String), 33(Integer), 30000.0(Double), null, javacode2018-3(String), 33(Integer), 30000.0(Double), null, 路人甲Java-4(String), 34(Integer), 40000.0(Double), null, javacode2018-4(String), 34(Integer), 40000.0(Double), null, 路人甲Java-5(String), 35(Integer), 50000.0(Double), null, javacode2018-5(String), 35(Integer), 50000.0(Double) 38:12.484 [main] DEBUG c.j.m.chat01.UserMapper.insertBatch - <== Updates: 10 38:12.502 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Preparing: SELECT `id`, `name`, `age`, `salary` FROM `t_user` a 38:12.502 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Parameters: 38:12.521 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - <== Total: 12 38:12.521 [main] INFO c.j.mybatis.chat01.UserMapperTest - 结果:[UserModel(id=1, name=路人甲Java, age=1, salary=0.0), UserModel(id=2, name=路人, age=30, salary=50000.0), UserModel(id=3, name=路人甲Java-1, age=31, salary=10000.0), UserModel(id=4, name=javacode2018-1, age=31, salary=10000.0), UserModel(id=5, name=路人甲Java-2, age=32, salary=20000.0), UserModel(id=6, name=javacode2018-2, age=32, salary=20000.0), UserModel(id=7, name=路人甲Java-3, age=33, salary=30000.0), UserModel(id=8, name=javacode2018-3, age=33, salary=30000.0), UserModel(id=9, name=路人甲Java-4, age=34, salary=40000.0), UserModel(id=10, name=javacode2018-4, age=34, salary=40000.0), UserModel(id=11, name=路人甲Java-5, age=35, salary=50000.0), UserModel(id=12, name=javacode2018-5, age=35, salary=50000.0)] ``` 这次批量插入了10条用户记录,可以看到有这样的输出: ```java 40:40.727 [main] DEBUG c.j.m.chat01.UserMapper.insertBatch - <== Updates: 10 ``` 上面这个表示插入影响的行数,10表示插入了10行。 批量插入之后,又执行了全表查询,这次插入了10条,加上前面的2个单条插入,表中总计12条记录。 #### 用例:根据用户id删除数据 运行`UserMapperTest#delete`,输出如下: ```java 38:36.498 [main] DEBUG c.j.mybatis.chat01.UserMapper.delete - ==> Preparing: DELETE FROM `t_user` WHERE `id` = ? 38:36.551 [main] DEBUG c.j.mybatis.chat01.UserMapper.delete - ==> Parameters: 1(Integer) 38:36.560 [main] DEBUG c.j.mybatis.chat01.UserMapper.delete - <== Updates: 1 38:36.561 [main] INFO c.j.mybatis.chat01.UserMapperTest - 删除行数:1 ``` #### 用例:动态更新 运行`UserMapperTest#update`,输出如下: ```java 38:51.289 [main] DEBUG c.j.mybatis.chat01.UserMapper.update - ==> Preparing: UPDATE `t_user` SET `name` = ? WHERE `id` = ? 38:51.347 [main] DEBUG c.j.mybatis.chat01.UserMapper.update - ==> Parameters: ready(String), 2(Long) 38:51.355 [main] DEBUG c.j.mybatis.chat01.UserMapper.update - <== Updates: 1 38:51.356 [main] INFO c.j.mybatis.chat01.UserMapperTest - 更新行数:1 38:51.358 [main] INFO c.j.mybatis.chat01.UserMapperTest - --------------------- 38:51.359 [main] DEBUG c.j.mybatis.chat01.UserMapper.update - ==> Preparing: UPDATE `t_user` SET `name` = ?, `salary` = ? WHERE `id` = ? 38:51.360 [main] DEBUG c.j.mybatis.chat01.UserMapper.update - ==> Parameters: ready(String), 1000.88(Double), 3(Long) 38:51.363 [main] DEBUG c.j.mybatis.chat01.UserMapper.update - <== Updates: 1 38:51.364 [main] INFO c.j.mybatis.chat01.UserMapperTest - 更新行数:1 ``` `UserMapperTest#update`方法,大家也认真看一下,2个更新,调用都是`mapper.update`方法,传入的都是`UserModel`类型的参数,只是2个`UserModel`对象的字段值不一样,最后产生的2个`update`语句也是不一样的,这个`update`语句是mybatis动态组装的,mybatis可以根据`UserModel`中字段是否为NULL,来拼装sql,这个更新是不是很强大。 #### 用例:动态查询 ##### 查询所有数据 运行`UserMapperTest#getModelList1`,输出如下: ```java 39:10.552 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Preparing: SELECT `id`, `name`, `age`, `salary` FROM `t_user` a 39:10.611 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Parameters: 39:10.639 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - <== Total: 11 39:10.639 [main] INFO c.j.mybatis.chat01.UserMapperTest - 结果:[UserModel(id=2, name=ready, age=30, salary=50000.0), UserModel(id=3, name=ready, age=31, salary=1000.88), UserModel(id=4, name=javacode2018-1, age=31, salary=10000.0), UserModel(id=5, name=路人甲Java-2, age=32, salary=20000.0), UserModel(id=6, name=javacode2018-2, age=32, salary=20000.0), UserModel(id=7, name=路人甲Java-3, age=33, salary=30000.0), UserModel(id=8, name=javacode2018-3, age=33, salary=30000.0), UserModel(id=9, name=路人甲Java-4, age=34, salary=40000.0), UserModel(id=10, name=javacode2018-4, age=34, salary=40000.0), UserModel(id=11, name=路人甲Java-5, age=35, salary=50000.0), UserModel(id=12, name=javacode2018-5, age=35, salary=50000.0)] ``` 可以看到sql是没有查询条件的。 ##### 查询多个用户id对应的数据 运行`UserMapperTest#getModelListByIds`,输出如下: ```java 39:38.000 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Preparing: SELECT `id`, `name`, `age`, `salary` FROM `t_user` a WHERE a.`id` IN ( ? , ? , ? ) 39:38.064 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Parameters: 2(Integer), 3(Integer), 4(Integer) 39:38.096 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - <== Total: 3 39:38.097 [main] INFO c.j.mybatis.chat01.UserMapperTest - 结果:[UserModel(id=2, name=ready, age=30, salary=50000.0), UserModel(id=3, name=ready, age=31, salary=1000.88), UserModel(id=4, name=javacode2018-1, age=31, salary=10000.0)] ``` 上面这个按照id列表查询也是比较常用的,比如我们在电商中查询订单列表,还需要查询每个订单对应的商品,此时可以先查询订单列表,然后在通过订单列表拿到所有的商品id集合,然后通过商品id集合去通过上面的方式检索商品信息,只需要2次查询就可以查询出订单及商品的信息了。 ##### 多条件 & 指定返回的列 运行`UserMapperTest#getModelList2`,查询姓名中包含路人甲java以及薪资大于3万的用户id、姓名,输出如下: ```java 41:12.185 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Preparing: SELECT id , name FROM `t_user` a WHERE a.`name` like '%路人甲java%' AND a.`salary` >= ? 41:12.275 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Parameters: 30000.0(Double) 41:12.311 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - <== Total: 3 41:12.312 [main] INFO c.j.mybatis.chat01.UserMapperTest - 结果:[UserModel(id=7, name=路人甲Java-3, age=null, salary=null), UserModel(id=9, name=路人甲Java-4, age=null, salary=null), UserModel(id=11, name=路人甲Java-5, age=null, salary=null)] ``` 看一下上面`select`语句,select后面只有`id,name`2个字段,where后面有多个条件,这种查询也是比较常用的,有些表可能有几十个字段,可能我们只需要几个字段,就可以使用上面这种查询。 ##### 条件过滤 & 排序 & 分页查询数据 & 只返回用户id、salary 运行`UserMapperTest#getModelList3`,查询姓名中包含路人甲java以及薪资大于3万的用户id,按照薪资倒叙,每页5条取第1页,输出如下: ```java 44:00.719 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Preparing: SELECT id , salary FROM `t_user` a WHERE a.`name` like '%路人甲java%' AND a.`salary` >= ? order by salary desc LIMIT ?,? 44:00.775 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - ==> Parameters: 30000.0(Double), 0(Integer), 5(Integer) 44:00.805 [main] DEBUG c.j.m.chat01.UserMapper.getModelList - <== Total: 3 44:00.806 [main] INFO c.j.mybatis.chat01.UserMapperTest - 结果:[UserModel(id=11, name=null, age=null, salary=50000.0), UserModel(id=9, name=null, age=null, salary=40000.0), UserModel(id=7, name=null, age=null, salary=30000.0)] ``` 大家主要看一下输出的sql,如下: ```java SELECT id , salary FROM `t_user` a WHERE a.`name` like '%路人甲java%' AND a.`salary` >= ? order by salary desc LIMIT ?,? ``` 这个sql会根据查询条件,自动构建出我们需要的sql,这点上面是最厉害的。 #### 案例总结 上面列举的一些用例基本上包含了我们对db所需的大部分操作,动态sql处理方面体现的最为强劲,如果让我们自己写,我们需要写很多判断,而用mybatis这么简单就实现了,我们在java代码中没有看到一个判断拼接语句,而这些sql的判断拼接都在一个文件中:`user.xml`中,这个就是mybatis中核心的文件,我们需要写的sql及判断逻辑基本上都在这个xml中,大家可以认真去看一下这个xml文件。 ### mybatis开发项目的具体步骤 #### 项目中引入mybatis maven配置 ```xml <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> ``` 上面的`mybatis.version`版本,大家可以在maven社区中央仓库中去查找最新的,目前最新的是`3.5.3` #### 创建mybatis配置文件 mybatis配置文件为xml格式,可以放在`resource`目录下面,如上面案例中的`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="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"/> </properties> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/user.xml"/> </mappers> </configuration> ``` 这个文件主要是对mybatis进行全局配置,比如`数据源、事务`的配置,如上面的`datasource`元素用来配置数据源,数据源中就需要指定数据库的一些配置信息;还有其他更多的配置,此处先不做具体说明,后面我们慢慢来,整个系列完成之后,这些配置大家都会懂的。 #### 创建mapper xml文件 如上面案例中的`user.xml`,大家在打开看看,我们需要对`t_user`表所有操作sql就写在这个文件中,下一篇文章我们会详细介绍`mapper xml`文件的各种写法,`user.xml`文件是对`t_user`表的所有操作一般都会放在这个里面,mybatis如何使用到这个文件呢,我们需要在上面的`mybatis配置文件`中引入这个mapper文件,如案例中在`mybatis-config.xml`有下面这样的内容: ```xml <mappers> <mapper resource="mapper/user.xml"/> </mappers> ``` `mappers`元素中可以有多个`mapper`文件,我们开发的项目中可能有很多表需要操作,那么对应会有很多`mapper xml`文件,我们都需要在`mappers`元素中进行引入,然后mybatis才会使用到。 #### 创建Mapper接口 开发者如何去调用`user.xml`中的各种操作去执行sql呢,这时我们就需要一个`Mapper接口了`,`Mapper`接口会和`mapper xml`建立映射关系,当我们调用`Mapper`接口中的方法的时候,会间接的调用到`mapper xml`中的各种数据的sql操作,`Mapper`接口如何和`Mapper xml`文件关联的呢? 大家去看一下`user.xml`文件中有个这样的一个配置: ```xml <mapper namespace="com.javacode2018.mybatis.chat01.UserMapper"> ``` 注意上面的`namespace`的值,对应的是`UserMapper`这个接口完整的引用,通过这个namespace,`UserMapper`接口就可以`user.xml`建立了映射关系。 `user.xml`中又有很多db操作,这些操作会和`UserMapper`接口中的方法建立映射关系,当调用`UserMapper`中的方法的时候,间接的会调用到`user.xml`中对应的操作。 如`user.xml`中有下面一段配置: ```xml <!-- 批量插入 --> <insert id="insertBatch" parameterType="map"> <![CDATA[ INSERT INTO `t_user` (`id`, `name`, `age`, `salary`) VALUES ]]> <foreach collection="list" separator="," item="item"> (#{item.id}, #{item.name}, #{item.age}, #{item.salary}) </foreach> </insert> ``` 而`UserMapper`中有个`insertBatch`方法和上面这个`insert`批量插入对应,如下: ```java /** * 批量插入用户信息 * * @param userModelList */ void insertBatch(List<UserModel> userModelList); ``` 所以当我们调用`UserMapper`中的`insertBatch`方法的时候,会间接调用到`user.xml`中的 `id="insertBatch"`这个操作。 提示一下:接口和mapper xml映射起来间接调用,是通过java动态代理实现的,后面我们会详解如何实现的。 下面我们就可以使用mybatis来操作db了。 #### 通过mybatis获取Mapper接口执行对db的操作 上面我们说了,我们可以通过mapper接口来执行对db的操作,获取Mapper的主要代码如下: ```java SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml")); SqlSession sqlSession = sqlSessionFactory.openSession(true); UserMapper mapper = sqlSession.getMapper(UserMapper.class); ``` 上面代码中使用到了mybatis中的核心组件,我们具体来看一下。 #### Mybatis核心对象介绍 ##### SqlSessionFactoryBuilder 这个是一个构建器,通过名字大家也可以感觉到`SqlSessionFactoryBuilder`构建器,是用来构建`SqlSessionFactory`对象的,SqlSessionFactoryBuilder可以通过读取mybatis的配置文件,然后构建一个`SqlSessionFactory`对象,一个项目中有很多`mapper xml`文件,如果每次操作都去重新解析是非常慢的,那么怎么办? 能不能第一次解析好然后放在内存中,以后直接使用,SqlSessionFactoryBuilder就是搞这个事情的,将mybatis配置文件、`mapper xml`文件、`mapper xml`文件和`Mapper 接口`的映射关系,这些都先给解析好,然后放在java对象中,java对象存在于内存中,内存中访问会非常快的,那么我们每次去用的时候就不需要重新去解析xml了,SqlSessionFactoryBuilder解析配置之后,生成的对象就是`SqlSessionFactory`,这个是一个重量级的对象,创建他是比较耗时的,所以一般一个db我们会创建一个`SqlSessionFactory`对象,然后在系统运行过程中会一直存在,而SqlSessionFactoryBuilder用完了就可以释放了。 ##### SqlSessionFactory 通过名字可以知道,这个是一个工厂,是用来创建`SqlSession`的工厂,`SqlSessionFactory`是一个重量级的对象,一般一个db对应一个`SqlSessionFactory`对象,系统运行过程中会一直存在。 SqlSessionFactory是一个接口,这个接口有2个实现`DefaultSqlSessionFactory`和`SqlSessionManager`,一般都是通过`SqlSessionFactoryBuilder`来创建`SqlSessionFactory`对象。 通过`SqlSessionFactoryBuilder`来创建`SqlSessionFactory`对象主要有2种方式,一种通过读取mybatis配置文件的方式,另外一种是硬编码的方式,这个后面会专门抽一篇文件介绍这块,springboot中会使用到硬编码的方式,所以这块会详细介绍。 ##### SqlSession 我们通过jdbc操作数据库需要先获取一个`Connection`连接,然后拿着这个连接去对db进行操作,在mybatis中`SqlSession`就类似于jdbc中`Connection`连接对象,在mybatis中叫做`Sql会话对象`,一般我们一个db操作使用一个`SqlSession`对象,所以这个对象一般是方法级别的,方法结束之后,这个对象就销毁了,这个对象可以调用`sqlSessionFactory.openSession`的方法来进行获取。 我们可以直接通过SqlSession对象来调用`mapper xml`中各种db操作,需要指定具体的操作的id,id的格式为`namespace.操作的id`。 ##### Mapper接口 我们可以通过SqlSession直接调用`mapper xml`中的db操作,不过更简单的以及推荐的方式是使用Mapper接口,`Mapper接口中的方法和mapper xml文件中的各种db操作`建立了映射关系,是通过`Mapper接口完整名称+方法名称`和`mapper xml`中的`namespace+具体操作的id`来进行关联的,然后我们直接调用`Mapper接口`中的方法就可以间接的操作db了,使用想当方便,`Mapper接口需要通过SqlSession`获取,传入`Mapper接口对应的Class`对象,然后会返回这个接口的实例,如: ```java UserMapper mapper = sqlSession.getMapper(UserMapper.class); ``` ### 总结 本篇文章主要通过一个案例来感受一下mybatis可以干什么,以及他的强大之处,还需要大家掌握mybatis开发项目的具体步骤,后面的文章将对mybatis中具体的知识点做详细介绍,让大家成为mybatis高手。 ### 案例代码 ```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