1.简介
1.1 什么是Mybatis1.2 为什么需要Mybatis 2、第一个Mybatis程序
2.1 搭建环境2.2 创建一个模块2.3 编写代码2.4测试 3、CRUD
1、namespace2、select3、Insert4、update5、delete6、万能的Map7、模糊查询 4、配置解析
1、核心配置文件2、环境配置3、属性(properties)4、类型别名(typeAliases)5、设置6、其他配置7、映射器(mappers)8、作用域(Scope)和生命周期 5、解决属性名和字段名不一致的问题:ResultMap6、日志
6.1 日志工厂6.2 LOG4J 7、分页
7.1 使用limit分页7.2 RowBounds实现分页7.3 分页插件 8、使用注解开发
8.1 面向接口编程8.2使用注解开发8.3 CRUD 9、Lombok10、多对一处理
测试环境搭建按照查询嵌套处理按结果嵌套查询 11、一对多处理
1、搭建环境2.按结果嵌套查询3. 按查询嵌套处理 12、动态SQL
搭建环境IFchoose whensetForeachSQL片段 13、缓存
1. Mybatis缓存2、一级缓存3、二级缓存4、ehcache
1.简介 1.1 什么是Mybatishttps://mybatis.org/mybatis-3/zh/index.html
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
1.2 为什么需要Mybatis帮助程序员将数据存入到数据库中
传统的jdbc代码太复杂。简化。框架。自动化。
简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件。易于学习,易于使用。通过文档和源代码,可以比较完全的掌握它的设计思路和实现。灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。提供映射标签,支持对象与数据库的orm字段关系映射。提供对象关系映射标签,支持对象关系组建维护。提供xml标签,支持编写动态sql。 2、第一个Mybatis程序 2.1 搭建环境
搭建数据库
新建普通maven项目,删src
导入依赖
2.2 创建一个模块mysql mysql-connector-java 5.1.47 org.mybatis mybatis 3.5.2 junit junit 4.13 test
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。
1.resource文件下配置mybatis-config.xml
2.编写工具类
//sqlSessionFactory----->sqlSession(类比preparedStatement)
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory = null;
static {
try {
//获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
// 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
public static SqlSession getsqlSession(){
return sqlSessionFactory.openSession();
}
}
2.3 编写代码
实体类
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
Mapper接口(Dao)
ListgetUserList();
Mapper.xml(Dao实现类)
在Mybatis-config.xml中注册Mapper:
2.4测试
junit
配置文件无法被导出或生效:
src/main/java ***.properties src/main/resources ***.properties
实体类中写toString方法,才能打印user
for (User user : userList) {
System.out.println(user);
}
3、CRUD
1、namespace
namespace中的包名要和Dao/mapper接口的包名一致
2、select编写接口
User getUserById(int id);
编写实现
id:就是对应的namespace中的方法名
resultType:Sql语句执行的返回值
parameterType:参数类型
编写测试
//增删改需要提交事务
@Test
public void addUser(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser(new User(4,"haha","123"));
//提交事务
sqlSession.commit();
sqlSession.close();
}
3、Insert
4、updateinsert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd})
5、deleteupdate mybatis.user set name = #{name},pwd = #{pwd} where id = #{id};
6、万能的Mapdelete from mybatis.user where id = #{id};
insert into mybatis.user(id,pwd) values (#{userid},#{password})
@Test
public void addUser2(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map map = new HashMap<>();
map.put("userid",5);
map.put("password","222233");
mapper.addUser2(map);
//提交事务
sqlSession.commit();
sqlSession.close();
}
7、模糊查询
@Test
public void getUserLike(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List userLike = mapper.getUserLike("李");
for (User user : userLike) {
System.out.println(user);
}
sqlSession.commit();
sqlSession.close();
}
或#{value} “%李%”
4、配置解析 1、核心配置文件mybatis-config.xml
configuration(配置):
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量):
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
2、环境配置
3、属性(properties)
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
driver=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8username=truepassword=123456
在核心配置文件中引入(注意顺序)
4、类型别名(typeAliases)
1、类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
2、或者指定实体类的包,mybatis会在包下搜索需要的JavaBean。类名首字母小写。
实体类较少,使用第一种,实体类多,使用第二种
第一种可以diy别名
3、每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:
@Alias("author")
public class Author {
...
}
5、设置
6、其他配置
7、映射器(mappers)
方式一:
方式二:使用class文件绑定注册
注意点:接口和它的Mapper配置文件必须同名且在同一个包下
方式三:使用扫描包进行注入绑定
注意点:接口和它的Mapper配置文件必须同名且在同一个包下
8、作用域(Scope)和生命周期不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder
一旦创建了 SqlSessionFactory,就不再需要它了。局部变量
SqlSessionFactory
可以类比数据库连接池SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在SqlSessionFactory 的最佳作用域是应用作用域(全局)。最简单的就是使用单例模式或者静态单例模式。
SqlSession
每个线程都应该有它自己的 SqlSession 实例。(连接到池的一个请求)SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。用完赶紧关闭,否则资源被占用
每个Mapper代表一个具体业务
5、解决属性名和字段名不一致的问题:ResultMap数据库中的字段:
测试实体类字段不一致的情况:
private int id;private String name;private String password;
select * from mybatis.user where id = #{id}即 select id,name,pwd from mybatis.user where id = #{id}
解决办法:
起别名
select id,name,pwd as password from mybatis.user where id = #{id}
resultMap
结果集映射(显式)
select * from mybatis.user where id = #{id}
resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。
之前你已经见过简单映射语句的示例,它们没有显式指定 resultMap。比如:
上述语句只是简单地将所有的列映射到 HashMap 的键上,这由 resultType 属性指定。虽然在大部分情况下都够用,但是 HashMap 并不是一个很好的领域模型。
曾经:sout debug
现在:日志工厂
SLF4JLOG4J(deprecated since 3.5.9) 【掌握】LOG4J2JDK_LOGGINGCOMMONS_LOGGINGSTDOUT_LOGGING 【掌握】NO_LOGGING
在Mybatis具体使用哪一个日志实现,在设置中设定
6.2 LOG4J
1.先导入LOG4J的包
log4j log4j 1.2.17
2.写配置文件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
3.配置log4j为日志的实现
4.LOG4J的使用
7、分页 7.1 使用limit分页ListgetUserByLimit(Map map);
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map map = new HashMap();
map.put("startIndex",0);
map.put("pageSize",2);
List userByLimit = mapper.getUserByLimit(map);
for (User user : userByLimit) {
System.out.println(user);
}
sqlSession.close();
}
7.2 RowBounds实现分页
ListgetUserByRowBounds();
@Test
public void getUserByRowBounds(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
//RowBounds实现
RowBounds rowBounds = new RowBounds(1,2);
//通过java代码层面实现分页
List userList = sqlSession.selectList("com.cc.mapper.UserMapper.getUserByRowBounds",null,rowBounds);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
7.3 分页插件
8、使用注解开发
8.1 面向接口编程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WZlXtBJ0-1643705679030)(https://gitee.com/roadside-stall/images/raw/master/imgs/20220130192709.png)]
8.2使用注解开发1、注解在接口上实现
@Select("select * from user")List getUsers();
2、需要再核心配置文件中绑定接口
3、测试
@Testpublic void getUsers(){ SqlSession sqlSession = MybatisUtil.getsqlSession(); //底层主要应用反射 UserMapper mapper = sqlSession.getMapper(UserMapper.class); List users = mapper.getUsers(); for (User user : users) { System.out.println(user); } sqlSession.close();}
本质:反射机制实现
底层:动态代理
Mybatis详细的执行流程!
8.3 CRUD可以在工具类创建的时候实现自动提交事务
public static SqlSession getsqlSession(){
return sqlSessionFactory.openSession(true);
}
9、Lombok
使用步骤:
1、在IDEA中安装Lombok插件
2、导入Lombok的jar包
org.projectlombok lombok 1.18.22
3、在实体类上加注解即可
@Data:无参构造,getter,setter,tostring,hashcode,equals
@AllArgsConstructor有参
@NoArgsConstructor无参
@Getter and @Setter @ToString @EqualsAndHashCode @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog @Data @Builder10、多对一处理
一个老师---->多个学生
测试环境搭建1、导入Lombok
2、新建实体类Teacher Student
3、建立Mapper接口
4、建立Mapper.xml文件
5、在核心配置文件中绑定注册Mapper
6、测试查询
按照查询嵌套处理第一种:select id,name,tid from student where tid=(select id from teacher)
按结果嵌套查询select * from student
第二种:select s.id,s.name,t.name from student s,teacher t where s.tid=t.id
11、一对多处理 1、搭建环境select s.id sid,s.name sname,t.name tname from student s,teacher t where s.tid=t.id
实体类
@Data
public class Student {
private int id;
private String name;
//学生关联一个老师
private int tid;
}
@Datapublic class Teacher { private int id; private String name; //一个老师拥有多个学生 private List students;}
2.按结果嵌套查询
3. 按查询嵌套处理
小结
1.关联-association 多对一
2.集合-collection 一对多
3.JavaType & ofType
javaType:用来指定实体类中属性的类型
ofType:用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
12、动态SQLifchoose (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
//实体类
@Data
public class Blog {
private int id;
private String title;
private String author;
private Date createTime;
private int views;
}
IF
ListqueryBlogIF(Map map);
select * from blog where 1=1 and title = #{title} and author = #{author}
@Testpublic void queryBlogIF(){ SqlSession sqlSession = MybatisUtil.getsqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap hashMap = new HashMap(); hashMap.put("title","Java"); hashMap.put("author","狂神说"); List blogs = mapper.queryBlogIF(hashMap); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close();}
choose when
setselect * from blog title = #{title} and author = #{author} and views = #{views}
Foreachupdate blog where id = #{id} title = #{title}, author = #{author},
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。
ListqueryBlogForeach(Map map);
eg. select * from blog where 1=1 and (id=1 or id=2 or id=3)select * from blog id = #{id}
@Test
public void queryBlogForeach(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
ArrayList ids = new ArrayList<>();
ids.add(1);
map.put("ids",ids);
List blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
SQL片段
title = #{title}, author = #{author}, update blog where id = #{id}
动态SQL就是在拼接SQL语句
建议:
先在MySQL中写出完整SQL,再去对应修改为动态SQL实现通用即可
13、缓存 1. Mybatis缓存 2、一级缓存测试步骤:
1.开启日志
2.测试在一个session中查询两次相同记录
3.查看日志输出
缓存失效情况:
1.查询不同东西
2.增删改操作,可能会改变原来的数据,所以必定刷新缓存
3.查询不同的Mapper.xml
4.手动清理缓存
sqlSession.clearCache();//手动清理
小结:一级缓存默认开启,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段
3、二级缓存要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:
LRU – 最近最少使用:移除最长时间不被使用的对象。FIFO – 先进先出:按对象进入缓存的顺序来移除它们。SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU。
步骤:
1.开启全局缓存
2.在要使用二级缓存中的Mapper中开启
3.测试
问题:只写标签,需要将实体类序列化!
readOnly默认为false,只读不报错,可读写的缓存会通过序列化返回缓存对象的拷贝,此时需要实体类实现Serializable接口或配置 readonly=“true”
小结
只要开启了二级缓存,在同一个Mapper下就有效
所有的数据都会先放在一级缓存中
只有当会话提交或关闭的时候,才会提交到二级缓存中
查找顺序:先看二级缓存中有没有,再看一级缓存中有没有,再查询数据库
4、ehcacheehcache是一种广泛使用的开源Java分布式操作,主要面向通用缓存
1.导包
org.mybatis.caches mybatis-ehcache 1.2.1
2.在mapper中指定使用ehcache缓存实现
3.配置文件ehcache.xml



