Mybatis学习
1.简介
- MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
- MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- MyBatis 本是apache的一个开源项目ibatis, 2010年这个项目由apache 迁移到了google code,并且改名为MyBatis 。
- 2013年11月迁移到Github .
- Mybatis官方文档 :https://mybatis.org/mybatis-3/zh/index.html
- GitHub : https://github.com/mybatis/mybatis-3
2.第一个Mybatis程序
环境:
- jdk 8+
- MySQL 5.7+
- maven -3.6.0
- idea
需要掌握:
- jdbc
- mysql
- java基础
- maven
- junit
思路:搭建环境,导入Mybatis,编写代码,测试
- 创建实验数据库
SET NAMES utf8
SET SQL_MODE='';
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 ;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 ;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' ;
SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 ;
CREATE DATABASE IF NOT EXISTS `mybatis` DEFAULT CHARACTER SET latin1 ;
USE `mybatis`;
/*Table structure for table `user` */
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`name` varchar(30) DEFAULT NULL,
`pwd` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=192 DEFAULT CHARSET=utf8;
SET SQL_MODE=@OLD_SQL_MODE ;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS ;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS ;
SET SQL_NOTES=@OLD_SQL_NOTES ;
- 导入相关的jar包
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
- 编写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.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/hecn/dao/userMapper.xml"/>
</mappers>
</configuration>
- 编写工具类
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 {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SqlSession连接
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
}
- 创建实体类
public class User {
private int id; //id
private String name; //姓名
private String pwd; //密码
//构造,有参,无参
//set/get
//toString()
}
- 编写Mapper(dao层)接口类
public class User {
private int id; //id
private String name; //姓名
private String pwd; //密码
//构造,有参,无参
//set/get
//toString()
}
- 编写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.hecn.dao.UserMapper">
<select id="selectUser" resultType="com.hecn.pojo.User">
select * from user
</select>
</mapper>
- 测试类
public class MyTest {
@Test
public void selectUser() {
SqlSession session = MybatisUtils.getSession();
//方法一:
//List<User> users = session.selectList("com.kuang.mapper.UserMapper.selectUser");
//方法二:
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.selectUser();
for (User user: users){
System.out.println(user);
}
session.close();
}
}
- 运行测试
Maven静态资源过滤问题:在pom文件中添加
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
3.CRUD(增删改查)
4.配置解析
5.解决属性名和字段名不一致的问题
6.日志
7.分页
8.使用注解开发
9.Lombok
10.多对一的处理
11.一对多
12.动态Sql
12.1.搭建环境
12.2.IF
<select id="queryBlogIf" parameterType="map" resultType="com.wuhan.jiayou.pojo.Blog">
select * from mybatis.blog
where 1=1
<if test="title !=null">
and title=#{title}
</if>
<if test="author !=null">
and author=#{author}
</if>;
</select>
12.3.choose(when,otherwise)
有时我们不想应用到所有的条件语句,而只想从中择其一项。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的***switch***语句。
<select id="queryBlogChoose" parameterType="map" resultType="com.wuhan.jiayou.pojo.Blog">
select * from mybatis.blog
<where>
<choose>
<when test="title!=null">
title=#{title}
</when>
<when test="author!=null">
author=#{author}
</when>
<otherwise>
views=#{views}
</otherwise>
</choose>
</where>
;
</select>
12.4.trim(where,set)
- where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。
<select id="queryBlogIf" parameterType="map" resultType="com.wuhan.jiayou.pojo.Blog">
select * from mybatis.blog
<where>
<if test="title !=null">
title=#{title}
</if>
<if test="author!=null">
and author=#{author}
</if>
</where>;
</select>
- set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号,因为用了条件语句之后很可能就会在生成的 SQL 语句的后面留下这些逗号。
(注:因为用的是“if”元素,若最后一个“if”没有匹配上而前面的匹配上,SQL 语句的最后就会有一个逗号遗留)
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
author=#{author}
</if>
</set>
<where>
id=#{id}
</where>
;
</update>
12.5.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>
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。
<select id="queryBlogForeach" parameterType="map" resultType="com.wuhan.jiayou.pojo.Blog">
select * from mybatis.blog
<where>
<foreach collection="ids" index="" item="id" open="and (" separator="or" close=")">
id=#{id}
</foreach>
</where>
;
</select>
注意 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象传递给 foreach 作为集合参数。当使用可迭代对象或者数组时,index 是当前迭代的次数,item 的值是本次迭代获取的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
12.6.SQL片段
有的时候我们需要将一些功能的部分抽取出来,方便复用!
- 使用SQL标签抽取公共部分
<sql id="if-title-author">
<if test="title !=null">
title=#{title}
</if>
<if test="author!=null">
and author=#{author}
</if>
</sql>
- 在需要的地方使用Incloude标签引用即可
<select id="queryBlogIf" parameterType="map" resultType="com.wuhan.jiayou.pojo.Blog">
select * from mybatis.blog
<where>
<include refid="if-title-author"></include>
</where>
;
</select>
- 注意事项
- 最好基于单表来定义SQL片段!
- 不要存在where标签
12.7.总结
动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了
建议:
6. 先在Mysql中写出完整的SQL,再对应的去修改成为我们的动态SQL实现通用即可
13.缓存
13.1.简介
- 什么是缓存[Cache]?
- 存在内存中的临时数据.
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
- 为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
- 什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据。
13.2.Mybatis缓存
- MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
- MyBatis系统中默认定义了两级缓存: 一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。 (SqISession级别的缓存, 也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
- 为了提高扩展性, MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
13.3.一级缓存
一级缓存也叫本地缓存:Sqlsession
- 与数据库同一次会话期间查询到的数据会放在本地缓存中.
- 以后如果需要相同的数据,直接从缓存中拿,没必要再去查询数据库.
测试步骤:
- 开启日志!
- 测试在一个Session中查询两次相同记录
- 查看日志输出
缓存失效的情况:
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存,使缓存失效。
- 查询不同的东西
- 查询不同的Mapper.xml
- 手动清理缓存
sqlSession.clearCache();//手动清理缓存
总结:
一级缓存默认是开启的,只在一次SqISession中有效, 也就是拿到连接到关闭连接这个区间段!
13.4.二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
- 工作机制:
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;
- 步骤:
i. 开启全局缓存
<!--显示的开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
ii.在要使用二级缓存的Mapper中开启
<!--开启二级缓存-->
<cache/>
也可以自定义参数
<!--开启二级缓存-->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"></cache>
iii.测试:
我们需要将实体类序列化,否则会报错
总结:
- 只要开启了二级缓存,在同一个Mapper下有效;
- 所有的数据都会先放在一级缓存中;
- 只有当会话提交,或者关闭的时候才会提交到二级缓存中;
13.5.缓存原理
先走二级缓存,没有就走一级缓存,还没有就查询数据库.