Mybatis
Mybatis
官网:https://mybatis.org/mybatis-3/
1. 简介
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
依赖jar包
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
2. 持久化
持久化就是将程序的数据在持久状态和瞬时状态下的转换
对应生活的例子
罐头,冷藏
3. 为什么使用Mybatis
- 方便
- 简化jdbc代码
- 帮助程序员将数据存到数据库中
- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sq
4. 第一个Mybatis程序
配置mybatis配置文件
<?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.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
编写工具类
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static{
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取openSession
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
编写测试环境
dao UserMapper
pojo User

编写 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.immortal.dao.UserMapper">
<!-- 查询语句-->
<select id="getUserList" resultType="com.immortal.pojo.User">
select * from mybatis.user
</select>
</mapper>
注意点

org.apache.ibatis.binding.BindingException: Type interface com.immortal.dao.UserMapper is not known to the MapperRegistry
没有注册到mapper的注册中心里面区 ,就是mybatis的核心配置文件
解决:

重新测试

初始化异常 失败
Cause: java.io.IOException: Could not find resource com.immortal.dao.UserMapper.xml
没有找到这个UserMapper.xml maven的静态资源过滤问题
解决:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
junit 测试
public class Test {
@org.junit.Test
public void TestSelect(){
//获得SqlSession
SqlSession sqlSession = MybatisUtils.getSqlSession();
//获取Mapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用接口的方法
List<User> userList = userMapper.getUserList();
//遍历
for (User user : userList) {
System.out.println(user);
}
//关闭连接
sqlSession.close();
}

5. CRUD
mamespace中的包名要和接口名字一致
Select
- id 就是对应的方法名
- resultType sql语句执行的返回值
- paramterType 参数的类型

<select id="getUserList" resultType="com.immortal.pojo.User">
select * from mybatis.user
</select>
Insert
<insert id="addUser" parameterType="com.immortal.pojo.User">
insert into mybatis.user (`id`,`name`,`pwd`) values(#{id},#{name},#{pwd})
</insert>
Update
<update id="updateUser" parameterType="com.immortal.pojo.User">
update mybatis.user set `name`=#{name},`pwd`=#{pwd} where `id`=#{id}
</update>
Delete
<delete id="deleteUser">
delete from mybatis.user where `id`=#{id}
</delete>
6. 万能的Map
假设,我们的实体类或者数据库中的表字段或者参数过多,我们应当考虑使用map
Map传递参数,直接在sql中取出key即可
7. 模糊查询
java代码执行的时候,传递通配符 %李%
@org.junit.Test
public void getUserLike(){
//获得SqlSession
SqlSession sqlSession = MybatisUtils.getSqlSession();
//获取Mapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用接口的方法
List<User> userList = userMapper.getUserLike("%杜%");
//遍历
for (User user : userList) {
System.out.println(user);
}
//关闭连接
sqlSession.close();
}
<select id="getUserLike" resultType="com.immortal.pojo.User">
select * from mybatis.user where `name` like #{value}
</select>
8. 配置解析
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
环境配置(environments)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
mybatis的默认事务管理器是jdbc它支持事务提交回滚操作 连接池是POOLED的
还有一种是MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:
官方文档 写的很清楚
属性(properties)
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai
username=root
password=123456
mybatis-config
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 导入外部配置文件-->
<properties resource="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/immortal/dao/UserMapper.xml"/>
</mappers>
</configuration>
优先使用外部配置文件
类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<typeAliases>
<typeAlias type="com.immortal.pojo.User" alias="User"/>
</typeAliases>
在其他地方可以使用User这个别名代表com.immortal.pojo.User
<typeAliases>
<package name="com.immortal.pojo"/>
</typeAliases>
每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:
@Alias("author")
public class Author {
...
}
下面是一些为常见的 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 |
设置(settings)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。


映射器(mappers)
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。例如:
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
这些配置会告诉 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL 映射文件了,也就是接下来我们要讨论的。
mapperRegister若如果没有注册的话,就区mybatis-config.xml配置
生命周期于作用域
生命周期于作用域是至关重要的,因为错误使用会导致严重的并发问题

SqlSessionFactoryBuilder
- 建造者模式
- 一旦创建了SqlSessionFactory,就不再需要了
- 局部变量
SqlSessionFactory
- 工厂模式
- 可以想象为数据库连接池
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是application作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
SqlSession
-
连接到连接池的一个请求
-
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
-
连接完毕后需要赶紧关闭,否则会占用资源!
-

9. 解决属性名和字段不一致
如果数据库的字段名与pojo的属性没有不一致的话,无法进行赋值到属性上,导致空值

ResultMap
可以通过 ResultMap这个标签将数据库的字段名与JavaBean的属性名进行关联进行赋值
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.immortal.dao.UserMapper">
<resultMap id="userMap" type="com.immortal.pojo.User">
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserById" resultMap="userMap">
select * from mybatis.user where id = #{id}
</select>
</mapper>

赋值到属性上面了
10. 日志
日志工厂

-
STDOUT_LOGGING简单,加上就可以用
-
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>控制台输出

LOG4J
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;
- 我们也可以控制每一条日志的输出格式
- 通过定义每一条日志信息的级别
- 最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
- 导入jar包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
编写配置文件
将mybatis的日志实现换成log4j的 就可以了
11. 分页
为什么要分页?
- 减少数据的处理量
语法
select * from user limit startIndex,pageSize;
select * from user limit 0,5;

基于Map实现,不正规,爽
<select id="getListUserLimit" parameterType="map" resultMap="userMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
</select>

RowBounds
<select id="getListUserRowBounds" resultMap="userMap">
select * from mybatis.user
</select>
@org.junit.Test
public void getListUserRowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
RowBounds rowBounds = new RowBounds(0,5);
List<User> userList = sqlSession.selectList("com.immortal.dao.UserMapper.getListUserRowBounds", null, rowBounds);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
分页插件

按照文档一步一步操作就行 easy
12. 使用注解开发
关于接口的理解 解耦
接口更深的理解就是定义与实现的分离
接口本事反应了系统设计人员对系统的抽象理解
接口有两类
- 第一种是对一个个体的抽象,它对应一个抽象体
- 第二种是对一个个体莫一方面的抽象,既成为一个抽象面
官网上很具体了 !!! 复杂的sql不建议使用
13. Mybatis执行流程
- Resources对象获取加载全局配置文件
- 实例化SqlSessionFactoryBuilder构造器
- 解析配置文件流XMLConfigBuilder
- 返回Configuration所有的配置信息
- sqlSessionFactory实例化
- 事务管理器transaction
- 创建执行器executor
- 创建sqlSession
- 实现crud
- 是否执行成功 失败回滚
14. 多对一,一对多
多个学生关联一个老师 【多对一】
对于老师而言就是 【一对多】

如果java实体类中包含其他java类 需要进行结果集映射
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.immortal.dao.StudentMapper">
<select id="getStudentList" resultMap="StudentTeacher">
select * from mybatis.student;
</select>
<!--这里将结果集通过association 进行映射 用子查询将结果映射到属性上-->
<resultMap id="StudentTeacher" type="com.immortal.pojo.Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="com.immortal.pojo.Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="com.immortal.pojo.Teacher">
select * from mybatis.teacher where id = #{tid}
</select>
</mapper>
还可以向将数据查询出来 在将数据映射到具体的属性上
<select id="getStudentList" resultMap="StudentTeacher">
select student.id as sid,student.name as sname,teacher.name as tname,teacher.id as tid
from mybatis.student,mybatis.teacher
where student.tid = teacher.id
</select>
<!--映射查询的数据-->
<resultMap id="StudentTeacher" type="com.immortal.pojo.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="com.immortal.pojo.Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
</association>
</resultMap>
其实我们在解决我问题就是 javaBean字段与数据库字段无法映射的问题
提供映射关系
15. 动态SQL
根据不同的条件生成不同的sql
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
-
if
-
<select id="selectBlogIF" resultType="com.immortal.pojo.Blog" parameterType="map"> select * from Blog <where> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </where> </select>
-
-
choose (when, otherwise)
-
<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ <choose> <when test="title != null"> AND title like #{title} </when> <when test="author != null and author.name != null"> AND author_name like #{author.name} </when> <otherwise> AND featured = 1 </otherwise> </choose> </select>
-
-
trim (where, set)
-
foreach
-
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:
-
<select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
-
报错
org.apache.ibatis.binding.BindingException: Mapper method 'com.immortal.dao.BlogMapper.addBlog attempted to return null from a method with a primitive return type (int).

这里脑子问题将select 标签写错了 导致这个问题 将改成insert标签就可以了
https://mybatis.org/mybatis-3/zh/dynamic-sql.html 官网 上面写的很好
SQL片段
我们有的时候可能会将一些公共的部分抽取出来
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
<select id="selectBlogIF" resultType="com.immortal.pojo.Blog" parameterType="map">
select * from Blog
<where>
<include refid="if-title-author"/>
</where>
</select>
16. 缓存
- 什么是缓存?
- 内存中存储的临时数据,
- 将用户经常查询的数据放在缓存中,用户区查询数据就不用次磁盘上查询,从缓存上查询会大大提高效率,提高性能
- 为什么使用缓存
- 减少与数据库的交互,减小系统的开销,提高系统的效率
- 什么数据能使用缓存
- 经常查询,不变的数据
Mybatis缓存
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置
- Mybatis系统默认定义两种缓存,一级缓存,二级缓存
- 默认情况下,只有一级缓存(sqlSession基本的缓存,也称为本地缓存)
- 二级缓存需要手动配置和开启,它是基于namespace级别的
- 为了提高扩展性,Mybatis定义了缓存接口Cache,我们可以通过实现接口,自定义二级缓存
- 可用的清除策略有:
LRU– 最近最少使用:移除最长时间不被使用的对象。FIFO– 先进先出:按对象进入缓存的顺序来移除它们。SOFT– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
一级缓存
这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
开启日志
测试在一个session中查询两次记录
@org.junit.Test
public void selectBlogIF(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("author","james");
List<Blog> blogs = mapper.selectBlogIF(map);
List<Blog> blogs1 = mapper.selectBlogIF(map);
System.out.println(blogs == blogs1);
sqlSession.close();
查看日志输出

缓存失效的情况:
- 增删改 可能会改变原来的数据,所有必定会刷新缓存!
- 查询不同的东西
- 查询不同的mapper.xml
- 手动清除


二级缓存
二级缓存也称全局缓存,一级的作用域太低了
基于namespace级别的缓存,一个命名空间,对应一个二级缓存
工作机制
- 一个会话查询一条数据,这个数据会存储在当前会话的一级缓存中
- 如果当前会话关闭了,这个对话的一级缓存就没有了,但是我们想要的是会话关闭了,一级缓存的数据就会被保存在二级缓存中
- 新的会话查询信息,就可以在二级缓存中获取信息
- 不同的mapperhu查出的数据,会放在自己对应的缓存中
步骤
-
需要在setting中中显示开启二级缓存
-
设置名 描述 有效值 默认值 cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true -
在mapper中 配置二级缓存
-
加上
-
<cache eviction="FIFO" //缓存策略 flushInterval="60000" //刷新实现 size="512" //缓存大小 readOnly="true"/> //只读
缓存原理


浙公网安备 33010602011771号