Mybatis详解
MyBatis中文手册
Maven仓库
Maven Repository: Search/Browse/Explore
为什么需要Mybatis
MyBatis工作示意图
传统方式(非MyBatis)




一、快速入门

1.Maven父子项目配置
父项目以多个子模块/子项目管理工程
父项目会管理多个子模块/子项目,将来父项目中的引入的依赖可以直接给子项目用
新建Maven项目后,将src文件夹删除

引入MyBatis依赖
-
<dependencies>
-
<dependency>
-
<groupId>mysql</groupId>
-
<artifactId>mysql-connector-java</artifactId>
-
<version>8.0.23</version>
-
</dependency>
-
<dependency>
-
<groupId>junit</groupId>
-
<artifactId>junit</artifactId>
-
<version>4.13.1</version>
-
<!--<scope>test</scope>-->
-
</dependency>
-
<dependency>
-
<groupId>org.mybatis</groupId>
-
<artifactId>mybatis</artifactId>
-
<version>3.5.14</version>
-
</dependency>
-
</dependencies>

scope标签
如果有一个scope-test表示该jar 的作用范围在test目录

创建子项目


子项目的pom.xml文件

artifactId子项目的名称
这里配置后,该模块可以使用/引用父项目的依赖
2.配置MyBatis-Config
作用:配置数据库连接/数据源
管理XXXMapper.xml
位置一定要放在resources文件中
名称不一定是MyBatis-Config,可以自己自定义

具体配置信息查看文档

配置外部配置文件db.properties

db.properties
-
driver=com.mysql.cj.jdbc.Driver
-
url=jdbc:mysql://localhost:3306/mybatis2025?userSSL=true&useUnicode=true&characterEncoding=utf8
-
username=root
-
password=123456
MyBatis-Config文件内容
-
-
-
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
-
"http://mybatis.org/dtd/mybatis-3-config.dtd">
-
<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="org/mybatis/example/BlogMapper.xml"/>
-
</mappers>
-
</configuration>


3.创建一个Monster对象 - 和数据库中的monster表对应

并参考数据库的中的字段定义monster对象
-
CREATE TABLE `monster` (
-
`id` INT NOT NULL AUTO_INCREMENT,
-
`age` INT NOT NULL,
-
`birthday` DATE DEFAULT NULL,
-
`email` VARCHAR(255) NOT NULL ,
-
`gender` TINYINT NOT NULL,
-
`name` VARCHAR(255) NOT NULL,
-
`salary` DOUBLE NOT NULL,
-
PRIMARY KEY(`id`)
-
) CHARSET=utf8;
-
public class Monster {
-
private Integer id;
-
private String age;
-
private Date birthday;
-
private String email;
-
private String gender;
-
private String name;
-
private String salary;
-
.......
-
}

4.创建一个MonsterMapper接口 - 该接口用于声明操作monster表的方法
-
package com.goodup.mapper;
-
-
import com.goodup.entity.Monster;
-
-
public interface MonsterMapper {
-
/**
-
* 添加
-
* @param monster
-
*/
-
public void addMonster(Monster monster);
-
}
创建MonsterMapper.xml
里面的配置参考文档中的模版

-
-
-
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
-
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-
<mapper namespace="org.mybatis.example.BlogMapper">
-
<select id="selectBlog" resultType="Blog">
-
select * from Blog where id = #{id}
-
</select>
-
</mapper>

