Mybatis详解
共 31737字,需浏览 64分钟
·
2021-03-14 18:21
点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
作者 | Jeson_sun
来源 | urlify.cn/AbEfEr
1. Mybatis介绍
1.1什么是Mybatis
Mybatis是是一款优秀的持久层框架。
它支持定制SQL,存储过程和高级映射。
Mybatis几乎避免了所有的JDBC代码和手动设置参数已经获取结果集。
Mybatis可以用简单的xml或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
1.2 Mybatis的优点
简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
提供映射标签,支持对象与数据库的orm字段关系映射
提供对象关系映射标签,支持对象关系组建维护
提供xml标签,支持编写动态sql。
1.3 Mybatis的执行流程
1.4如何获得Mybatis
maven仓库
Github
1.5 为什么要使用Mybatis
帮助程序员把数据存入数据库中
因为懒:传统的JDBC代码太复杂,于是就有了Mybatis,它可以使代码更加的简化。
方便:作为一个框架,条条框框已经帮我们约定好,我们只需要往里面填自己需要的东西即可
2.我的第一个mybatis程序
2.1 搭建环境
2.1.1 搭建数据库
2.1.2 新建一个项目
新建一个普通maven项目
删除src目录
导入maven依赖
<dependencies>
<!--servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!--jsp依赖-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
2.1.3 新建一个模块
编写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核心配置文件-->
<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?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
编写mybatis工具类
package com.lee.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 {
//第一步:使用mybatis获取sqlsessionfactory对象
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputstream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputstream);
} catch (IOException e) {
e.printStackTrace();
}
}
//第二步:既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
public static SqlSession getSqlsession()
{
return sqlSessionFactory.openSession();
}
}
编写代码
实体类
package com.lee.pojo;
public class User {
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
private int id;
private String name;
private String pwd;
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getPwd() {
return pwd;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
Dao接口
接口实现类
2.1.4 测试
注意点:错误Type interface com.lee.dao.UserMapper is not known to the MapperRegistry.
需要把Mapper接口放到资源文件
3.配置解析
3.1 核心配置文件
mybatis-config.xml
mybatis的配置文件包含了会深深影响mybatis行为的设置和属性的信息。
3.1.1 配置环境(environments)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者共享相同 Schema 的多个生产数据库, 想使用相同的 SQL 映射。许多类似的用例。
不过要记住:尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。
<environments default="development"> <!--默认选择的ID-->
<environment id="development">
<transactionManager type="JDBC"> <!--mybatis默认事务管理器-JDBC-->
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED"> <!--mybatis默认数据源-POOLED-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
3.1.2 属性(properties)
我们可以通过properties属性来实现引用配置文件
编写一个配置文件
3.1.3 类型别名(typeAliases)
类型别名是为 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>
- 也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean;每一个在包中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。比如 domain.blog.Author 的别名为 author.
例如:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
3.1.4 设置(settings)
目前只需要挑重点记忆
一个配置完整的 settings 元素的示例如下:
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
4.生命周期和作用域
理解我们之前讨论过的不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。
因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
SqlSessionFactory
说白了可以想象成数据库连接池
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。
SqlSession可以理解为连接到连接池的请求
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
5. 解决字段名和属性名不一致的办法
5.1 起别名
5.2 result Map(结果集映射)
resultMap 元素是 MyBatis 中最重要最强大的元素。
ResultMap 的设计就是简单语句不需要明确的结果映射,而很多复杂语句确实需要描述它们 的关系。
ResultMap 最优秀的地方你已经了解了很多了,但是你还没有真正的看到一个。
如果世界总是那么简单就好了!!!
6.日志
如果一个数据库操作出现了异常,我们需要拍错,日志就是最好的帮手。
SLF4J
LOG4J 《掌握》
LOG4J2
JDK_LOGGING
COMMONS_LOGGING
STDOUT_LOGGING《掌握》
NO_LOGGING
6.1 STDOUT_LOGGING标准日志输出
6.2 LOG4J
6.2.1 什么是LOG4J
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
我们也可以控制每一条日志的输出格式
通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
6.2.2 使用LOG4J
导入log4j的包
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
配置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/lee.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为日志实现
<settings>
<setting name="logImpl"value="LOG4J"/>
</settings>
使用log4j
在使用LOG4J的类中导入包
生成日志对象,参数为当前类的class
7. 分页
为什么要分页?
减少数据处理量
7.1 使用limit分页
7.1.1语法
select * from user startindex,pageSize
7.1.2 使用mybatis实现分页
接口
Mapper.xml配置文件
测试
8. Mybatis执行流程剖析
9. Lombok
9.1 使用步骤
在idea中安装Lombok插件
导入Maven依赖
9.2 作用
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.使用注解,简化操作
9.3注解的使用
@Getter/@Setter: 作用类上,生成所有成员变量的getter/setter方法;作用于成员变量上,生成该成员变量的getter/setter方法。可以设定访问权限及是否懒加载等。
@Getter @Setter private boolean funny;
@ToString:作用于类,覆盖默认的toString()方法,可以通过of属性限定显示某些字段,通过exclude属性排除某些字段。
@EqualsAndHashCode:作用于类,覆盖默认的equals和hashCode
@NonNull:主要作用于成员变量和参数中,标识不能为空,否则抛出空指针异常。
@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor:作用于类上,用于生成构造函数。有staticName、access等属性。
staticName属性一旦设定,将采用静态方法的方式生成实例,access属性可以限定访问权限。@NoArgsConstructor:生成无参构造器;
@RequiredArgsConstructor:生成包含final和@NonNull注解的成员变量的构造器;
@AllArgsConstructor:生成全参构造器
@Data:作用于类上,是以下注解的集合:@ToString
@EqualsAndHashCode @Getter @Setter
@RequiredArgsConstructor
@Builder:作用于类上,将类转变为建造者模式
@Log:作用于类上,生成日志变量。针对不同的日志实现产品,有不同的注解:
10.复杂查询处理
10.1 多对一的处理
按照查询嵌套处理
<!--
思路:按照查询嵌套处理
1.查询所以学生信息
2.根据查询出来的学生tid查询对应的老师!子查询
-->
<select id="getStudent" resultMap="Student">
select * from mybatis.student;
</select>
<resultMap id="Student" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id=#{id};
</select>
按照结果嵌套处理
<!--2
思路:按照结果嵌套处理
-->
<select id="getStudent" resultMap="Student">
select s.id sid,s.name sname,t.name tname
from student s,teacher t
where s.tid=t.id;
</select>
<resultMap id="Student" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
10.2 一对多处理
按照结果嵌套处理
<select id="getTeacher" resultMap="Teacher">
select s.id sid,s.name sname,s.tid stid,t.id tid,t.name tname
from student s,teacher t
where s.tid=t.id and t.id=#{tid};
</select>
<resultMap id="Teacher" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
小结
关联--association 【多对一】
集合--collection 【一对多】
3.Javatype--用来指定实体类中属性类型
4.oftype--用来指定映射到List或者集合中的pojo类型
11.动态SQL
什么是动态SQL?
根据不同的的条件生成不同的SQL
11.1 搭建环境
创建表
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-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核心配置文件-->
<configuration>
<!--引入外部配置文件-->
<properties resource="db.properties"/>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<typeAlias type="com.lee.pojo.Teacher" alias="Teacher"/>
<typeAlias type="com.lee.pojo.Student" alias="Student"/>
</typeAliases>
<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="StudentMapper.xml"/>
<mapper resource="TeacherMapper.xml"/>
</mappers>
</configuration>
编写工具类--utils.java
package com.lee.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 Utils {
private static SqlSessionFactory sqlSessionFactory;
static{
String resource = "mybatis-config.xml";
try {
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
编写实体类
package com.lee.pojo;
import java.util.Date;
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}
编写实体类对应的Mapper接口和Mapper.xml实现文件
11.2 动态SQL之if语句
动态 SQL 通常要做的事情是有条件地包含 where 子句的一部分。比如:
<select id="getBlogIF" parameterType="map" resultType="Blog">
select * from mybatis.blog
where 1=1
<if test="title != null">
and title=#{title}
</if>
</select> <!--if语句-->
这条语句提供了一个可选的文本查找类型的功能。如果没有传入"title",那么所有处于"ACTIVE"状态的BLOG都会返回;反之若传入了"title",那么就会把模糊查找"title"内容的BLOG结果返回(就这个例子而言,细心的读者会发现其中的参数值是可以包含一些掩码或通配符的)。
11.3 动态SQL之where语句
where 元素知道只有在一个以上的if条件有值的情况下才去插入"WHERE"子句。而且,若最后的内容是"AND"或"OR"开头的,where 元素也知道如何将他们去除。
也就是说如果有如果一个以上的if条件有值的情况下,它不会去除"AND"或"OR",如果如果有多个if语句,但只有第一个语句有值的情况下,它将会省略后面的"AND"或"OR",这样则避免了后面if语句因为没有值而出错的可能。
<where>
<if test="title != null">
title=#{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
11.4 动态SQL之choose,when语句
有些时候,我们不想用到所有的条件语句,而只想从中择其一二。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
还是上面的例子,但是这次变为提供了"title"就按"title"查找,提供了"author"就按"author"查找,若两者都没有提供,就返回所有符合条件的BLOG(实际情况可能是由管理员按一定策略选出BLOG列表,而不是返回大量无意义的随机结果)。
<select id="getBlogIF" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<choose>
<when test="title!=null">
title=#{title}
</when>
<when test="author != null">
author = #{author}
</when>
<when test="views !=null">
views = #{views}
</when>
</choose>
</where>
</select>
11.5 SQL片段
有时候我们可能需要将一些公共的部分抽取出来,方便复用
使用SQL标签抽取公共的部分
<sql id="sql_when">
<choose>
<when test="title!=null">
title=#{title}
</when>
<when test="author != null">
author = #{author}
</when>
<when test="views !=null">
views = #{views}
</when>
</choose>
</sql>
2 在需要的地方使用include标签进行插入
select * from mybatis.blog
<where>
<include refid="sql_when"/>
</where>
</select>
注意:最好基于单表定义SQL片段
11.6 动态SQL之Foreach标签
foreach 元素的功能是非常强大的,它允许你指定一个集合,声明可以用在元素体内的集合项和索引变量。它也允许你指定开闭匹配的字符串以及在迭代中间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
select * from mybatis.blog
where id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
12. 缓存
12.1 什么是缓存
存在内存当中的临时数据
将用户经常查询的数据放到缓存当中,当用户去查询数据的时候就不用从磁盘(关系型数据库数据文件)中查询,从缓存当中查询,从而提高了查询效率,解决了高并发系统问题。
12.2 为什么使用缓存
减少和数据库交互的次数,减少系统开销,提高系统效率。
12.3 什么样的数据能使用缓存
经常使用且不常改变的数据。
12.4 一级缓存
12.4.1 生命周期
从sqlsession的开始到结束
12.4.2 实例
SqlSession sqlSession = Utils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Blog blog1=mapper.getBlog(1);
System.out.println(blog1);
Blog blog2=mapper.getBlog(1);
System.out.println(blog2);
sqlSession.close();
结果:查询相同数据两次只连接了一次数据库。第二次查询是从缓存当中查询。可以分析日志的到。
12.4.3 缓存失效的情况
查询不同的东西
增删改操作可能会改变原来的缓存,所以必定会刷新缓存。
在不同的Mapper.xml中查询
手动清理缓存
小结:一级缓存默认是开启的,只在一次sqlsession中有效,也就是拿到连接到关闭连接!
12.5 二级缓存
12.5.1 概念
二级缓存也叫全局缓存,因为一级缓存作用域太低了,所以就有了二级缓存,它是基于namespace级别的缓存,一个名称空间对应一个二级缓存。
12.5.2 工作机制
一个会话查询一个数据,那这个数据就会被放到会话的一级缓存当中。
如果当前会话关闭了,那这个会话对应的一级缓存就没了,但是一级缓存当中的数据会被保存到二级缓存中。
新的会话查询信息就可以从二级缓存中获取内容
不同的mapper查处的信息会放到自己对应的缓存当中
12.5.3 步骤
开启全局缓存
在需要使用二级缓存的Mapper中开启
测试
粉丝福利:Java从入门到入土学习路线图
👇👇👇
👆长按上方微信二维码 2 秒
感谢点赞支持下哈