Mybatis
环境
- JDK 1.8
- Mysql 5.7
- maven 3.6.1
- IDEA
回顾:
- JDBC
- Mysql
- Java基础
- Maven
- Junit
框架:有配置文件的。最好看官网文档
1、简介
1.1 什么是Mybatis
优秀的持久层框架
如何获取
maven仓库 3.5.3
github :
中文文档:
1.2 持久层
数据持久化
-
持久化:将程序的数据在持久状态和瞬时状态转化的过程
-
内存:断电即失
-
数据库(JDBC),io文件持久化
为什么需要持久化
-
有一些对象,不能丢失
-
内存太贵了
1.3 持久层
Dao层,service层,Conntroller层
- 完成持久化工作的代码块
- 层界限十分明显
1.4 为什么需要Mybatis
- 方便,帮助程序员将数据存入数据库中
- 传统的JDBC太复杂,需要简化
- 不用Mybatis也可以,更容易上手。技术没有高低之分
- 优点
- 简单易学
- 灵活
- 解除sql与代码的耦合,sql和代码的分离
- 支持动态sql
- 最重要的一点:使用的人多
Spring SpringMVC SpringBoot
2、第一个Mybatis
思路:搭建环境,导入mybatis,编写代码,测试
2.1 搭建环境
搭建数据库
create database `mybatis`;
use `mybatis`;
create table `user`(
`id` int(20) not null primary key,
`name` varchar(30) default null,
`pwd` varchar(30) default null
)engine=innodb default charset=utf8;
insert into `user`(`id`,`name`,`pwd`) values
(1,'张三','123456'),
(2,'李四','123456'),
(3,'王五','123456');
新建项目
1.普通的maven项目com.liu
mybatis-study
2.删除src目录(父工程)
3.导入依赖pom.xml
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2.2 创建一个模块
1.刚刚项目下新建modules mybatis-01 普通的maven
2.编写mybatis的核心配置
resources文件夹下编写 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>
<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"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/liu/dao/UserMapper.xml"/>
</mappers>
</configuration>
3.编写工具类
com.liu.dao.util MybatisUtils
package com.liu.dao.util;
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{
//使用mybatis第一步;获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch(IOException e){
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
2.3 编写代码
- 实体类
pojo -User
package com.liu.pojo;
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
-
接口
UserDao-dao包下
package com.liu.dao; import com.liu.pojo.User; import java.util.List; public interface UserDao { List<User> getUserList(); }
-
接口实现类 由原来的 变成一个配置文件
dao-UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<mapper namespace="com.liu.dao.UserDao">
<select id="getUserList" resultType="com.liu.pojo.User">
select * from mybatis.user
</select>
</mapper>
2.4 测试
写到 test 中要建立对应的包
注意点:binding.BindingException报错
每一个mapper都要在核心配置文件mybatis-config中注册
<build>
<resources>
<resource>
<directory>src/main/resource</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>
资源过滤问题
test
package com.liu.dao;
import com.liu.dao.util.MybatisUtils;
import com.liu.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class test {
@Test
public void UserDaoTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
for (User user : userList) {
System.out.println(user);
}
//关闭资源
sqlSession.close();
}
}
3、CRUD-增删改必须要有事务
1.namespace
namespace中的包名要和dao/mapper中的接口一致
2.select
选择,查询语句
- id:就是对应namespace中的方法名
- resultType:SQL语句执行的返回值
- parameterType参数类型
3.Insert
4.update
5.delete
1.编写接口
//根据id查询用户
User getUserById(int id);
//插入用户
int insertUser(User user);
//更改用户
int updateUser(User user);
//删除用户
int deleteUser(int id);
2.编写对应的mapper中的sql语句
这个文件中不要有中文注释
<select id="getUserById" parameterType="int" resultType="com.liu.pojo.User">
select * from mybatis.user where id = #{id}
</select>
<insert id="insertUser" parameterType="com.liu.pojo.User" >
insert into mybatis.user (id,name,pwd) values(#{id},#{name},#{pwd})
</insert>
<update id="updateUser" parameterType="com.liu.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd} where id = #{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id = #{id}
</delete>
3.测试—增删改需要提交事务
@Test
public void AllTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
// User user = userDao.getUserById(1);
// System.out.println(user);
// int x =userDao.insertUser(new User(4, "哈哈", "123456"));
int x = userDao.updateUser(new User(4,"嘿嘿","123123"));
userDao.deleteUser(4);
//增删改要提交事务
sqlSession.commit();
sqlSession.close();
}
6.分析错误
- 标签不要匹配错
- resoure绑定mapper,需要用/ 不要用.
- 程序配置文件必须不能出错
- 空指针异常,没有注册到
- usermapper.xml中不能写中文注释 将UTF-8改为UTF8
- maven资源没有导出问题
7.万能的mapper
数据库中的字段或者参数过多,应当考虑使用Map
int addUser(Map<String,Object> map);
<insert id="addUser" parameterType="map">
insert into mybatis.user(id,name,pwd) values(#{1},#{2},#{3})
</insert>
Map<String,Object> map = new HashMap<String,Object>();
map.put("1",5);
map.put("2","李五");
map.put("3","123123");
mapper.addUser(map)
Map传递参数,直接在sql中取出key即可
对象传递参数,直接在sql中去对象的属性即可
只有一个基本类型参数的情况下,可以直接在数据库中取到
8.模糊查询怎么写
1.Java代码执行的时候,传递通配符 %
mapper.xxx("%李%");
2.在sql拼接中使用通配符!(更安全)
select * from mybatis.user where name like "%"#{value}"%"
4、配置解析
1.核心配置文件
- mybatis-config.xml
- MyBatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息
mybatis-02
大多数代码直接拷贝
2.环境变量
mybatis可以设置配置多个环境
尽管可以配置多个环境,但是每个SqlSessionFactory实例只能选择一种环境
学会配置多个环境
mybatis默认的事务管理器是JDBC,连接池是:POOLED
3.属性(properties)
可以通过properties属性来实现引用配置文件
这些属性都是可外部配置且可动态替换的,既可以在典型的java属性文件中配置,亦可以通过properties元素的子元素来传递[db.properties]
编写一个配置文件db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis
username=root
password=123456
在核心配置中引入
在xml中,所有标签是有顺序的
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>
<properties resource="db.properties">
</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/liu/dao/UserMapper.xml"/>
</mappers>
</configuration>
如果两个文件有同一个字段优先使用外部配置文件
可以直接引入外部文件
可以在其中增加一些属性配置
4.类型别名(typeAliases)
- 类型别名是为Java类型设置一个短的名字
- 存在的意义仅仅是用来减少完全限定名的冗余
方式一 给一个类起一个别名
<typeAliases>
<typeAlias type="com.liu.pojo.User" alias="User"/>
</typeAliases>
方式二 指定一个包名,mybatis会在包名下面搜索需要的javabean
扫描实体类的包,他的默认别名就为这个类的类名小写(小写也可以)
<typeAliases>
<package name="com.liu.pojo"/>
</typeAliases>
使用
<select id="getUserList" resultType="User">
select * from mybatis.user
</select>
在实体类比较少的时候,使用第一种
实体类比较多的时候,推荐第二种
第一种可以DIY别名,第二种改名需要在实体类上加注解
5.设置
看文档,要记得不多,需要用到的时候看官方文档就好
6.其他配置
插件,类型处理器,对象工厂
7.映射器
注册绑定Mapper文件
1.使用资源路径(推荐使用)
<mapper resource="com/liu/dao/UserMapper"/>
2.通过class文件绑定
<mapper class="com.liu.dao.UserDao"/>
注意点
- 接口和他的Mapper配置文件必须同名
- 接口和他的Mapper配置文件在同一个包下
3.使用包扫描
<package name=""/>
注意点
- 接口和他的Mapper配置文件必须同名
- 接口和他的Mapper配置文件在同一个包下
8.生命周期和作用域
错误的使用会导致严重的并发问题
SqlSessionFactoryBuilder
- 一旦创建SQL sessionfactory,就不再需要它了
- 局部变量
SqlSessionfactory
- 可以想象成:数据库连接池
- SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或者重新创建另一个实例
- 应用作用域
- 最简单的就是使用单例模式或者静态单例模式
SqlSession
- 连接到连接池的一个请求
- 用完关闭,否则资源被占用
- 不是线程安全的,不能线程共享
这里的每一个Mapper,就代表一个具体的业务
5、解决属性名和字段名不一致的问题
1.新建一个项目,拷贝之前的,测试
private String password;
测试出现问题 ,password = null
解决方法:
1.起别名
select id,name,pwd as password from mybatis.user where id = #{id}
2.resultMap(很重要)
结果集映射
//id唯一绑定 ,type为结果集的类型
<resultMap id="UserMap" type="User">
//column数据库中的字段,property实体类中的属性
<result column="id" property="id"/> //数据库的字段和实体类中的字段相同可以不用写
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
- resultMap 元素是MyBatis种最重要最强大的元素
- 设计思想:对于简单的语句根部不需要配置显示的结果映射,而对于复杂的语句,只需要描绘他们的关系就好了
6、日志
6.1 日志工厂
如果一个数据库操作出现异常,需要排错。
logImpl | 指定MyBatis所用日志的具体实现 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING|STDOUT_LOGGING|NO_LOGGING |
- LOG4J 掌握
- STDOUT_LOGGING 掌握
在MyBatis中具体使用哪一个日志实现,在设置中设定
STDOUT_LOGGING标准日志输出
在mybatis核心配置文件中配置
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
6.2 Log4j
什么是Log4j?
- 是apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送目的地是控制台、文件、GUI组件
- 可以控制输出日志的格式
- 设置级别
- 通过配置文件来灵活配置
1.导包
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
2.在resources中新建 log4j.properties
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/log4j.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为日志实现
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4.log4j的使用,测试
简单使用
1.在要使用Log4j的类中导入包 import org.apache.log4j.Logger
2.日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(xxx.class);
3.日志级别
@Test
public void LogTest(){
logger.info("info---->");
logger.debug("debug--->");
logger.error("error---->");
}
7、分页
思考:为什么要分页
- 减少数据的处理量
7.1 使用:Limit分页
select * from tables limit startIndex,pageSize;
使用MyBatis实现分页,核心SQL
1.接口
//分页
List<User> getUserLimit(Map<String,Integer> map);
2.Mapper.xml
<select id="getUserLimit" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
</select>
3.测试
@Test
public void LimitTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper =sqlSession.getMapper(UserMapper.class);
Map map = new HashMap<String,Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> users=userMapper.getUserLimit(map);
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
7.2 RowBounds分页(不建议使用)
不再使用SQL实现分页
1.接口
List<User> getUserByRowBounds();
2.mapper.xml
<select id="getUserByRowBounds" resultMap="UserMap">
select * from mybatis.user
</select>
3.测试
RowBounds row = new RowBounds(1,2);
List<User> userList=sqlSession.selectList("com.liu.dao.UserMapper.getUserByRowBounds",null,row);
7.3 分页插件
PageHelper 了解即可
8、使用注解开发
8.1 面向接口编程
解耦,可扩展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,是得开发变得容易,规范性更好
关于接口得理解
- 是定义(规范,约束)与实现(名实分离得原则) 的分离
- 反映系统设计人员对系统的抽象理解
- 两类接口
- 对一个个体的抽象,它对应一个抽象体
- 对一个个体某一方面的抽象理解,形成一个抽象面
- 一个个体可能有多个抽象面,抽象体与抽象面是有区别的
三个面向区别
- 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法
- 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现
- 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题更多的体现就是对系统整体的架构
8.2 注解开发
新建项目
底层主要应用反射
1.注解在接口上实现
@Select("select * from user")
List<User> getUser();
2.核心文件中绑定接口
<mappers>
<mapper class="com.liu.dao.UserMapper"/>
</mappers>
3.测试
本质:反射机制实现
底层:动态代理
mybatis详细的执行流程
8.3 CRUD
可以在工具类创建的时候自动提交事务
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
编写接口,注解
@Select("select * from user where id = #{id}")
User getUserById(@Param("id") int id);
@Insert("insert into user(id,name,pwd) values(#{id},#{name},#{pwd})")
int addUser(User user);
@Update("update user set name=#{name},pwd=#{pwd} where id = #{id}")
int updateUser(User user);
@Delete("delete from user where id = #{uid}")
int deleteUser(@Param("uid") int id);
测试
注意:我们必须要将接口注册到mybatis核心配置文件中
关于@Param("")注解
- 基本类型参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型,可以忽略,但是建议加上
- 在Sql中引用的就是@Param中设置的
#{} ${}区别
#{}很大程度防止sql注入
9、Lombok(公司用就用,公司不用就少用)
- java库
- 插件
- 构建工具
使用步骤
1.下载
settings->plugins->搜索lombok
2.导入jar
maven
3.使用:在实体类中加注解
@Date:无参构造,get,set,tostring,hashcode,equlas
@AllArgsConstructor 有参
@NOArgsConstructor 无参
10、多对一处理
多对一
- 多个学生对应一个老师
- 对于学生,关联 多个学生关联一个老师 多对一
- 对老师而言,集合
环境搭建
SQL
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师');
CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
新建06项目
环境搭建
2.新建实体类
3.建立mapper接口
4.建立Mapper.xml文件
5.核心配置文件绑定注册
6.测试
按照查询嵌套处理
<select id="getStudent" resultMap="StudentTeacher">
select * from mybatis.student
</select>
<resultMap id="StudentTeacher" 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 = #{tid}
</select>
按照结果嵌套处理
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name table_name
from student s,teacher t
where s.tid = t.id
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
回顾mysql多对一查询
- 子查询
- 连表查询
11、一对多处理
一个老师教导多个学生
对于老师而言,就是一对多
新建项目 和06相似
实体类
private int id;
private String name;
private int tid;
private int id;
private String name;
private List<Student> student;
按结果嵌套
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,s.name sname,t.name tname,t.id tid
from student s,teacher t
where s.tid=t.id and t.id=#{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="student" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
按查询嵌套
<select id="getTeacher" resultMap="Teacher2">
select * from mybatis.teacher where id = #{tid}
</select>
<resultMap id="Teacher2" type="Teacher">
<collection property="student" javaType="ArrayList" ofType="Student"
select="getStudentByTeacherId" column="id"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from mybatis.student where tid=#{tid}
</select>
小结
关联 association 多对一
集合collection 一对多
javaType & ofType
javaType 用来指定实体类中属性的类型
ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
注意点:
- 保证sql的可读性,尽量保证通俗易懂
- 注意一对多和多对一中,属性名和字段的问题
慢SQL
面试高频
- MySql引擎
- InnoDB底层原理
- 索引
- 索引优化
12、动态SQL
什么是动态sql:根据不同的条件生成不同的SQL语句
搭建环境
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
创建一个基础工程
1.导包
2.编写配置文件
3.编写实体类
4.编写接口,接口实现
5.测试环境
IF
满足test中的表达式就 追加sql语句,可以追加多条
<select id="queryBlogIF" parameterType="map" resultType="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>
choose(when,otherwise)
按顺序走,只选一个,一定有一个追加
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<choose>
<when test="title != null">
and title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
where
自动追加where
<select id="queryBlogIF" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<if test="title!=null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</where>
</select>
set
最后一个if里面的语句不要加 ,
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title!=null">
title=#{title},
</if>
<if test="author != null">
author=#{author},
</if>
<if test="views != null">
views=#{views}
</if>
</set>
where id =#{id}
</update>
trim
很少用,可以自定义
动态SQL本质还是SQL语句,只是我们在sql层面,去执行一个逻辑代码
SQL片段
将一些抽取出来,方便复用
1使用SQL标签抽取公共的部分
2.在需要的地方使用include标签引用即可
<sql id="xxx">
<if test="">
</if>
<if test="">
</if>
</sql>
<select id="" parameterType="" resultType="">
select * from mybatis.blog
<where>
<include refid="xxx"></include>
</where>
</select>
注意事项:
- 最好基于单表来定义
- 不要存在where标签
Foreach
<select id="" paramterType="map" resultType="blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="(" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>
动态SQL就是在拼接SQL
建议:
- 先写好完整的sql,再根据格式修改成动态SQL
13、缓存
13.1 简介
查询 : 连接数据库 :耗资源
一次查询的结果暂存到一个可以取到的地方->内存:缓存
1.什么是缓存
- 存在内存中的临时数据
- 将用户经常查询的数据放在缓存中。提高查询效率
2.为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率
3.什么样的数据能使用缓存
- 经常查询并且不经常改变的数据
13.2 MyBatis缓存
- 默认定义了两种缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启,(SqlSession级别的缓存,也称本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace 级别的缓存
- 为了提高扩展性,mybatis定义了缓存接口cache,我们可以通过实现Cache接来定义二级缓存
13.3 一级缓存
- 一级缓存也叫本地缓存
- 与数据库同义词会话期间查询到的数据会放在本地缓存中
- 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
测试:
开启日志
在一个session中查询两次相同的数据
走了一次sql,两个对象是一致的
缓存失效情况:
1.查询不同的东西
2.增删改操作,可能会改变原来的数据,所以必定会刷新缓存
3.查询不同的Mapper.xml
4.手动清理缓存sqlSession.clearCeche();
小结:一级缓存是默认开启的,只在一次sqlsession中有效,也就是拿到连接到关闭连接这个区间段
13.4 二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间对应一个二级缓存
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果当前会话关闭了,这个会话对应的一级缓存就没了,但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中
- 新的会话查询信息,就可以从二级缓存中获取内容
- 不同的mapper查出的数据会放在自己对应的缓存(map)中
步骤:
1.开启全局缓存 核心配置文件中
<setting name="cacheEnabled" value="true"/>
2.在接口xml文件中开启二级缓存
<cache />
也可以自定义参数
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
问题:实体类序列化
解决:
public class User implements Serializable{
}
小结:开启了二级缓存,在同一个Mapper下有效
所有的数据都会在一级缓存中
只有当会话提交或者关闭,才会提交到二级缓存
13.5 缓存原理
顺序
1.先看二级缓存
2.找一级缓存
3.查询数据库
13.6 自定义缓存-ehcache
1.导包 maven mybatis-ehcache
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
3.ehcache.xml配置文件