系列文章目录
Spring学习笔记 之 Spring
https://blog.youkuaiyun.com/weixin_43985478/article/details/124411746?spm=1001.2014.3001.5501
Spring学习笔记 之 Spring-MVC
https://blog.youkuaiyun.com/weixin_43985478/article/details/124923196?spm=1001.2014.3001.5501
目录
- 一、MyBatis
- MyBatis 和 Hibernate的区别?
- MyBatis的优点
- MyBatis的缺点
- 二、MyBatis ⼊⻔
- 使用原生接口
- Mapper 代理实现⾃定义接⼝
- Mapper.xml 常⽤配置
- 三、单表查询
- paramterType
- resultType
- 四、多表关联查询
- ⼀对多
- collection 和 association 的区别
- 多对多
- 五、MyBatis逆向工程
- 逆向⼯程概念
- 使⽤逆向⼯程
- 六、MyBatis 延迟加载
- 七、MyBatis 缓存
- ⼀级缓存
- ⼆级缓存
- ⾃带⼆级缓存
- 第三⽅ ehcache ⼆级缓存
- 八、MyBatis 动态 SQL
- 静态实现
- 动态实现
一、MyBatis
主流的ORM(对象关系映射)框架,之前叫做 iBatis,后来更名为 MyBatis,实现数据持久化的框架同时 Java,.NET,Ruby 三种语⾔,MyBatis 是⼀个对 JDBC 进⾏封装的框架
MyBatis 和 Hibernate的区别?
- Hibernate 是⼀个“全⾃动化” ORM 框架,MyBatis 是⼀个“半⾃动化的”ORM框架
- 全⾃动化:开发者只需要调⽤相关接⼝就可以完成操作,整个流程框架都已经进⾏了封装
- Hibernate 实现了 POJO 和数据库表之间的映射,同时可以⾃动⽣成 SQL 语句并完成执⾏
- 半⾃动化:框架只提供⼀部分功能,剩下的⼯作仍需要开发者⼿动完成,MyBatis 没有提供 POJO 与数据库表的映射,只实现了 POJO 与 SQL 之间的映射关系,需要开发者⾃定义 SQL 语句,以及数据与POJO 之间的装配关系
- “半⾃动化”的⽅式提⾼了框架的灵活性,开发者可以根据具体的业务需求,完成定制化的持久层解决⽅案
- MyBatis 对所有的 JDBC 进⾏了封装,包括参数设置、SQL 执⾏、结果集解析等,通过 XML 配置/注解的⽅式完成 POJO 与数据的映射
MyBatis的优点
- 极⼤简化了 JDBC 代码的开发
- 简单好⽤、容易上⼿、具有更好的灵活性
- 通过将 SQL 定义在 XML 中的⽅式降低程序的耦合性
- ⽀持动态 SQL,可以根据具体业务需求灵活实现功能
MyBatis的缺点
- 相⽐于 Hibernate,开发者需要完成更多⼯作,⽐如定义 SQL、设置 POJO 与数据的映射关系等
- 要求开发⼈员具备⼀定的 SQL 编写能⼒,在⼀些特定场景下⼯作量⽐较⼤
- 数据库移植性差,以为 SQL 依赖于底层数据库,如果要进⾏数据库迁移,部分 SQL 需要重写编写
- ⾃⼰编写 SQL
- ⾃⼰完成数据库数据与 POJO 的映射
二、MyBatis ⼊⻔
1、创建 Maven ⼯程,pom.xml 引⼊相关依赖
<dependencies>
<!--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.29</version>
</dependency>
</dependencies>
2、创建实体类
import lombok.Data;
/**
* @Author 苦恼的java小宝
* @Date 2022/6/17 12:11
* @ClassName: People
* @Version 1.0
*/
@Data
public class People {
private Integer id;
private String name;
private Double money;
}
3、配置 MyBatis 环境,在 resources 路径下创建 confifig.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>
<!-- 配置 MyBatis 运⾏环境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!--连接本地sql服务的test库-->
<property name="url" value="jdbc:mysql://localhost:3306/test?
useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
</configuration>
- 使⽤原⽣接⼝
- Mapper 代理实现⾃定义接⼝
使用原生接口
1、在mapper包中创建 Mapper ⽂件 PeopleMapper.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.hqq.mapper.PeopleMapper">
<select id="findById" parameterType="java.lang.Integer" resultType="com.hqq.entity.People">
select * from people where id = #{id};
</select>
</mapper>
- namespace 设置为PeopleMapper.xml⽂件所在包名 + ⽂件名
- parameterType 是参数数据类型
- resultType 是返回值数据类型
2、在全局配置⽂件 confifig.xml 中注册 PeopleMapper.xml
<!--注册Mapper-->
<mappers>
<!--这里要用斜/,而不是用点.-->
<mapper resource="com/hqq/mapper/PeopleMapper.xml"></mapper>
</mappers>
3、调⽤ API 完成操作
public class Test {
public static void main(String[] args) {
//加载MyBatis配置文件
InputStream inputStream = Test.class.getClassLoader().getResourceAsStream("config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sessionFactory = builder.build(inputStream);
//获取SqlSession
SqlSession sqlSession = sessionFactory.openSession();
//调用MyBatis原生接口执行SQL语句
String statement = "com.hqq.mapper.PeopleMapper.findById";
People people = sqlSession.selectOne(statement);
System.out.println(people);
sqlSession.close();
}
}
4、IDEA 中⽆法直接读取 非resources 路径下的 XML ⽂件,需要进⾏设置,pom.xml
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<!--**/*.xml,表示所有路径下的所有xml文件-->
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
Mapper 代理实现⾃定义接⼝
1.自定义PeoPleRepository接口
public interface PeopleRepository {
//增删改查
public int save(People people);
public int deleteById(Integer id);
public int update(People people);
public People findById(Integer id);
public List<People> FindAll();
}
MyBatis 会⾃动根据规则创建 PeopeleRepository 接⼝的 实现类代理对象
- PeopleMapper.xml 中的 namespace 为PeoPleRepository接⼝的全限定类名
- PeopleMapper.xml 中的 statement 的 id 为接⼝中对应的⽅法名
- statement可分为insert、delete、update、select
- PeopleMapper.xml 中的 parameterType 和接⼝中对应⽅法的参数类型⼀致
- PeopleMapper.xml 中的 resultType 和接⼝中对应⽅法的返回值类型⼀致
<?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.hqq.repository.PeopleRepository">
<!--增-->
<insert id="save" parameterType="com.hqq.entity.People">
insert into people(name,money) values(#{name},#{money})
</insert>
<!--删-->
<delete id="deleteById" parameterType="java.lang.Integer">
delete from people where id = #{id}
</delete>
<!--改-->
<update id="update" parameterType="com.hqq.entity.People">
update people set name = #{name},money = #{money} where id = #{id}
</update>
<!--查-->
<select id="findById" parameterType="java.lang.Integer" resultType="com.hqq.entity.People">
select * from people where id = #{id}
</select>
<select id="findAll" resultType="com.hqq.entity.People">
select * from people
</select>
</mapper>
3、config.xml完成注册
<!--注册Mapper-->
<mappers>
<mapper resource="com/hqq/repository/PeopleRepository.xml"></mapper>
</mappers>
4、调⽤API
public class Test {
public static void main(String[] args) {
//加载MyBatis配置文件
InputStream inputStream = Test.class.getClassLoader().getResourceAsStream("config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sessionFactory = builder.build(inputStream);
//获取SqlSession
SqlSession sqlSession = sessionFactory.openSession();
//获取实现了自定义接口的代理对象
PeopleRepository peopleRepository = sqlSession.getMapper(PeopleRepository.class);
System.out.println("-------------执行前---------------");
//执行前
List<People> list1 = peopleRepository.findAll();
for(People p :list1){
System.out.println(p);
}
//增
People p1 = new People(4,"大聪明",5.5);
peopleRepository.save(p1);
//删
peopleRepository.deleteById(1);
//改
People p2 = new People(2,"李逍遥",888.8);
peopleRepository.update(p2);
//查
//System.out.println(peopleRepository.findById(2));
System.out.println("---------------执行后----------------");
List<People> list2 = peopleRepository.findAll();
for(People p :list2){
System.out.println(p);
}
sqlSession.close();
}
}
结果:
Mapper.xml 常⽤配置
- 全局环境配置⽂件(数据源、事务管理、Mapper 注册、打印 SQL、惰性加载、⼆级缓存)
- Mapper 配置⽂件(定义⾃定义接⼝的具体实现⽅案:SQL、数据与 POJO 的映射)
三、单表查询
<select id="findById" parameterType="java.lang.Integer"
resultType="com.hqq.entity.People">
select * from people where id = #{id}
</select>
- 业务:通过 id 查询 People 对象
- ⽬标表:test/people
- 实体类:com.hqq.entity.People
- Mapper.xml 设置相关配置逻辑,由 MyBatis ⾃动完成查询,⽣成 POJO。
- statement 标签主要属性有 id、parameterType、resultType
- id 对应接⼝的⽅法名,parameterType 定义参数的数据类型、resultType 定义查询结果的数据类型 (实体类的成员变量列表必须与⽬标表的字段列表⼀致)
paramterType
⽀持基本数据类型、包装类、String、多参数、POJO 等
1、基本数据类型,通过 id 查询 POJO
//接口方法
public People findById(int id);
//配置信息
<select id="findById" parameterType="int"
resultType="com.hqq.entity.People">
select * from people where id = #{num}
</select>
2、包装类
//接口方法
public People findById(Integer id);
//配置信息
<select id ="findById" parameterType="int" resultType ="com.hqq.entity.People">
select * from people where id = #{num}
</select>
3、String类型
//接口方法
public People findByName(String name);
//配置信息
<select id="findByName" parameterType="java.lang.String"
resultType="com.hqq.entity.People">
select * from people where name = #{name}
</select>
4、多参数
//接口方法
public People findByIdAndName(Integer id,String name);
//配置信息
<select id="findByIdAndName" resultType="com.hqq.entity.People">
select * from people where id = #{id} and name = #{name}
</select>
5、POJO
//接口方法
public int update(People people);
//配置信息
<update id="update" parameterType="com.hqq.entity.People">
update people set name = #{name},money = #{money} where id = #{id}
</update>
resultType
resultType 与 parameterType 的使⽤基本⼀致
四、多表关联查询
⼀对多
1、建表
create table `t_classes`(
`id` int(11) NOT NULL primary key auto_increment,
`name` varchar(11) default null
);
create table `t_student`(
`id` int(11) not null primary key auto_increment,
`name` varchar(11) default null,
`cid` int(11) default null,
key `cid` (`cid`),
constraint `t_student_ibfk_1` foreign key (`cid`) references `t_classes`(`id`)
)
2、SQL
select s.id sid,s.name sname,c.id cid,c.name cname
from t_student s,t_classes c
where s.id = 1 and s.cid = c.id
3、创建实体类
//学生类
@Data
public class Student {
private Integer id;
private String name;
private Classes classes;
}
//教室类
@Data
public class Classes {
private Integer id;
private String name;
private List<Student> students;
}
4、StudentRepository
public interface StudentRepository {
public Student findById(Integer id);
}
5、StudentRepository.xml
- resultType 直接将结果集与实体类进行映射
- resultMap可以对接过进行二次封装,根据需求映射
- id 表示主键
- result 表示其他属性
<?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.hqq.repository.StudentRepository">
<resultMap id="studentMap" type="com.hqq.entity.Student">
<id column="sid" property="id"></id>
<result column="sname" property="name"></result>
<association property="classes" javaType="com.hqq.entity.Classes">
<id column="cid" property="id" ></id>
<result column="cname" property="name" ></result>
</association>
</resultMap>
<select id="findById" parameterType="java.lang.Integer" resultMap="studentMap">
select s.id sid,s.name sname,c.id cid,c.name cname
from t_student s,t_classes c
where s.id = #{id} and s.cid = c.id
</select>
</mapper>
6、ClassesRepository
public interface ClassesRepository {
public Classes findById(Integer id);
}
7、ClassesRepository.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.hqq.repository.ClassesRepository">
<resultMap id="classesMap" type="com.hqq.entity.Classes">
<id property="id" column="cid"></id>
<result property="name" column="cname"></result>
<collection property="students" ofType="com.hqq.entity.Student">
<id property="id" column="sid"></id>
<result property="name" column="sname"></result>
</collection>
</resultMap>
<select id="findById" parameterType="java.lang.Integer" resultMap="classMap">
select c.id cid,c.name cname,s.id sid,s.name sname
from t_classes c,t_student s
where c.id = #{id} and c.id = s.cid
</select>
</mapper>
collection 和 association 的区别
- collection 是将结果集封装成⼀个集合对象(多个⽬标对象)
- association 是将结果集封装成⼀个实体类的对象(⼀个⽬标对象)
- collection 是通过 ofType 设置数据类型,association 是通过 javaType 设置数据类型
多对多
1、建表
create table `t_account`(
`id` int(11) not null primary key auto_increment,
`name` varchar(11) default null
);
create table `t_course`(
`id` int(11) not null primary key auto_increment,
`name` varchar(11) default null
);
create table `account_course`(
`id` int(11) not null primary key auto_increment,
`aid` int(11) default null,
`cid` int(11) default null,
key `aid`(`aid`),
key `cid`(`cid`),
constraint `account_course_ibfk_1` foreign key (`aid`) references
`t_account`(`id`),
constraint `account_course_ibfk_2` foreign key (`cid`) references
`t_course`(`id`)
);
2、创建实体类
@Data
public class Account {
private Integer id;
private String name;
private List<Course> courses;
}
@Data
public class Course {
private Integer id;
private String name;
private List<Account> accounts;
}
3、AccountRepository
public interface AccountRepository {
public Account findById(Integer id);
}
4、AccountRepository.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.hqq.repository.AccountRepository">
<resultMap id="accountMap" type="com.hqq.entity.Account">
<id property="id" column="aid"></id>
<result property="name" column="aname"></result>
<collection property="courses" ofType="com.hqq.entity.Course">
<id property="id" column="cid"></id>
<result property="name" column="cname"></result>
</collection>
</resultMap>
<select id="findById" parameterType="java.lang.Integer" resultMap="accountMap">
select a.id aid,a.name aname,c.id cid,c.name cname
from t_account a,account_course ac,t_course c
where a.id = #{id} and a.id = ac.aid and c.id =ac.cid
</select>
</mapper>
5、CourseRepository
public interface CourseRepository {
public Course findById(Integer id);
}
6、CourseRepository.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.hqq.repository.CourseRepository">
<resultMap id="courseMap" type="com.hqq.entity.Course">
<id column="cid" property="id"></id>
<result column="cname" property="name"></result>
<collection property="accounts" ofType="com.hqq.entity.Account">
<id column="aid" property="id"></id>
<result column="aname" property="name"></result>
</collection>
</resultMap>
<select id="findById" parameterType="java.lang.Integer" resultMap="courseMap">
select a.id aid,a.name aname,c.id cid,c.name cname
from t_account a,account_course ac,t_course c
where c.id = #{id} and a.id = ac.aid and c.id = ac.cid
</select>
</mapper>
五、MyBatis逆向工程
逆向⼯程概念
逆向⼯程是 MyBatis 提供的⼀种⾃动化配置⽅案,针对数据表⾃动⽣成 MyBatis 所需要的各种资源 (实体类、Mapper 接⼝、Mapper.xml),但是逆向⼯程只针对于单表,如果数据表之间有级联关系,逆向⼯程⽆法⾃动⽣成级联关系
使⽤逆向⼯程
MyBatis 逆向⼯程的组件是 MyBatis Generator,简称 MBG,是专为 MyBatis 框架定制的代码⾃动⽣成解决⽅案,MBG 可以根据数据表结构快速⽣成对应的实体类、Mapper接⼝、Mapper.xml,并且⽀持基本的 CRUD 操作,但是业务逻辑相对复杂的操作就需要开发者⼿动完成
1、创建 Maven ⼯程,pom.xml 添加相关依赖
<dependencies>
<!--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.29</version>
</dependency>
<!--逆向-->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.1</version>
</dependency>
</dependencies>
create table `account`(
`id` int(11) not null primary key auto_increment,
`name` varchar(11),
`password` varchar(11),
`age` int(11)
)
- jdbcConnection 配置数据库连接信息
- javaModelGenerator 配置 JavaBean 的⽣成策略
- sqlMapGenerator 配置 SQL 映射⽂件⽣成策略
- javaClientGenerator 配置 Mapper 接⼝的⽣成策略
- table 配置要逆向解析的数据表(tableName:表名,domainObjectName;实体类名)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--id自定义 targetRuntime 与MyBatis版本号有关-->
<context id="generator" targetRuntime="MyBatis3">
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8"
userId="root"
password="root"></jdbcConnection>
<javaModelGenerator targetPackage="com.hqq.entity" targetProject="./src/main/java"></javaModelGenerator>
<sqlMapGenerator targetPackage="com.hqq.repository" targetProject="./src/main/java"></sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.hqq.repository" targetProject="./src/main/java"></javaClientGenerator>
<!-- domainObjectName表示生成的实体类 叫Account-->
<table tableName="account" domainObjectName="Account"></table>
</context>
</generatorConfiguration>
4、创建 GeneratorMain 类,执⾏⾃动⽣成资源的代码
package com.hqq.test;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* @Author 苦恼的java小宝
* @Date 2022/6/18 22:27
* @ClassName: Test_0
* @Version 1.0
*/
public class GeneratorMain {
public static void main(String[] args) {
//执行过程中的警告信息
List<String> warings = new ArrayList<String>();
//生成的代码重复时,覆盖原代码
boolean overwrite = true;
//读取配置文件
String genCig = "/generatorConfig.xml";
File configFile =
new File(GeneratorMain.class.getResource(genCig).getFile());
ConfigurationParser configurationParser = new ConfigurationParser(warings);
Configuration configuration = null;
try {
configuration = configurationParser.parseConfiguration(configFile);
} catch (IOException e) {
e.printStackTrace();
} catch (XMLParserException e) {
e.printStackTrace();
}
//覆盖原代码
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = null;
try {
//创建代码生成器
myBatisGenerator = new MyBatisGenerator(configuration, callback, warings);
} catch (InvalidConfigurationException e) {
e.printStackTrace();
}
try {
//执行生成代码
myBatisGenerator.generate(null);
} catch (SQLException throwables) {
throwables.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
六、MyBatis 延迟加载
在进行数据查询时,为了提高数据库查询性能,尽量使用单表查询,因为单表查询比多表关联查询速度要快
如果查询单表就可以满足需求,一开始先查询单表,当需要关联信息时,再关联查询,当需要关联信息再查询这个叫延迟加载,又称为懒加载
1、创建实体类
@Data
public class Customer {
private Integer id;
private String name;
private List<Order> orders;
}
@Data
public class Order {
private Integer id;
private String name;
private Customer customer;
}
2、confifig.xml 中开启打印 SQL
<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
3、创建 OrderRepository
public interface OrderRepository {
public Order findById(Integer id);
}
<?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.hqq.repository.OrderRepository">
<resultMap id="orderMap" type="com.hqq.entity.Order">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<association
property="customer"
javaType="com.hqq.entity.Customer"
<!-- cid 的值作为findById的参数 返回对象-->
select="com.hqq.repository.CustomerRepository.findById"
column="cid"
></association>
</resultMap>
<select id="findById" parameterType="java.lang.Integer"
resultMap="orderMap">
select * from orders where id = #{id}
</select>
</mapper>
4、创建 CustomerRepository
public interface CustomerRepository {
public Customer findById(Integer id);
}
<?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.hqq.repository.CustomerRepository">
<select id="findById" parameterType="java.lang.Integer"
resultType="com.hqq.entity.Customer">
select * from customer where id = #{id}
</select>
</mapper>
<!-- 延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
6、测试
public class Test {
public static void main(String[] args) {
//加载MyBatis配置文件
InputStream inputStream = Test.class.getClassLoader().getResourceAsStream("config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sessionFactory = builder.build(inputStream);
//获取SqlSession
SqlSession sqlSession = sessionFactory.openSession();
//获取实现了自定义接口的代理对象
OrderRepository orderRespository = sqlSession.getMapper(OrderRepository.class);
Order order = orderRespository.findById(3);
System.out.println(order.getCustomer().getName());
sqlSession.close();
}
}
MyBatis 延迟加载机制,是实际开发中使⽤频率较⾼的⼀个功能,正确地使⽤延迟加载,可以有效减少Java Application 和数据库的交互次数,从⽽提⾼整个系统的运⾏效率,延迟加载是为了提⾼程序运⾏效率的⼀种⼿段,⼀般应⽤于多表关联查询的业务场景
七、MyBatis 缓存
使⽤缓存的作⽤也是为了减少 Java Application 与数据库的交互次数,从⽽提升程序的运⾏效率
⼀级缓存
MyBatis ⾃带⼀级缓存,并且是⽆法关闭的,⼀直存在,⼀级缓存的数据存储在 SqlSession 中
即使⽤同⼀个 SqlSession 进⾏查询操作的时候,⼀级缓存存在,如果使⽤多个 SqlSession 进⾏查询操作,⼀级缓存不存在,缓存只针对于查询
如果 SqlSession 执⾏了增、删、改操作,MyBatis 会动清空 SqlSession 缓存中的数据,以此来保证数据的⼀致性
1、实体类
@Data
public class MyClass {
private Integer id;
private String name;
}
2、Mapper.java
public interface MyClassRepository {
public MyClass findById(Integer id);
}
3、Mapper.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.hqq.repository.MyClassRepository">
<select id="findById" parameterType="java.lang.Integer"
resultType="com.hqq.entity.MyClass">
select * from t_classes where id = #{id}
</select>
</mapper>
public class Test_0 {
public static void main(String[] args) {
InputStream inputStream =
Test_0.class.getClassLoader().getResourceAsStream("config.xml");
//FactoryBuilder -> Factory -> Session -> Repository
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
MyClassRepository myClassRepository = sqlSession.getMapper(MyClassRepository.class);
MyClass myClass = myClassRepository.findById(1);
System.out.println(myClass);
MyClass myClass1 = myClassRepository.findById(1);
System.out.println(myClass1);
}
}
结果:sql语句只会执行一次,第二次不用查询语句直接返回
⼆级缓存
MyBatis ⼆级缓存是⽐⼀级缓存作⽤域更⼤的缓存机制,它是 Mapper 级别的,只要是同⼀个 Mapper,⽆论使⽤多少个 SqlSession,数据都是共享的
MyBatis ⼆级缓存默认是关闭的,需要使⽤时可以通过配置⼿动开启
MyBatis 可以使⽤⾃带的⼆级缓存,也可以使⽤第三⽅ ehcache ⼆级缓存
⾃带⼆级缓存
1、confifig.xml 中配置开启⼆级缓存
<settings>
<!-- 开启⼆级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
2、Mapper.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.hqq.repository.MyClassRepository">
<!--配置二级缓存-->
<cache></cache>
<select id="findById" parameterType="java.lang.Integer"
resultType="com.hqq.entity.MyClass">
select * from t_classes where id = #{id}
</select>
</mapper>
3、实体类实现 Serializable 接⼝
@Data
public class MyClass implements Serializable {
private Integer id;
private String name;
}
4、测试
public class Test6 {
public static void main(String[] args) {
InputStream inputStream =
Test2.class.getClassLoader().getResourceAsStream("config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
MyClassRepository myClassRepository = sqlSession.getMapper(MyClassRepository.class);
MyClass myClass = myClassRepository.findById(1);
System.out.println(myClass);
sqlSession.close();
sqlSession = sqlSessionFactory.openSession();
myClassRepository = sqlSession.getMapper(MyClassRepository.class);
MyClass myClass2 = myClassRepository.findById(1);
System.out.println(myClass2);
}
}
只会执行一次SQL语句
第三⽅ ehcache ⼆级缓存
1、pom.xml
<!-- ehcache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.0</version>
</dependency>
2、resources 路径下创建 ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<diskStore/>
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
3、confifig.xml 中配置⼆级缓存
<settings>
<!-- 开启⼆级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
4、Mapper.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.hqq.repository.MyClassRepository">
<!-- 开启⼆级缓存 -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache" >
<!-- 缓存创建以后,最后⼀次访问缓存的时间⾄失效的时间间隔 -->
<property name="timeToIdleSeconds" value="3600"/>
<!-- 缓存⾃创建时间起⾄失效的时间间隔-->
<property name="timeToLiveSeconds" value="3600"/>
<!-- 缓存回收策略,LRU移除近期最少使⽤的对象 -->
<property name="memoryStoreEvictionPolicy" value="LRU"/>
</cache>
<select id="findById" parameterType="java.lang.Integer"
resultType="com.hqq.entity.MyClass">
select * from t_classes where id = #{id}
</select>
</mapper>
5、实体类不需要实现序列化接⼝
@Data
public class MyClass {
private Integer id;
private String name;
}
6、测试如上
只会执行一次SQL语句
八、MyBatis 动态 SQL
静态实现
User类
@Data
public class User {
private Integer id;
private String userName;
private String password;
private Integer age;
}
需求
- 通过 id 和 username 查询 User
- 通过 username 和 password 查询 User
- 通过 password 和 age 查询 User
UserRepository 接口
public interface UserRepository {
public User findByUser1(User user);
public User findByUser2(User user);
public User findByUser3(User user);
}
UserRepository.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.hqq.repository.UserRepository">
<select id="findByUser1" parameterType="User" resultType="User">
select * from t_user where id = #{id} and username = #{username}
</select>
<select id="findByUser2" parameterType="User" resultType="User">
select * from t_user where username = #{username} and password = #{password}
</select>
<select id="findByUser3" parameterType="User" resultType="User">
select * from t_user where password = #{password} and age = #{age}
</select>
</mapper>
动态实现
select * from t_user
where
id = #{id}
and username = #{username}
and password = #{password}
and age = #{age}
UserRepository
public interface UserRepository {
public User findByUser(User user);
}
UserRepository.xml (if标签)
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hqq.repository.UserRepository">
<select id="findByUser" parameterType="com.hqq.entity.User"
resultType="com.hqq.entity.User">
select * from t_user
<where>
<if test="id!=null">
id = #{id}
</if>
<if test="username!=null">
and username = #{username}
</if>
<if test="password!=null">
and password = #{password}
</if>
<if test="age!=null">
and age = #{age}
</if>
</where>
</select>
</mapper>
- choose、when标签
<select id="findByUser" parameterType="com.hqq.entity.User"
resultType="com.hqq.entity.User">
select * from t_user
<where>
<choose>
<when test="id!=null">
id = #{id}
</when>
<when test="username!=null">
and username = #{username}
</when>
<when test="password!=null">
and password = #{password}
</when>
<when test="age!=null">
and age = #{age}
</when>
</choose>
</where>
</select>
- trim标签
通过设置 prefifix 和 suffix 参数来完成使⽤的
<select id="findByUser" parameterType="com.hqq.entity.User"
resultType="com.hqq.entity.User">
select * from t_user
<trim prefix="where" prefixOverrides="and">
<if test="id!=null">
id = #{id}
</if>
<if test="username!=null">
and username = #{username}
</if>
<if test="password!=null">
and password = #{password}
</if>
<if test="age!=null">
and age = #{age}
</if>
</trim>
</select>
- set
set 标签⽤于 Update 操作,会⾃动根据参数选择⽣成 SQL 语句
UserRepository
public interface UserRepository {
public int update(User users);
}
UserRepository.xml
<update id="update" parameterType="com.hqq.entity.User">
update t_user
<set>
<if test="username!=null">
username = #{username},
</if>
<if test="password!=null">
password = #{password},
</if>
<if test="age!=null">
age = #{age}
</if>
</set>
where id = #{id}
</update>