- 是用于实现对应接口方法的文件。
namespace用于指定该 XML 文件与哪个接口相对应。id="addMonster"对应接口的方法名。parameterType="com.hspedu.entity.Monster"表示传入的形参类型为com.hspedu.entity包下的Monster类,且该类名可简写。- 编写 SQL 语句时,建议先在
SQL工具中完成并测试通过后再使用。 - 添加语句 - 建议表名和字段名带上反引号
- #{age},#{birthday},#{email},#{gender},#{name},#{salary}是从Monster对象传入的
-
-
-
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
-
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-
<mapper namespace="com.goodup.mapper.MonsterMapper">
-
<insert id="addMonster" parameterType="com.goodup.entity.Monster">
-
insert into `monster`(`age`, `birthday` , `email`,`gender`, `name`, `salary`) values (#{age},#{birthday},#{email},#{gender},#{name},#{salary});
-
</insert>
-
</mapper>
5.在MyBatis-Config中配置关联Mapper.xml

如果快速复制全路径



6.创建一个工具类MyBatisUtils,快速得到sqlSession


Resources的选择

编写静态代码块-初始化sqlSessionFactory
-
private static SqlSessionFactory sqlSessionFactory;
-
static {
-
try {
-
//获取到配置文件mybatis-config.xml对应的inputStream
-
//加载文件时,默认resources目录==》运行后的工作目录
-
InputStream resourceAsStream = Resources.getResourceAsStream("mybaties-config.xml");
-
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
-
System.out.println("sqlSessionFactory:"+sqlSessionFactory);
-
} catch (IOException e) {
-
throw new RuntimeException(e);
-
}
-
}
编写方法,返回sqlSession对象-会话
-
public static SqlSession getSqlSession(){
-
return sqlSessionFactory.openSession();
-
}
7.获取到Mapper代理对象
-
package com.goodup.mapper;
-
-
import com.goodup.entity.Monster;
-
import com.goodup.util.MyBatisUtil;
-
import org.apache.ibatis.session.SqlSession;
-
import org.junit.Before;
-
import org.junit.Test;
-
-
import java.util.Date;
-
-
public class MonsterMapperTest {
-
private MonsterMapper monsterMapper;
-
private SqlSession sqlSession;
-
-
-
public void init(){
-
sqlSession = MyBatisUtil.getSqlSession();
-
monsterMapper = sqlSession.getMapper(MonsterMapper.class);
-
}
-
-
public void testAddMonster(){
-
Monster monster = new Monster();
-
monster.setAge("18");
-
monster.setBirthday(new Date());
-
monster.setEmail("123@qq.com");
-
monster.setGender("男");
-
monster.setName("张三");
-
monster.setSalary("10000");
-
monsterMapper.addMonster(monster);
-
sqlSession.commit();
-
}
-
}
运行将发生错误

原因是target中的MonsterMapper.xml文件不存在

解决方案
- 方案一:在父工程的pom.xml加入build配置
-
<build>
-
<resources>
-
<resource>
-
<directory>
-
src/main/java
-
</directory>
-
<includes>
-
<include>**/*.xml</include>
-
</includes>
-
</resource>
-
<resource>
-
<directory>src/main/resources</directory>
-
<includes>
-
<include>**/*.xml</include>
-
<include>**/*.properties</include>
-
</includes>
-
</resource>
-
</resources>
-
</build>
- 方案二:将com.goodup.mapper.MonsterMapper.xml移动到resources中

@Before注解的含义
表示在执行目标测试方法前,会先执行该方法
如果增删改,需要提交事务

8.返回自增长id
参考文档中的两个属性



运行结果

9.删除
java.lang.Integer是java类型,可以简写成Integer
-
<delete id="deleteMonster" parameterType="java.lang.Integer">
-
delete from `monster` where `id` = #{id};
-
</delete>

测试
-
-
public void testDeleteMonster(){
-
monsterMapper.deleteMonster(1);
-
if(sqlSession != null){
-
sqlSession.commit();
-
sqlSession.close();
-
}
-
}


10.修改
在MonsterMapper接口中添加方法

在MonsterMapper.xml中添加update标签
-
<update id="updateMonster" parameterType="com.goodup.entity.Monster">
-
update `monster` set `age` = #{age},
-
`birthday` = #{birthday},
-
`email` = #{email},
-
`gender` = #{gender},
-
`name` = #{name},
-
`salary` = #{salary}
-
where `id` = #{id};
-
</update>



11.类型别名-缩写名字
参考官方文档

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>


12.查询


查询单个-根据id查询




查询所有-集合
在MonsterMapper接口中添加一个方法

在MonsterMapper.xml做出修改



测试

二、日志输出配置-查看执行的sql语句

文档跳转


日志信息


三、原生的Api和注解的方式
1.原生api的调用

-
-
public void testApi(){
-
Monster monster = new Monster();
-
monster.setAge("182");
-
monster.setBirthday(new Date());
-
monster.setEmail("123213@qq.com");
-
monster.setGender("1");
-
monster.setName("张三231");
-
monster.setSalary("102330");
-
int insert = sqlSession.insert("com.goodup.mapper.MonsterMapper.addMonster", monster);
-
System.out.println("insert:" + insert);
-
if(sqlSession != null){
-
sqlSession.commit();
-
sqlSession.close();
-
}
-
}

2.注解的方式操作-不用在xml文件中配置sql语句

-
package com.goodup.mapper;
-
-
import com.goodup.entity.Monster;
-
import org.apache.ibatis.annotations.Delete;
-
import org.apache.ibatis.annotations.Insert;
-
import org.apache.ibatis.annotations.Select;
-
import org.apache.ibatis.annotations.Update;
-
-
public interface MonsterAnnotation {
-
-
public Monster getMonster(int id);
-
-
-
-
-
public void insertMonster(Monster monster);
-
-
-
-
-
-
public void updateMonster(Monster monster);
-
-
-
-
public void deleteMonster(int id);
-
}
修改Mybatis-config.xml,对MonsterAnnotation进行注册

注意事项

如果在Config中不配置将会发生以下错误

@option


四、Mybatis-config.xml配置文件

官方文档
1.properties属性


数据库配置信息db.properties
-
driver=com.mysql.cj.jdbc.Driver
-
url=jdbc:mysql://localhost:3306/mybatis2025?userSSL=true&useUnicode=true&characterEncoding=utf8
-
username=root
-
password=123456
在mybatis-config中引入外部db.properties
<properties resource="db.properties"/>
-
<dataSource type="POOLED">
-
<property name="driver" value="${driver}"/>
-
<property name="url" value="${url}"/>
-
<property name="username" value="${username}"/>
-
<property name="password" value="${password}"/>
-
</dataSource>
如果运行后db.properties无法抵达target目录

需要在pom.xml中进行配置

-
<resource>
-
<directory>src/main/resources</directory>
-
<includes>
-
<include>**/*.xml</include>
-
<include>**/*.properties</include>
-
</includes>
-
</resource>
2.settings全局参数定义

3.typeAliases别名处理器
- 别名作用:为 Java 类型命名简短名称,仅和 XML 配置相关,能减少类名重复部分。
- 对 XML 文件的影响:若指定别名,
Mapper相关的 XML 文件可进行简化处理。 - 使用规则:指定别名后,仍能使用类的全名。

配置一个包
-
<typeAliases>
-
<package name="com.goodup.entity"/>
-
</typeAliases>
4.typeHandlers类型处理器
- 功能:用于 Java 类型和 JDBC 类型之间的映射。
- 默认情况:MyBatis 自身的映射基本能满足需求,一般无需重新定义,使用默认的即可,MyBatis 会自动进行 Java 和 JDBC 类型的转换。
- 参考资料:Java 类型和 JDBC 类型的映射关系可查阅 MyBatis 手册,地址为配置_MyBatis中文网
- 工作场景:MyBatis 在设置预处理语句(PreparedStatement)中的参数,或者从结果集中取出值时,都会借助类型处理器来处理相关类型。
5.environments环境
- resource 方式:用于注册
XXXMapper.xml文件,是常用且已使用过的方式,通过在<mappers>标签下配置<mapper resource="..."/>来引入 Mapper.xml 文件。 - class 方式:适用于接口注解实现的情况。若采用注解方式,可不再使用 Mapper.xml 文件,但需要在
mybatis - config.xml中注册含注解的类,形式为<mapper class="..."/>,也已使用过。 - url 方式:通过外部路径注册,使用很少且不推荐,格式为
<mapper url="..."/>。 - package 方式:直接配置
<package name="包名"/>来注册指定包下的 Mapper,还需进行测试。
五、xml映射器



1.parameterType输入参数类型
传入pojo类型
以根据id或者name查询Monster为例
MonsterMapper.java
-
//根据id或者名字查询
-
public List<Monster> findByIdOrName(Monster monster);
MonsterMapper.xml
-
<select id="findByIdOrName" parameterType="com.goodup.entity.Monster" resultType="com.goodup.entity.Monster">
-
select * from monster where id=#{id} or name=#{name}
-
</select>
传入简单类型
查询name中包含''牛魔王"的妖怪
MonsterMapper中
-
//根据名字查询
-
public List<Monster> queryMonsterByName(String name);
MonsterMapper.xml文件中

2.传入HashMap-灵活增加查询属性
查询id>10并且salary大于40的Monster,传入参数是HashMap
MonsterMapper中
public List<Monster> queryMonsterByIdANdSalary_PrameterHashMap(Map<String,Object> map);
MonsterMapper.xml文件中
-
<select id="queryMonsterByIdANdSalary_PrameterHashMap" parameterType="java.util.Map" resultType="com.goodup.entity.Monster">
-
select * from monster where id>#{id} and salary>#{salary}
-
</select>
测试
-
-
public void queryMonsterByIdANdSalary_PrameterHashMap(){
-
Map<String,Object> map = new HashMap<>();
-
map.put("id",10);
-
map.put("salary",40);
-
List<Monster> monsters = monsterMapper.queryMonsterByIdANdSalary_PrameterHashMap(map);
-
for (Monster monster : monsters) {
-
System.out.println(monster);
-
}
-
if(sqlSession!=null){
-
sqlSession.close();
-
}
-
}

3.map返回类型
MonsterMapper中
public List<Map<String,Object>> queryMonsterByIdReturnMap(Map<String,Object> map);
MonsterMapper.xml文件中
-
<select id="queryMonsterByIdReturnMap" parameterType="java.util.Map" resultType="java.util.Map">
-
select * from monster where id>#{id} and salary>#{salary}
-
</select>
测试
-
-
public void queryMonsterByIdReturnMap() {
-
Map<String, Object> map = new HashMap<>();
-
map.put("id", 10);
-
map.put("salary", 40);
-
List<Map<String, Object>> mapList = monsterMapper.queryMonsterByIdReturnMap(map);
-
for (Map<String, Object> map1 : mapList) {
-
for (String s : map1.keySet()) {
-
System.out.println(s + ":" + map1.get(s));
-
}
-
System.out.println("----------------");
-
}
-
if (sqlSession != null) {
-
sqlSession.close();
-
}
-
}
Monster对象将以键值对的形式返回

4.resultMap(结果集映射)

当实体类的属性和表的字段名不一致时,我们可以通过resultMap进行映射,从而屏蔽实体类属性名和表的字段名的不同
-
create table `user`(
-
`user_id` int not null auto_increment,
-
`user_email` varchar(255) default '',
-
`user_name` varchar(255) default '',
-
primary key (`user_id`)
-
)charset=utf8;
创建实体类user(该实体类的属性和表的字段名不一致)



UserMapper接口
-
package com.goodup.mapper;
-
-
import com.goodup.entity.User;
-
-
import java.util.List;
-
-
public interface UserMapper {
-
public List<User> queryUser();
-
}
UserMapper.xml
-
-
-
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
-
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-
-
<mapper namespace="com.goodup.mapper.UserMapper">
-
<select id="queryUser" resultType="user" >
-
select * from user
-
</select>
-
</mapper>
测试-不使用resultmap
-
-
public void testQueryUser() {
-
List<User> users = userMapper.queryUser();
-
for (User user : users) {
-
System.out.println(user);
-
}
-
if (sqlSession != null) {
-
sqlSession.close();
-
}
-
}
如果字段名不同依旧按照原来resultType返回会出现以下结果

使用resultMap
property为实体类属性,colum为表字段名,user为返回的对象类型
-
<mapper namespace="com.goodup.mapper.UserMapper">
-
<resultMap id="userResultMap" type="User">
-
<result property="useremail" column="user_email"/>
-
<result property="username" column="user_name"/>
-
</resultMap>
-
<select id="queryUser" resultMap="userResultMap" >
-
select * from user
-
</select>
-
</mapper>
运行结果

注意事项和细节
- 方式一(通用,复用性欠佳):在 SQL 语句中,通过
AS关键字给表字段起别名,使别名与对象属性名一致。例如在查询用户信息时,将user_name别名为username,user_email别名为useremail,对应 SQL 语句为SELECT user_id,user_name AS username, user_email AS useremail FROM user。 - 方式二(MyBatis - Plus 专属,更简便):
- 若实体字段名和表字段名不一致,使用
@TableField注解来解决。 - 若实体类名和表名不一致,使用
@TableName注解来解决。
- 若实体字段名和表字段名不一致,使用
5.动态sql




if标签
查找大于指定年龄的Monster,如果输入age不大于0,则输入所有Monster
MonsterMapper.xml文件
-
<select id="getMonsterListHigherAge" resultType="com.goodup.entity.Monster" parameterType="Integer">
-
select * from monster where 1 = 1
-
<if test="age != null and age > 0">
-
and age > #{age}
-
</if>
-
</select>
MonsterMapper接口
public List<Monster> getMonsterListHigherAge(Integer age);
where标签
需求:
查询id 大于20 的,并且名字是“何杰”的所有妖怪
如果名字为空,或者输入的id小于0,则不拼接sql语句
MonsterMapper.xml文件
-
<select id="getMonsterListByIdAndName" resultType="com.goodup.entity.Monster" parameterType="com.goodup.entity.Monster">
-
select * from monster
-
<where>
-
<if test="id >= 0">
-
and id > #{id}
-
</if>
-
<if test="name != null and name != ''">
-
and name = #{name}
-
</if>
-
</where>
-
</select>
MonsterMapper接口
public List<Monster> getMonsterListByIdAndName(Monster monster);
如果入参是对象,test 表达式中,直接使用对象的属性名即可

where标签,会在组织动态sql时,加上where
mybatis底层where标签自动去掉多余的and


choose标签-类似Java中的Switch语句
需求:
如果给的name不为空,就按名字查询monster
如果指定的id>0,就按id查询monster
如果前面两个条件都不满足,就默认查询salary>30000

MonsterMapper.xml文件
-
<select id="getMonsterListByIdAndName_choose" resultType="com.goodup.entity.Monster" parameterType="Map">
-
select * from monster
-
<where>
-
<choose>
-
<when test="name != null and name != ''">
-
and name = #{name}
-
</when>
-
<when test="id > 0">
-
and id = #{id}
-
</when>
-
<otherwise>
-
and salary > 30000
-
</otherwise>
-
</choose>
-
</where>
-
</select>
MonsterMapper接口
public List<Monster> getMonsterListByIdAndName_choose(Map<String,Object> map);
foreach标签

查询id为33,55,77的妖怪

MonsterMapper.xml文件
-
<select id="getMonsterListById_foreach" resultType="com.goodup.entity.Monster" parameterType="Map">
-
select * from monster
-
<where> id in
-
<foreach collection="ids" item="id" open="(" close=")" separator=",">
-
#{id}
-
</foreach>
-
</where>
-
</select>
MonsterMapper接口
public List<Monster> getMonsterListById_foreach(Map<String,Object> map);
测试
-
public void getMonsterListById_foreach(){
-
Map<String,Object> map = new HashMap<>();
-
List<Integer> ids = new ArrayList<>();
-
ids.add(33);
-
ids.add(55);
-
ids.add(77);
-
map.put("ids",ids);
-
List<Monster> monsterListById_foreach = monsterMapper.getMonsterListById_foreach(map);
-
for (Monster monster1 : monsterListById_foreach) {
-
System.out.println(monster1);
-
}
-
if(sqlSession != null){
-
sqlSession.close();
-
}
-
}
运行结果

trim标签
可以替换一些关键字-可以做到where标签的功能,比where标签更强大

where等价trim的功能按名字查询妖怪,如果sql语句开头有and | or 就替换成where
-
<trim prefix="WHERE" prefixOverrides="AND |OR ">
-
...
-
</trim>
需求如果开头有goodup或者and或者or,改成where

MonsterMapper.xml文件
-
<select id="getMonsterListByIdAndName_trim" resultType="com.goodup.entity.Monster" parameterType="Map">
-
select * from monster
-
<trim prefix="where" prefixOverrides="and |or |goodup ">
-
<if test=" id != null and id >= 0">
-
goodup id > #{id}
-
</if>
-
<if test="name != null and name != ''">
-
and name = #{name}
-
</if>
-
</trim>
-
</select>
MonsterMapper接口
public List<Monster> getMonsterListByIdAndName_trim(Map<String,Object> map);
测试
-
-
public void getMonsterListByIdAndName_trim(){
-
Map<String,Object> map = new HashMap<>();
-
map.put("id",20);
-
map.put("name","");
-
List<Monster> monsterListByIdAndName_trim = monsterMapper.getMonsterListByIdAndName_trim(map);
-
for (Monster monster1 : monsterListByIdAndName_trim) {
-
System.out.println(monster1);
-
}
-
if(sqlSession != null){
-
sqlSession.close();
-
}
-
}

set标签-重点
与set标签等价的trim标签
-
<trim prefix="SET" suffixOverrides=",">
-
...
-
</trim>
请对指定id的妖怪进行修改,如果没有设置新的属性,则保持原来的值
MonsterMapper.xml文件
-
<update id="updateMonster" parameterType="com.goodup.entity.Monster" >
-
update monster
-
<set>
-
<if test="name != null and name != ''">
-
name = #{name},
-
</if>
-
<if test="age != null and age > 0">
-
age = #{age},
-
</if>
-
<if test="salary != null and salary > 0">
-
salary = #{salary},
-
</if>
-
<if test="email != null and email != ''">
-
email = #{email},
-
</if>
-
<if test="gender != null and gender != ''">
-
gender = #{gender},
-
</if>
-
<if test="birthday != null and birthday != ''">
-
birthday = #{birthday},
-
</if>
-
</set>
-
where id = #{id}
-
</update>
MonsterMapper接口
public void updateMonster(Monster monster);

什么时候用@Param
在 MyBatis 中,是否需要使用 @Param 注解,取决于你的参数传递情况以及 MyBatis 的版本:
1. 单参数(无 @Param 也能工作的场景)
如果 Mapper 方法只有一个参数,且参数类型是基本类型(如 int、Integer)或简单对象(如 String),MyBatis 会自动将参数名识别为 param1(或根据参数类型推断),此时不需要 @Param。
在你提供的代码中:
java
public List<Monster> getMonsterListHigherAge(Integer age);
方法只有一个参数 Integer age,因此 MyBatis 能自动识别这个参数,不需要额外用 @Param 显式指定参数名。
2. 多参数(必须用 @Param 的场景)
如果 Mapper 方法有多个参数,MyBatis 无法自动识别每个参数的含义,此时必须用 @Param 为每个参数指定 “逻辑名”,才能在 XML 中通过 ${paramName} 或 #{paramName} 引用参数。
例如:
java
-
// 多参数必须用 @Param
-
List<Monster> findByAgeAndName( Integer age, String name);
总结
你的代码中是单参数,MyBatis 能自动识别,因此不需要 @Param。只有多参数时,才必须用 @Param 显式指定参数名。
六、映射关系
官方文档
1.一对一
例子-Person(人) --- IDCard(身份证)
创建Person表和idcard表
-
-- 创建mybatis_idencard表
-
create table idencard
-
(
-
id int primary key auto_increment,
-
card_sn varchar(32) not null default ''
-
)charset utf8;
-
-
-- 创建person表
-
create table person
-
(
-
id int primary key auto_increment,
-
name varchar(32) not null default '',
-
card_id int,
-
foreign key (card_id) references idencard(id)
-
);
-
-
insert into idencard values (1,'1111111111111110');
-
insert into person values (1,'张三',1);
创建entity实体对象

IdenCard

Person

IdenCardMapper接口对象


IdenCardMapper.xml文件

-
-
-
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
-
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-
<mapper namespace="com.goodup.mapper.IdenCardMapper">
-
<select id="getIdenCardById" resultType="IdenCard" parameterType="Integer">
-
select * from idencard where id=#{id}
-
</select>
-
</mapper>
第一种方法-使用多表联查sql语句
创建PersonMapper.java和PersonMapper.xml文件 - 涉及到级联
PersonMapper.java
-
package com.goodup.mapper;
-
-
import com.goodup.entity.Person;
-
-
public interface PersonMapper {
-
//根据id获取人信息
-
public Person getPersonById(Integer id);
-
}
PersonMapper.xml文件
-
-
-
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
-
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-
<mapper namespace="com.goodup.mapper.PersonMapper">
-
<resultMap id="resultPersonMap" type="Person">
-
<id property="id" column="id"/>
-
<result property="name" column="name"/>
-
<association property="card" javaType="IdenCard" >
-
<result property="id" column="id"/>
-
<result property="card_sn" column="card_sn"/>
-
</association>
-
</resultMap>
-
<select id="getPersonById" resultMap="resultPersonMap" parameterType="Integer">
-
select * from person,idencard where person.id = #{id} and person.card_id = idencard.id;
-
</select>
-
</mapper>
- 属性及含义:
property="card":表示Person对象的card属性。javaType="IdenCard":表示card属性的类型为IdenCard。column="id":关联的是查询语句SELECT * FROM person, idencard WHERE person.id=1 AND person.card_id = idencard.id返回的字段。

测试


第二种方式(推荐) - 使用多次单表操作
可以复用已经写好的方法
PersonMapper.xml文件
-
<resultMap id="resultPersonMap2" type="Person">
-
<id property="id" column="id"/>
-
<result property="name" column="name"/>
-
<association property="card" javaType="IdenCard"
-
column="card_id"
-
select="com.goodup.mapper.IdenCardMapper.getIdenCardById">
-
</association>
-
</resultMap>
-
<select id="getPersonById2" resultMap="resultPersonMap2" parameterType="Integer">
-
select * from person where id=#{id}
-
</select>
- 核心思想:将多表联查分解为单表操作,简洁且易于维护。
- 优势:可复用已写好的方法(组合方式)。
property="card":表示Person对象的card属性。column="card_id":关联的是SELECT * FROM person WHERE id = #{id}语句返回的card_id字段。- 返回的字段card_id 信息/数据 作为getIdenCardById入参

测试

第三种方式-注解方式
PersonMapperAnnotation接口
-
package com.goodup.mapper;
-
-
import com.goodup.entity.Person;
-
import org.apache.ibatis.annotations.One;
-
import org.apache.ibatis.annotations.Result;
-
import org.apache.ibatis.annotations.Results;
-
import org.apache.ibatis.annotations.Select;
-
-
public interface PersonMapperAnnotation {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
public Person getPersonById(Integer id);
-
}
测试



2.多对一 - 需要使用collection标签

- 基本介绍:多对 1 是基本映射关系,也可理解为 1 对多。像 User 与 Pet 的关系,一个用户能养多只宠物;Dep 和 Emp 的关系,一个部门可有多个员工。
- User---Pet :一个用户可以养多只宠物
- Dept---Emp :一个部门可以有多个员工
- 双向的多对一关系:双向的多对一关系,它比单向的复杂,实际项目开发中会用到。双向多对一即通过一方(如 User)能查询到另一方(如 Pet),反过来通过另一方(Pet)也能级联查询到一方(User)的信息。另外,多对多关系可在多对 1 基础上扩展得到。
双向的多对一关系实例 Pet 和 User
建表Pet表和User表
-
create table mybatis_user(
-
id int primary key auto_increment,
-
name varchar(32) not null default ''
-
)charset=utf8;
-
create table mybatis_pet(
-
id int primary key auto_increment,
-
nickname varchar(32) not null default '',
-
user_id int,
-
foreign key (user_id) references mybatis_user(id)
-
)charset=utf8;
-
-
insert into mybatis_user values (NULL,'宋江'),(NULL,'张飞');
-
insert into mybatis_pet values (1,'黑背',1),(2,'小哈',1),(3,'波斯猫',2),(4,'贵妃猫',2);
实体类Entity
Pet类
-
package com.goodup.entity;
-
-
public class Pet {
-
private Integer id;
-
private String nickname;
-
private User user;
-
-
.....get set toString 省略
-
}
User类
-
package com.goodup.entity;
-
-
import java.util.List;
-
-
public class User {
-
private Integer id;
-
private String name;
-
private List<Pet> pets;
-
-
.....get set toString 省略
-
}
UserMapper.java接口和UserMapper.xml
UserMapper.java接口
-
package com.goodup.mapper;
-
-
import com.goodup.entity.User;
-
-
public interface UserMapper {
-
User getUserById(Integer id);
-
}
UserMapper.xml
-
-
-
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
-
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-
<mapper namespace="com.goodup.mapper.UserMapper">
-
<resultMap id="UserResultMap" type="User">
-
<id property="id" column="id"/>
-
<result property="name" column="name"/>
-
<collection property="pets" ofType="Pet" column="id" select="com.goodup.mapper.PetMapper.getPetListByUserId"/>
-
</resultMap>
-
<select id="getUserById" parameterType="Integer" resultMap="UserResultMap">
-
select * from mybatis_user where id=#{id}
-
</select>
-
</mapper>
collection标签解读
当执行 SQL 查询后,需要将查询结果映射到 Java 对象,且对象之间存在一对多关系时,就可以使用<collection>标签。比如,从数据库中查询用户信息,同时需要获取该用户下的所有宠物信息,将这些信息映射到User对象(包含List<Pet>类型的属性来存储员工信息)。
常见属性
- property:指定要映射的对象属性名称,该属性是必须的。例如,在User类中有一个
List<Pet>类型的属性名为pets,那么property的值就应该设置为pets。 - ofType:指定集合中元素的类型。比如上述
employees集合中元素是Pet对象,那么ofType就设置为Pet类的全限定名,如com.example.domain.Pet。 - select:指定用来加载关联对象的子查询语句的 id。使用这种方式,MyBatis 会先执行主查询,然后根据主查询的结果,再去执行子查询来加载关联对象。例如,先查询部门信息,再根据部门 id 去查询该部门的员工信息。
- column:指定将主查询结果中的哪些列作为子查询的参数。当使用
select属性进行子查询时,需要通过column指定传递给子查询的参数。比如,主查询结果中有列,子查询需要根据这个id来查询员工信息,那么column就设置为id。 - fetchType:指定加载关联对象的方式,取值为
eager(立即加载)和lazy(延迟加载) 。如果设置为lazy,只有在真正访问到关联对象时,才会执行子查询去加载数据。
PetMapper.java接口和PetMapper.xml
PetMapper.java接口
-
package com.goodup.mapper;
-
-
import com.goodup.entity.Pet;
-
-
import java.util.List;
-
-
public interface PetMapper {
-
List<Pet> getPetListByUserId(Integer userId);
-
Pet getPetById(Integer id);
-
}
PetMapper.xml
-
-
-
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
-
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-
<mapper namespace="com.goodup.mapper.PetMapper">
-
<resultMap id="PetResultMap" type="Pet">
-
<id property="id" column="id"/>
-
<result property="name" column="name"/>
-
<association property="user" javaType="User" column="user_id" select="com.goodup.mapper.UserMapper.getUserById"/>
-
</resultMap>
-
<select id="getPetListByUserId" parameterType="Integer" resultMap="PetResultMap">
-
select * from mybatis_pet where user_id = #{userId};
-
</select>
-
-
<select id="getPetById" parameterType="Integer" resultMap="PetResultMap">
-
select * from mybatis_pet where id = #{id};
-
</select>
-
</mapper>
测试
测试UserMapper
-
package com.goodup.mapper;
-
-
import com.goodup.entity.Pet;
-
import com.goodup.entity.User;
-
import com.goodup.util.MyBatisUtil;
-
import org.apache.ibatis.session.SqlSession;
-
import org.junit.Before;
-
import org.junit.Test;
-
-
import java.util.List;
-
-
public class UserTest {
-
private SqlSession sqlSession;
-
private UserMapper userMapper;
-
-
-
public void init(){
-
sqlSession = MyBatisUtil.getSqlSession();
-
userMapper = sqlSession.getMapper(UserMapper.class);
-
}
-
-
-
public void testGetUserById(){
-
User user = userMapper.getUserById(1);
-
System.out.println("用户名:" + user.getName());
-
List<Pet> pets = user.getPets();
-
for(Pet pet : pets){
-
System.out.println("宠物名:" + pet.getNickname());
-
}
-
System.out.println("========================");
-
if(sqlSession != null){
-
sqlSession.close();
-
}
-
}
-
}

测试PetMapper
-
package com.goodup.mapper;
-
-
import com.goodup.entity.Pet;
-
import com.goodup.entity.User;
-
import com.goodup.util.MyBatisUtil;
-
import org.apache.ibatis.session.SqlSession;
-
import org.junit.Before;
-
import org.junit.Test;
-
-
import java.util.List;
-
-
public class PetMapperTest {
-
private SqlSession sqlSession;
-
private PetMapper petMapper;
-
-
-
public void init(){
-
sqlSession = MyBatisUtil.getSqlSession();
-
petMapper = sqlSession.getMapper(PetMapper.class);
-
}
-
-
-
public void testGetPetListByUserId(){
-
List<Pet> petList = petMapper.getPetListByUserId(1);
-
for(Pet pet : petList){
-
System.out.println("宠物名:" + pet.getNickname());
-
}
-
}
-
-
-
public void testGetPetById(){
-
Pet pet = petMapper.getPetById(1);
-
User user = pet.getUser();
-
System.out.println("宠物名:" + pet.getNickname());
-
System.out.println("用户姓名:" + user.getName());
-
}
-
}

toString的问题


如果直接打印User或者Pet对象会报栈溢出错误


注解方式
UserMapperAnnotation接口

-
package com.goodup.mapper;
-
-
import com.goodup.entity.Pet;
-
import com.goodup.entity.User;
-
import org.apache.ibatis.annotations.Many;
-
import org.apache.ibatis.annotations.Result;
-
import org.apache.ibatis.annotations.Results;
-
import org.apache.ibatis.annotations.Select;
-
-
import java.util.List;
-
-
public interface UserMapperAnnotation {
-
-
-
-
-
-
-
User getUserById(Integer id);
-
}
PetMapperAnnotation接口
-
package com.goodup.mapper;
-
-
import com.goodup.entity.Pet;
-
import org.apache.ibatis.annotations.One;
-
import org.apache.ibatis.annotations.Result;
-
import org.apache.ibatis.annotations.Results;
-
import org.apache.ibatis.annotations.Select;
-
-
import java.util.List;
-
-
public interface PetMapperAnnotation {
-
-
-
-
-
-
-
-
-
List<Pet> getPetListByUserId(Integer userId);
-
-
-
-
-
-
-
-
-
-
-
Pet getPetById(Integer id);
-
}
ResultMap复用




七、缓存-提高检索效率
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
1一级缓存
MyBatis 默认启用一级缓存(本地缓存 /local Cache),该缓存为 SqlSession 级别。当同一个 SqlSession 接口对象调用相同的 select 语句时,会直接从缓存中获取数据,无需再次查询数据库。
一级缓存原理图

id号相同就会是一条sql语句
-
-
public void findById(){
-
//一条sql语句
-
Monster monster = monsterMapper.findById(19);
-
System.out.println(monster);
-
Monster monster1 = monsterMapper.findById(19);
-
System.out.println(monster1);
-
}

id号不同

一级缓存执行流程
默认情况下先走的是CachingExecutor
第一次查询



第二次查询相同id





一级缓存存储结构

存放了两个数据的情况

一级缓存失效的几种情况


sqlSession关闭后再查询
-
-
public void findById(){
-
//一条sql语句
-
Monster monster = monsterMapper.findById(19);
-
System.out.println(monster);
-
-
//关闭sqlSession
-
sqlSession.close();
-
sqlSession = MyBatisUtil.getSqlSession();
-
monsterMapper = sqlSession.getMapper(MonsterMapper.class);
-
-
Monster monster1 = monsterMapper.findById(19);
-
System.out.println(monster1);
-
}

执行sqlSession.clearCache()使一级缓存失效

-
-
public void findById2(){
-
//一条sql语句
-
Monster monster = monsterMapper.findById(19);
-
System.out.println(monster);
-
-
sqlSession.clearCache();
-
-
Monster monster1 = monsterMapper.findById(19);
-
System.out.println(monster1);
-
}



对同一个Entity对象进行修改
-
-
public void findById3(){
-
//一条sql语句
-
Monster monster = monsterMapper.findById(19);
-
System.out.println(monster);
-
-
sqlSession.clearCache();
-
-
monster.setName("zhangli");
-
//更新后,缓存清空
-
monsterMapper.updateMonster(monster);
-
-
Monster monster2 = monsterMapper.findById(19);
-
System.out.println(monster2);
-
}

2.二级缓存
- 二级缓存与一级缓存的目的一致,都是用于提升检索效率的技术。
- 二者最主要的区别在于作用域范围:一级缓存作用域为
SqlSession会话级别,仅在一次会话内有效;二级缓存作用域是全局范围,对不同的会话都能发挥作用。
二级缓存的工作原理
二级缓存工作原理图

二级缓存quick start
配置文件setting对缓存的设置


使用二级缓存时Entity类实现序列化接口,因为二级缓存可能使用到序列化技术

在对应的XXXMapper.xml中设置二级缓存的策略
-
<cache
-
eviction="FIFO"
-
flushInterval="30000"
-
size="360"
-
readOnly="true"/>
- eviction="FIFO":采用先进先出的缓存淘汰策略,后续会详细说明。
- flushInterval="30000":每 30000 毫秒(即 30 秒)刷新一次缓存,以此保证与数据库数据一致。
- size:二级缓存最多可保存 360 个对象,若超出数量,就会启用 FIFO 策略进行处理,该属性默认值为 1024。
- readOnly:设置为只读模式,目的是提高缓存使用效率。建议设置成true,这样可以提升效率,如果有修改操作,设置成false,默认是false




默认的清除策略是 LRU。
flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
当配置了二级缓存后再进行 sqlsession关闭再查询


二级缓存的执行流程


二级缓存注意事项和使用陷阱

全局性地开启或关闭所有映射器配置文件中已配置的任何缓存

不在映射文件中配置二级缓存

直接在配置方法上指定


mybatis刷新二级缓存的设置
flushCache

mybatis的一级缓存和二级缓存执行顺序
缓存的执行顺序:二级缓存-->一级缓存-->数据库
第一查询后关闭sqlSession,再连续查询两次 观察数据是从哪里取出

细节说明
不会出现一级缓存和二级缓存中有同一个数据,因为二级缓存是在一级缓存关闭之后才有的


第一次是从数据库拿到, 后两次是从一级缓存中拿到
3.ehcache缓存

Ehcache配置文件ehcache.xml - 请叫我刀刀 - 博客园


引入ehcache依赖

mybatis-config.xml中开启二级缓存

加入ehcache.xml配置文件



Java Ehcache缓存的timeToIdleSeconds和timeToLiveSeconds区别
Java Ehcache缓存的timeToIdleSeconds和timeToLiveSeconds区别 - TaoBye
在XXXMapper.xml文件中启用Ehcache

ehcache细节


缓存用hashMap存储
3.创建一个Monster对象 - 和数据库中的monster表对应
4.创建一个MonsterMapper接口 - 该接口用于声明操作monster表的方法
5.在MyBatis-Config中配置关联Mapper.xml
6.创建一个工具类MyBatisUtils,快速得到sqlSession
执行sqlSession.clearCache()使一级缓存失效
使用二级缓存时Entity类实现序列化接口,因为二级缓存可能使用到序列化技术

浙公网安备 33010602011771号