Java Mybatis 学习笔记(2021.11.2~11.4)
MyBatis
- MyBatis
- 环境
- 回顾
- 学习方式
- 一、什么是Mybatis
- 二、第一个MyBatis程序
- 三、增删改查实现
- 四、错误排查
- 五、Map和模糊查询拓展
- 六、配置之属性优化
- 七、配置之别名优化(typeAliases)
- 八、配置值映射器说明
- 九、生命周期和作用域
- 十、ResultMap结果集映射
- 十一、日志工厂
- 十二、Log4j讲解
- 十三、Limit实现分页
- 十四、RowBounds分页
- 十五、使用注解开发
- 十六、Mybatis执行流程剖析
- 十七、注解增删改查
- 十八、Lombok的使用
- 十九、复杂查询环境搭建
- 二十、多对一的处理(association)
- 二十一、一对多的处理(collection)
- 二十二、动态SQL环境搭建
- 二十三、动态SQL之IF语句
- 二十四、动态SQL常用标签
- 二十五、动态SQL之Foreach
- 二十六、缓存简介
- 二十七、一级缓存
- 二十八、二级缓存
- 二十九、Mybatis缓存原理
- 三十、自定义缓存Ehcache
环境
- jdk1.8
- Mysql5.7
- maven3.8.3
- IDEA
回顾
- JDBC
- Mysql
- Java基础
- Maven
- Junit
学习方式
看官网
一、什么是Mybatis
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
获得:
GitHub - mybatis/mybatis-3: MyBatis SQL mapper framework for Java
Maven仓库:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
什么是持久层:
持久化就是将数据从瞬时状态(在内存中)到持久状态(在硬盘中:数据库、文件都可以)的转化
为什么要持久化:
- 一些信息很重要一定要持久化
- 内存贵
持久层是什么:
- 完成持久化工作的代码块
- 层界限明显
为什么需要Mybatis
- 方便
- 传统JDBC代码复杂。简化、框架、自动化
- 帮助程序员将数据存到数据库中
- 不用也可以。更容易上手。技术没有高低之分
- 优点
- 简单易学
- 灵活
- 解除sql与程序代码的耦合,sql和代码的分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql。
- 用的人多
二、第一个MyBatis程序
思路:搭建环境-->导入Mybatis-->编写代码-->测试
搭建数据库环境:
CREATE DATABASE `mybatis`;
USE `mybatis`;
CREATE TABLE `user`(
`id` INT NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`pwd` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user`(`id`,`name`,`pwd`)
VALUES(1,'xiaowei','123123'),(2,'xiaowei2','1231231'),(3,'xiaowei3','1231232')
第一个Mybatis文件
-
搭建IDEA项目
-
删除SRC文件夹(父工程)
-
加入依赖
<?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.xiaowei</groupId> <artifactId>mybatis-study</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>mybatis-01</module> </modules> <dependencies> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> </dependencies> </project> -
建立子模块
-
在resources文件夹下建立mybatis-config.xml文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> </configuration> -
编写mybatis工具类
package com.xiaowei.utils; 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; import java.io.InputStream; public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { InputStream inputStream = null; try { String resource = "mybatis-config.xml"; inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); }finally { if (inputStream!=null){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static SqlSession getSession(){ return sqlSessionFactory.openSession(); } } -
在mybatis-config.xml文件中设置结果集
<configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/xiaowei/dao/UserMapper.xml"/> </mappers> </configuration> -
加入实体类和UserMapper接口(这个UserMapper接口就是Dao,只是名字不一样)
-
加入映射xml:UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.xiaowei.dao.UserMapper"> <select id="getUserList" resultType="com.xiaowei.pojo.User"> select * from mybatis.user </select> <select id="getUserById" parameterType="int" resultType="com.xiaowei.pojo.User"> select * from mybatis.user where id = #{id} </select> <insert id="addUser" parameterType="com.xiaowei.pojo.User"> insert into mybatis.user (id ,name ,pwd) values (#{id},#{name},#{pwd}); </insert> <insert id="addUser1" parameterType="map"> insert into mybatis.user (id ,name,pwd) values (#{uid},#{uname},#{upwd}); </insert> <update id="updateUser" parameterType="com.xiaowei.pojo.User"> update mybatis.user set name=#{name}, pwd=#{pwd} where id=#{id} </update> <delete id="deleteUser" parameterType="int"> delete from mybatis.user where id = #{id}; </delete> </mapper> -
测试类:
@Test public void test(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); List<User> userList = mapper.getUserList(); for (User user : userList) { System.out.println(user.getName()); } //有第二种方式,详情请去官方文档 session.close(); //The error may exist in com.xiaowei.dao/UserMapper.xml }第一个可能的错误:
//org.apache.ibatis.binding.BindingException: Type interface com.xiaowei.dao.UserMapper is not known to the MapperRegistry. 没找到对应的Mapper映射,第三步中必须要设置结果集 <mappers> <mapper resource="com/xiaowei/dao/UserMapper.xml"/> </mappers> 记得不是. 是/分隔第二个可能的错误
//The error may exist in com.xiaowei.dao/UserMapper.xml 没找到这个资源,因为Maven项目中,如果xml文件不在resources文件夹下就不会复制资源,所以需要配置 在父子工程中都加入以下配置👇配置pom.xml:
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>
-
三、增删改查实现
mapper.xml中的
namespace一定要对应好类
select
查询:
- id就是对应的方法名
- resultType:sql语句的返回值
- parameterType:方法中参数的类型,想要传参的话,在sql语句中使用占位符#
Demo:
Java代码:
package com.xiaowei.dao;
import com.xiaowei.pojo.User;
import com.xiaowei.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserMapperTest {
@Test
public void test(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user.getName());
}
//org.apache.ibatis.binding.BindingException: Type interface com.xiaowei.dao.UserMapper is not known to the MapperRegistry.
session.close();
//The error may exist in com.xiaowei.dao/UserMapper.xml
}
@Test
public void addUserTest(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int xiaowei = mapper.addUser(new User(4, "xiaowei", "123123111"));
session.commit();
System.out.println("结果影响了:"+xiaowei+"行。");
}
@Test
public void deleteUserTest(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int xiaowei = mapper.deleteUser(4);
session.commit();
System.out.println("结果影响了:"+xiaowei+"行。");
}
@Test
public void selectUserTest(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User xiaowei = mapper.getUserById(2);
session.commit();
System.out.println(xiaowei.toString());
}
@Test
public void updateUserTest(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int i = mapper.updateUser(new User(1, "彭于晏", "pengyuyan"));
session.commit();
System.out.println("结果影响了:"+i+"行。");
}
}
在Mapper中:
package com.xiaowei.dao;
import com.xiaowei.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> getUserList();
User getUserById(int id);
int addUser(User user);
int updateUser(User user);
int deleteUser(int id);
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiaowei.dao.UserMapper">
<select id="getUserList" resultType="com.xiaowei.pojo.User">
select * from mybatis.user
</select>
<select id="getUserById" parameterType="int" resultType="com.xiaowei.pojo.User">
select * from mybatis.user where id = #{id}
</select>
<insert id="addUser" parameterType="com.xiaowei.pojo.User">
insert into mybatis.user (id ,name ,pwd)
values (#{id},#{name},#{pwd});
</insert>
<update id="updateUser" parameterType="com.xiaowei.pojo.User">
update mybatis.user set name=#{name}, pwd=#{pwd} where id=#{id}
</update>
<delete id="deleteUser" parameterType="int">
delete
from mybatis.user
where id = #{id};
</delete>
</mapper>
四、错误排查
【狂神说Java】Mybatis最新完整教程IDEA版通俗易懂_哔哩哔哩_bilibili
五、Map和模糊查询拓展
万能的Map(使用Map自定义传递参数)多个参数用Map或者注解
public void addUserTest1(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("uid",5);
map.put("uname","古天乐");
map.put("upwd","shixiongdikanwo");
int xiaowei = mapper.addUser1(map);
session.commit();
System.out.println("结果影响了:"+xiaowei+"行。");
}
UserMapper.xml中
<insert id="addUser1" parameterType="map">
insert into mybatis.user (id ,name,pwd)
values (#{uid},#{uname},#{upwd});
</insert>
UserMapper.java中
int addUser1(Map<String,Object> map);
防止模糊查询时的SQL注入攻击:
- 在Java代码执行的时候,写死通配符
- 在UserMapper中,直接将通配符写死
六、配置之属性优化
需要掌握的配置属性

环境配置
- mybatis可以配置多套环境,但是每次工厂只能使用一套环境
- 学会使用配置多套运行环境
- mybatis有两种事务管理器(JDBC| MANAGE),连接池:POOLED
属性properties
-
通过配置properties属性实现配置文件
-
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置
-
在xml中所有的标签都要有顺序
-

-
例如db.properties
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=false username=root password=123456 -
然后再mybatis-config.xml中配置
<configuration> <!--引入外部配置文件--> <properties resource="db.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="com/xiaowei/dao/UserMapper.xml"/> </mappers> </configuration> -
优先级:外部文件会覆盖内部文件
七、配置之别名优化(typeAliases)
- 类型别名可为 Java 类型设置一个缩写名字
- 它仅用于 XML 配置,意在降低冗余的全限定类名书写
在mybatis-config.xml中配置了别名后,所有的Mapper.xml文件都可以使用别名
方式一:
mybatis-config.xml
<typeAliases>
<typeAlias type="com.xiaowei.pojo.User" alias="user"/>
</typeAliases>
UserMapper.xml
<select id="getUserList" resultType="user">
select * from mybatis.user
</select>
方式二:会自动扫描包下的所有可以使用的JavaBean
在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如
domain.blog.Author的别名为author;若有注解,则别名为其注解值。见下面例子:@Alias("author") public class Author { ... }
mybatis-config.xml:
<typeAliases>
<package name="com.xiaowei.pojo"/>
</typeAliases>
UserMapper.xml
<select id="getUserList" resultType="user">
select * from mybatis.user
</select>
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。大写开头的类型的别名是直接小写即可,如果小写开头的类型在前面加短下划线就是别名
| 别名 | 映射的类型 |
|---|---|
| _byte | byte |
| _long | long |
| _short | short |
| _int | int |
| _integer | int |
| _double | double |
| _float | float |
| _boolean | boolean |
| string | String |
| byte | Byte |
| long | Long |
| short | Short |
| int | Integer |
| integer | Integer |
| double | Double |
| float | Float |
| boolean | Boolean |
| date | Date |
| decimal | BigDecimal |
| bigdecimal | BigDecimal |
| object | Object |
| map | Map |
| hashmap | HashMap |
| list | List |
| arraylist | ArrayList |
| collection | Collection |
| iterator | Iterator |
暂时能用的setting配置
| 设置名 | 描述 | 有效值 | 默认值 | 备注 |
|---|---|---|---|---|
| cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true | 是否开启缓存 |
| lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 |
true | false | false | 是否开启懒加载 |
| logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 | 指定实现类 |
八、配置值映射器说明
插件:(暂时不用掌握)
- Mybatis Generator core(常规生成器)
- MyBatis Plus(拓展包)
- 通用mapper
配置值映射器(Mapper.xml)
方式一:resource【推荐使用】
<mappers>
<mapper resource="com/xiaowei/dao/UserMapper.xml"/>
</mappers>
方式二:class
<mappers>
<mapper class="com.xiaowei.dao.UserMapper"/>
</mappers>
方式二注意点:
- 映射器(也就是mapper.xml)要和接口文件在同一个包下
- 要同名称
方式三:pakage
<mappers>
<package name="com.xiaowei.pojo"/>
</mappers>
注意点同方式二
九、生命周期和作用域
生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。

SqlSessionFactoryBuilder
- 一旦创建了 SqlSessionFactory,就不再需要它了
- 局部变量
SqlSessionFactory
- 说白了就是数据库连接池一样得东西
- 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
- 因此 SqlSessionFactory 的最佳作用域是应用作用域(全局)
- 最简单的就是使用单例模式或者静态单例模式
SqlSession
- SqlSession 的实例不是线程安全的,因此是不能被共享的
- 每个线程都应该有它自己的 SqlSession 实例
- 最佳的作用域是请求或方法作用域
- 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。
- 简单来说,因为他的线程不安全,用了才新建,结束要关闭

这里面的每一个Mapper就代表每一个业务
十、ResultMap结果集映射
解决属性名和字段名不一致的问题
数据库表:

实体类:
private int id;
private String name;
private String password;
可见:数据库的字段名和实体类属性名并不一样?如何解决?
方式一:暴力别名:
<select id="getUserList" resultType="user">
select id,name,pwd as password from mybatis.user
</select>
方式二:结果集映射:
<resultMap id="usermap" type="user">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="password" column="pwd"/>
</resultMap>
<select id="getUserList" resultMap="usermap" resultType="user">
select * from mybatis.user
</select>
十一、日志工厂
如果一个数据库操作出现了异常,我们需要排错:使用日志!
曾经:sout、debug
现在:日志工厂
| 设置名 | 描述 | 有效值 | 默认值 | 备注 |
|---|---|---|---|---|
| logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J LOG4J【掌握】 LOG4J2 JDK_LOGGING COMMONS_LOGGING STDOUT_LOGGING【掌握】 NO_LOGGING |
未设置 | 指定实现类 |
在配置文件中设置日志实现的方法
STDOUT_LOGGING标准日志输出
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
十二、Log4j讲解
什么是Log4j
- Log4j是Apache的一个开源项目。
- 通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等。
- 我们也可以控制每一条日志的输出格式。
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
怎么使用Log4j
-
导入包
<!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> -
设置日志实现类
<settings> <setting name="logImpl" value="LOG4J"/> </settings> -
配置log4j(在resource文件夹下新建log4j.properties)
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=【%c】-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/kuang.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=【%p】【%d{yy-MM-dd}】【%c】%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
简单实用log4j
-
在要使用log4j的类中导入包
-
日志对象加载参数为当前类的class
static Logger logger = Logger.getLogger(UserMapperTest.class); -
日志级别
logger.info("info:logger"); logger.debug("debug:logger"); logger.error("error:logger");
十三、Limit实现分页
减少数据的处理量
使用limit分页SQL
select * from `user`
limit #{startIndex}, #{pageSize};
十四、RowBounds分页
取出所有数据手动分页,不推荐
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.apache.ibatis.session;
public class RowBounds {
public static final int NO_ROW_OFFSET = 0;
public static final int NO_ROW_LIMIT = 2147483647;
public static final RowBounds DEFAULT = new RowBounds();
private final int offset;
private final int limit;
public RowBounds() {
this.offset = 0;
this.limit = 2147483647;
}
public RowBounds(int offset, int limit) {
this.offset = offset;
this.limit = limit;
}
public int getOffset() {
return this.offset;
}
public int getLimit() {
return this.limit;
}
}
分页插件:Mybatis pageHelper,了解,知道这是什么玩意就行
十五、使用注解开发
什么叫面向接口编程
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
1.关于接口的理解。
- 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
- 接口的本身反映了系统设计人员对系统的抽象理解。
- 接口应有两类:
- 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
- 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);
- 一个体有可能有多个抽象面。
- 抽象体与抽象面是有区别的。
2.设计接口的另一个不可忽视的因素是接口所处的环境(context,environment)
系统论的观点:环境是系统要素所处的空间与外部影响因素的总和。任何接口都是在一定的环境中产生的。因此环境的定义及环境的变化对接口的影响是不容忽视的,脱离原先的环境,所有的接口将失去原有的意义。
3.按照组件的开发模型(3C),它们三者相辅相成,各司一面,浑然一体,缺一不可。
- 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法
- 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现
- 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题
4.注解开发
-
不需要了Mapper.xml文件
-
直接在接口上使用注解例如
@Select("select * from mybatis.user") List<User> getUserList(); -
而在mybatis-config.xml文件中获得的映射是类映射
-
不推荐使用,因为遇到困难的语句时力不从心
5.原理
反射获取Mapper接口中的所有内容并且自动解析方法执行
十六、Mybatis执行流程剖析

十七、注解增删改查
在获得session时可以设置事务自动提交
return sqlSessionFactory.openSession(true);
//在有多个参数时,需要使用@Param进行注解
@Select("select * from mybatis.user where id=#{id} and pwd=#{pwd}")
User getUserByIdPassword(@Param("id")int id,@Param("pwd")String password);
@Insert("insert into mybatis.user values(#{id},#{name},#{pwd})")
int insertUser(@Param("id")int id,@Param("name")String name,@Param("pwd")String pwd);
@Delete("delete from mybatis.user where id=#{id}")
int deleteUser(@Param("id")int id);
@Update("update mybatis.user set name=#{name} where id=#{id}")
int updateUser(@Param("id")int id,@Param("name")String name,@Param("pwd")String password);
注意:Mapper.xml必须同名且在一个包下
关于@Param注解
- 基本类型及String需要加上
- 引用类型不需要加
- 只有一个基本类型可以不加(建议加)
- SQL中引用的就是这个注解中的变量名
#{}是预编译,防注入- ${}不是预编译
十八、Lombok的使用
- Lombok 项目是一个 java 库
- 可自动插入编辑器并构建工具
- 切勿再编写其他获取器或等于方法
- 用一个注释,您的类有一个功能齐全的建设者,自动记录变量
使用:
- 在IDEAsetting中进入plugin选项卡
- 搜索Lombok安装
- 导入jar包
- 加注解
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data:自动生成无参构造、getset、tostring、hashcode、equels
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
常用:
@AllArgsConstructor
@NoArgsConstructor
@Data
十九、复杂查询环境搭建
数据库
CREATE TABLE `teacher`(
`id` INT NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(`id`,`name`)VALUES (1,'秦老师');
CREATE TABLE `student`(
`id` INT NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT DEFAULT NULL,
PRIMARY KEY(`id`),
KEY `fktid`(`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
DROP TABLE student
INSERT INTO `student`(`id`,`name`,`tid`) VALUES(1,'小明',1);
INSERT INTO `student`(`id`,`name`,`tid`) VALUES(2,'小红',1);
INSERT INTO `student`(`id`,`name`,`tid`) VALUES(3,'小张',1);
INSERT INTO `student`(`id`,`name`,`tid`) VALUES(4,'小李',1);
INSERT INTO `student`(`id`,`name`,`tid`) VALUES(5,'小王',1);
新建模块
导入资源:db.properties、mybatis-config.xml
新建实体类Teacher、Student
记得学生实体类应该有教师的字段
写好接口TeacherMapper、StudentMapper
写好映射文件TeacherMapper.xml、StudentMapper.xml
二十、多对一的处理(association)
方式一:Mapper.xml中,子查询
<select id="getAllStudent" resultMap="allStudentOfTeacher">
select *
from mybatis.student;
</select>
<resultMap id="allStudentOfTeacher" type="Student">
<result column="id" property="id"/>
<result column="name" property="name"/>
<association column="tid" property="teacher" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select *
from mybatis.teacher
where id=#{tid};
</select>
方式二:联表查询
<select id="getAllStudent2" resultMap="allStudentOfTeacher2">
select s.id sid, s.name sname,t.name tname from student s ,teacher t
where s.tid=t.id
</select>
<resultMap id="allStudentOfTeacher2" type="Student">
<result column="sid" property="id"/>
<result column="sname" property="name"/>
<association property="teacher" javaType="Teacher">
<result column="tname" property="name"/>
</association>
</resultMap>
二十一、一对多的处理(collection)
实体类
老师
public class Teacher {
private int id;
private String name;
private List<Student> students;
}
学生
public class Student {
private int id;
private String name;
private int tid;
}
映射文件:
方式一:联表查询
<select id="getTeacherOfStudents" resultMap="TeacherOfStudents">
select t.id tid, t.name tname, s.name sname from student s ,teacher t
where s.tid=t.id and t.id=#{id}
</select>
<resultMap id="TeacherOfStudents" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="name" column="sname"/>
</collection>
</resultMap>
方式二:子查询
<select id="getAllTeacher" resultMap="TeacherOfStudents2">
select *
from mybatis.teacher
where id = #{id};
</select>
<resultMap id="TeacherOfStudents2" type="Teacher">
<collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudents"/>
</resultMap>
<select id="getStudents" resultType="Student">
select *
from mybatis.student
where tid = #{id};
</select>
- collection进行一对多
- association进行多对一
- JavaType指类型,ofType指类型中的泛型
慢SQL:不要!
二十二、动态SQL环境搭建
什么是动态SQL,根据不同的条件生成不同的SQL
你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
if
choose (when, otherwise)
trim (where, set)
foreach
环境
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8;
添加数据(Mybatis部分不再重复)
@Test
public void addBlogTest(){
SqlSession sqlSession = MybatisUtils.getSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IDUtils.getID());
blog.setTitle("Mybatis");
blog.setAuthor("狂神说");
blog.setCreateTime(new Date());
blog.setViews(9999);
mapper.addBlog(blog);
blog.setId(IDUtils.getID());
blog.setTitle("Java");
mapper.addBlog(blog);
blog.setId(IDUtils.getID());
blog.setTitle("Spring");
mapper.addBlog(blog);
blog.setId(IDUtils.getID());
blog.setTitle("微服务");
mapper.addBlog(blog);
sqlSession.close();
}
二十三、动态SQL之IF语句
mapper.xml:
<select id="selectBlogIF" parameterType="map" resultType="Blog">
select * from blog where 1=1
<if test="title!=null">
and title=#{title}
</if>
<if test="author!=null">
and author=#{titauthorle}
</if>
</select>
test.class:
public void testIF(){
SqlSession session = MybatisUtils.getSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, Object> map = new HashMap<>();
//map.put("title","Mybatis");
List<Blog> blogs = mapper.selectBlogIF(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
}
二十四、动态SQL常用标签
choose when otherwise:只选一个,哪个先符合就选哪个
<select id="selectBlogCHOOSE" parameterType="map" resultType="Blog">
select * from blog
<where>
<choose>
<when test="title!=null">
title=#{title}
</when>
<when test="author!=null">
and author=#{author}
</when>
<otherwise>
and views=#{views}
</otherwise>
</choose>
</where>
</select>
where:自动处理and问题
<select id="selectBlogWHERE" parameterType="map" resultType="Blog">
select * from blog
<where>
<if test="title!=null">
title=#{title}
</if>
<if test="author!=null">
and author=#{titauthorle}
</if>
</where>
</select>
set:自动把无关的逗号去除,前置set关键字
<update id="updateBlogSET" parameterType="map">
update mybatis.blog
<set>
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
author=#{titauthorle}
</if>
</set>
where id = #{id};
</update>
trim:定制标签
在sql层面执行逻辑代码,本质还是sql
二十五、动态SQL之Foreach
SQL片段:有些SQL复用程度高我们就弄成SQL片段
定义:
<sql id="set-if-title-author">
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
author=#{titauthorle}
</if>
</sql>
引用:
<update id="updateBlogSET" parameterType="map">
update mybatis.blog
<set>
<include refid="set-if-title-author"/>
</set>
where id = #{id};
</update>
注意:不要在sql片段中引用where和set标签
foreach:动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)例如:
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item1" index="index" collection="list"
open="(" separator="," close=")">
#{item1}
</foreach>
</select>
字段解释
item:遍历的变量,在下面的sql中引用
index:集合中元素的下标
collection:要遍历的集合,由Java层面传入
open:遍历前的开头字符
separator:元素间分隔符
close:遍历后的结束字符
最后的效果就是
SELECT *
FROM POST P
WHERE ID in(listItem1,listItem2,listItem2,)
完整例子:
测试类
public void testIF(){
SqlSession session = MybatisUtils.getSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, Object> map = new HashMap<>();
//map.put("title","111");
//map.put("author",9999);
ArrayList<String> ids = new ArrayList<String>();
ids.add("f0ab15c8d29b4b8d812df0052e55e489");
ids.add("963ff39f66ff4d9c845983cdf75f37eb");
ids.add("d494d68005214b5c88f69ec8c73beca9");
map.put("ids",ids);
List<Blog> blogs = mapper.selectBlogFOR(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
}
xml:
<select id="selectBlogFOR" parameterType="map" resultType="Blog">
select * from blog
<where>
<foreach collection="ids" index="index" item="id"
open="id in(" separator="," close=")">
#{id}
</foreach>
</where>
</select>
二十六、缓存简介
连接数据库---->需要资源
将资源保存到内存中---->缓存
下次直接走缓存,快!
- 什么是缓存 [ Cache ]?
- 存在内存中的临时数据。
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库
- 数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
- 为什么使用缓存?
减少和数据库的交互次数,减少系统开销,提高系统效率。 - 什么样的数据能使用缓存?
经常查询并且不经常改变的数据。【使用缓存】
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
二十七、一级缓存
一级缓存是本地缓存,SqlSession中,在一次会话内存在(默认使用)
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
手动清理缓存
sqlSession.clearcache();
二十八、二级缓存
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;
- 但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;
使用二级缓存
-
配置mybatis-config.xml
<setting name="cacheEnabled" value="true"/> -
配置SQL映射文件
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
一个session结束后,才提交自己的缓存,才能让别人查到
二十九、Mybatis缓存原理

三十、自定义缓存Ehcache
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点

浙公网安备 33010602011771号