Mybatis笔记
Mybatis笔记
Huang_ChunMyBatis是一款优秀的持久层框架,用于简化JDBC的开发。
mybatis入门
lombok
是一个实用的java类库,能通过注解的形式自动生成构造器,getter、setter、equals、toString等方法,并可以自动化生成日志变量,简化java开发,提高效率。
mybatis对数据库的增删改查
根据主键删除
- 预编译SQL:性能更高,更安全(防止SQL注入)
新增
- 主键返回:在事件添加成功后,需要获取插入数据库事件的主键。
更新
查询(根据ID查询)
数据封装
- 实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装。
- 如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。
XML映射文件
多参数
@Param注解(命名参数)
可以不用arg0 arg1 param1 param2吗?这个map集合的key我们自定义可以吗?当然可以。使用@Param注解即可。这样可以增强可读性。
需求:根据name和age查询
StudentMapper接口
1
2
3
4
5
6
7 /**
* 根据name和age查询
* @param name
* @param age
* @return
*/
List<Student> selectByNameAndAge(int age); String name,
StudentMapper.xml
1
2
3 <select id="selectByNameAndAge" resultType="student">
select * from t_student where name = #{name} and age = #{age}
</select>
1
2
3
4
5
public void testSelectByNameAndAge(){
List<Student> stus = mapper.selectByNameAndAge("张三", 20);
stus.forEach(student -> System.out.println(student));
}
打印日志
yml格式:
1
2
3 mybatis:
configuration: # 配置打印 MyBatis⽇志
log-impl: org.apache.ibatis.logging.stdout.StdOutImplproperties格式:
1
2 #指定mybatis输出⽇志的位置, 输出控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
动态SQL
随着用户的输入或者外部条件的变化而变化的SQL语句,称为动态SQL。
:用于判断条件是否成立。使用test属性进行条件判断,如果条件为True,则拼接SQL。
:where元素只会在子元素有 内容的情况下插入where子句。而且会自动去除子句的开头的and或or。
:动态地在行首插入set关键字,并会删除额外的逗号。(用在update语句中)
循环 SQL片段
:定义可重用的SQL片段 :通过属性refid,制动包含的SQL片段。
高级映射
https://blog.csdn.net/liubopro666/article/details/135291676
环境搭建
数据库:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 create table clazz
(
cid int not null comment '班级编号'
primary key,
cname varchar(50) not null comment '班级名称'
);
create table student
(
sid int not null comment '学生编号'
primary key,
sname varchar(50) not null comment '学生名称',
cid int null comment '班级编号',
constraint student_ibfk_1
foreign key (cid) references clazz (cid)
);
create index cid
on student (cid);目录结构:
区分主表和从表:
原则:
谁在前,谁是主表
如:多对一,多在前,多是主表;一对多,一在前,一是主表。
多对一映射
com/hc/mybatisdemo/pojo/Student.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.hc.mybatisdemo.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author: 椿
* @date: 2024-08-29 15:48
* @description 学生类
*/
public class Student {
private Integer sid;
private String sname;
private Clazz clazz;
}
com/hc/mybatisdemo/pojo/Clazz.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package com.hc.mybatisdemo.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author: 椿
* @date: 2024-08-29 15:47
* @description 班级类
*/
public class Clazz {
private Integer cid;
private String cname;
}多种方式,常见的包括三种:
- 第一种方式:一条SQL语句,级联属性映射。
- 第二种方式:一条SQL语句,association。
- 第三种方式:两条SQL语句,分步查询。(这种方式常用:优点一是可复用。优点二是支持懒加载。)
懒加载(延迟加载)
:核心原理:用的时候再执行查询语句,不用的时候不查询。
作用:提高性能,尽可能的不查或者尽可能的少查,来提高效率。
在Mybatis中开启延迟加载:在associateion标签中添加fetchType=”lazy”属性,默认关闭,需设置。局部生效。
在实际开发中,大部分都是需要使用延迟加载的,所以建议开启全部的延迟加载机制,在mybatis核心配置添加全局配置lazyLoadingEnabled = true。
如果某一步不需要使用延迟加载,设置在associateion标签中添加fetchType=”eager”属性.
方式一:级联属性映射
com/hc/mybatisdemo/mapper/StudentMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<mapper namespace="com.hc.mybatisdemo.mapper.StudentMapper">
<!-- 第一种方式:一条SQL语句,级联属性映射。property:对应类中属性;column:数据库中字段-->
<resultMap id="studentResultMap" type="com.hc.mybatisdemo.pojo.Student">
<id property="sid" column="sid"></id>
<result property="sname" column="sname"></result>
<result property="clazz.cid" column="cid"></result>
<result property="clazz.cname" column="cname"></result>
</resultMap>
<!--根据id查询学生信息,包含班级信息-->
<select id="getStudentById" resultMap="studentResultMap" parameterType="java.lang.Integer">
select s.sid, s.sname, c.cid, c.cname
from mybatis_demo.student s
left join clazz c on c.cid = s.cid
where s.sid = #{cid}
</select>
</mapper>
com/hc/mybatisdemo/mapper/StudentMapper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package com.hc.mybatisdemo.mapper;
import com.hc.mybatisdemo.pojo.Student;
import org.apache.ibatis.annotations.Mapper;
/**
* @author: 椿
* @date: 2024-08-29 15:49
* @description
*/
public interface StudentMapper {
/**
* 根据Id查询学生,包含学生的班级信息
* @param cid
* @return
*/
Student getStudentById(Integer cid);
}
test:
1
2
3
4
5
void getStudentByIdTest(){
Student student = studentMapper.getStudentById(1);
System.out.println("student = " + student);
}方式二:association
com/hc/mybatisdemo/mapper/StudentMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 <!-- 第二种方式:一条SQL语句,association。-->
<resultMap id="studentResultMapAssociation" type="com.hc.mybatisdemo.pojo.Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<association property="clazz" javaType="com.hc.mybatisdemo.pojo.Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
</association>
</resultMap>
<select id="getStudentById4Association"
resultMap="studentResultMapAssociation"
parameterType="java.lang.Integer">
select s.sid, s.sname, c.cid, c.cname
from mybatis_demo.student s
left join clazz c on c.cid = s.cid
where s.sid = #{cid}
</select>
com.hc.mybatisdemo.mapper.StudentMapper
1
2
3
4
5
6 /**
* 方式二:根据Id查询学生,包含学生的班级信息
* @param cid
* @return
*/
Student getStudentById4Association(Integer cid);
test
1
2
3
4
5
void getStudentById4AssociationTest(){
Student student = studentMapper.getStudentById4Association(3);
System.out.println("student = " + student);
}第三种方式:两条SQL语句,分步查询。(推荐)
com/hc/mybatisdemo/mapper/StudentMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12 <!-- 第三种方式:两条SQL语句,分步查询。(这种方式常用:优点一是可复用。优点二是支持懒加载。)-->
<resultMap id="studentResultMapStep" type="com.hc.mybatisdemo.pojo.Student">
<id column="sid" property="sid"/>
<result property="sname" column="sname"/>
<association property="clazz" column="cid" select="com.hc.mybatisdemo.mapper.ClazzMapper.getClazz"/>
</resultMap>
<select id="getStudentByIdStep1"
resultMap="studentResultMapStep"
parameterType="java.lang.Integer">
select sid, sname,cid from student where sid = #{sid}
</select>
com/hc/mybatisdemo/mapper/StudentMapper.java
1
2
3
4
5
6 /**
* 方式三:分步查询 根据Id查询学生,包含学生的班级信息
* @param cid
* @return
*/
Student getStudentByIdStep1(Integer sid);
com/hc/mybatisdemo/mapper/ClazzMapper.xml
1
2
3
4
5
6
7
8
9
10
<mapper namespace="com.hc.mybatisdemo.mapper.ClazzMapper">
<select id="getClazz" resultType="com.hc.mybatisdemo.pojo.Clazz" parameterType="java.lang.Integer">
select * from clazz where cid = #{id}
</select>
</mapper>
com/hc/mybatisdemo/mapper/ClazzMapper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package com.hc.mybatisdemo.mapper;
import com.hc.mybatisdemo.pojo.Clazz;
import org.apache.ibatis.annotations.Mapper;
/**
* @author: 椿
* @date: 2024-08-29 15:49
* @description
*/
public interface ClazzMapper {
/**
* 根据班级Id查询班级信息
* @param id 编辑编号
* @return Clazz
*/
Clazz getClazz(Integer id);
}
test
1
2
3
4
5
void getStudentById4StepTest(){
Student student = studentMapper.getStudentByIdStep1(4);
System.out.println("student = " + student);
}
一对多映射
一对多的实现通常包括两种实现方式:
- 第一种方式:collection
- 第二种方式:分步查询
com/hc/mybatisdemo/pojo/Clazz.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package com.hc.mybatisdemo.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author: 椿
* @date: 2024-08-29 15:47
* @description 班级类
*/
public class Clazz {
private Integer cid;
private String cname;
/**
* 一个班对应的学生
*/
List<Student> studentList;
}第一种方式:collection
com/hc/mybatisdemo/mapper/ClazzMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14 <resultMap id="ClazzResultMap" type="com.hc.mybatisdemo.pojo.Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<collection property="studentList" ofType="com.hc.mybatisdemo.pojo.Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
</collection>
</resultMap>
<select id="getClazzById2Collection" resultMap="ClazzResultMap"
parameterType="java.lang.Integer">
select c.*, s.sid, s.sname from clazz c cross join student s on c.cid = s.cid where s.cid = #{cid}
</select>
com/hc/mybatisdemo/mapper/ClazzMapper.java
1
2
3
4
5
6 /**
* 根据Id查询班级,包含学生列表
* @param cid
* @return
*/
Clazz getClazzById2Collection(Integer cid);
test
1
2
3
4
5
void getClazzById2CollectionTest(){
Clazz clazz = clazzMapper.getClazzById2Collection(1001);
System.out.println("clazz = " + clazz);
}第二种方式:分步查询(推荐)
com/hc/mybatisdemo/mapper/ClazzMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12 <!--分步查询-->
<resultMap id="ClazzResultMapStep" type="com.hc.mybatisdemo.pojo.Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<collection property="studentList" column="cid" select="com.hc.mybatisdemo.mapper.StudentMapper.getListByCid"/>
</resultMap>
<select id="getClazzByStep" resultMap="ClazzResultMapStep" parameterType="java.lang.Integer">
select *
from clazz
where cid = #{cid}
</select>
com/hc/mybatisdemo/mapper/ClazzMapper.java
1
2
3
4
5
6
7 /**
* 分步查询
* 根据cid查询Id查询班级,包含学生列表
* @param cid
* @return
*/
Clazz getClazzByStep(Integer cid);
com/hc/mybatisdemo/mapper/StudentMapper.xml
1
2
3 <select id="getListByCid" resultType="com.hc.mybatisdemo.pojo.Student" parameterType="java.lang.Integer">
select * from student where cid = #{cid}
</select>
com/hc/mybatisdemo/mapper/StudentMapper.java
1
2
3
4
5
6 /**
* 根据班级编号查询
* @param cid
* @return
*/
List<Student> getListByCid(Integer cid);test
1
2
3
4
5
void getClazzByStepTest(){
Clazz clazz = clazzMapper.getClazzByStep(1001);
System.out.println("clazz = " + clazz);
}
多对多关联查询
多对多关联查询通常涉及到三个或更多的表,其中至少有两个表之间存在多对多的关联关系。以下是一个示例,展示了如何在 MyBatis 中使用多对多关联查询:
MyBatis多对多关联查询本质就是两个一对多关联查询。例如有老师类和班级类:一个老师对应多个班级,也就是老师类中有一个班级集合属性。一个班级对应多个老师,也就是班级类中有一个老师集合属性。现在通过一个示例,将查询班级的时候,把同学和老师都查出来:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 <resultMap id="classesMapper" type="com.zhangsan.pojo.Classes">
<id property="cid" column="cid"></id>
<result property="className" column="className"></result>
<!-- 集合列 property:属性名 column:关联列名 ofType:集合的泛型 -->
<collection property="studentList" column="classId" ofType="com.zhangsan.pojo.Student">
<id property="sid" column="sid"></id>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
</collection>
<collection property="teacherList" column="cid" ofType="com.zhangsan.pojo.Teacher">
<id property="tid" column="tid"></id>
<result property="tname" column="tname"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="classesMapper">
select *
from classes
left join student
on classes.cid = student.classId
left join classes_teacher
on classes.cid = classes_teacher.cid
left join teacher
on classes_teacher.tid = teacher.tid;
</select>
缓存
介绍
缓存:cache
缓存的作用:通过减少IO的方式,来提高程序的执行效率。
mybatis的缓存:将select语句的查询结果放到缓存(内存)当中,下一次还是这条select语句的话,直接从缓存中取,不再查数据库。一方面是减少了IO。另一方面不再执行繁琐的查找算法。效率大大提升。
mybatis缓存包括:
- 一级缓存:将查询到的数据存储到SqlSession中。
- 二级缓存:将查询到的数据存储到SqlSessionFactory中。
- 或者集成其它第三方的缓存:比如EhCache【Java语言开发的】、Memcache【C语言开发的】等。
缓存只针对于DQL语句,也就是说缓存机制只对应select语句。
一级缓存
作用域默认为sqlSession.
一级缓存默认是开启的。不需要做任何配置。
原理:只要使用同一个SqlSession对象执行同一条SQL语句,就会走缓存。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 package com.powernode.mybatis.mapper;
import com.powernode.mybatis.pojo.Car;
public interface CarMapper {
/**
* 根据id获取Car信息。
* @param id
* @return
*/
Car selectById(Long id);
}
1
2
3
4
5
6
7
8
9
10
11
12
<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
<select id="selectById" resultType="Car">
select * from t_car where id = #{id}
</select>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 package com.powernode.mybatis.test;
import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
import com.powernode.mybatis.utils.SqlSessionUtil;
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 org.junit.Test;
public class CarMapperTest {
public void testSelectById() throws Exception{
// 注意:不能使用我们封装的SqlSessionUtil工具类。
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession1 = sqlSessionFactory.openSession();
CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
Car car1 = mapper1.selectById(83L);
System.out.println(car1);
CarMapper mapper2 = sqlSession1.getMapper(CarMapper.class);
Car car2 = mapper2.selectById(83L);
System.out.println(car2);
SqlSession sqlSession2 = sqlSessionFactory.openSession();
CarMapper mapper3 = sqlSession2.getMapper(CarMapper.class);
Car car3 = mapper3.selectById(83L);
System.out.println(car3);
CarMapper mapper4 = sqlSession2.getMapper(CarMapper.class);
Car car4 = mapper4.selectById(83L);
System.out.println(car4);
}
}什么情况下不走缓存?
- 第一种:不同的SqlSession对象。
- 第二种:查询条件变化了。
一级缓存失效情况包括两种:
- 第一种:第一次查询和第二次查询之间,手动清空了一级缓存。
1 sqlSession.clearCache();
- 第二种:第一次查询和第二次查询之间,执行了增删改操作。【这个增删改和哪张表没有关系,只要有insert delete update操作,一级缓存就失效。】
1
2
3
4 /**
* 保存账户信息
*/
void insertAccount();
1
2
3 <insert id="insertAccount">
insert into t_act values(3, 'act003', 10000)
</insert>
二级缓存
二级缓存的范围是SqlSessionFactory。
二级缓存是用来解决一级缓存不能跨会话共享的问题的,范围是namespace 级别的,可以被多个SqlSession 共享,二级缓存存在于 SqlSessionFactory生命周期中。如果你的MyBatis使用了二级缓存,并且你的Mapper中select语句也配置使用了二级缓存,那么在执行select查询的时候,MyBatis会先从二级缓存中取输入,其次才是一级缓存,即MyBatis查询数据的顺序是:二级缓存 —> 一级缓存 —> 数据库。
使用二级缓存需要具备以下几个条件:
<setting name="cacheEnabled" value="true">
全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。默认就是true,无需设置。- 在需要使用二级缓存的SqlMapper.xml文件中添加配置:
<cache />
- 使用二级缓存的实体类对象必须是可序列化的,也就是必须实现java.io.Serializable接口
- SqlSession对象关闭或提交之后,一级缓存中的数据才会被写入到二级缓存当中。此时二级缓存才可用。
CarMapper.xml
1 <cache/>
Car类
1
2
3 public class Car implements Serializable {
//......
}
CarMapperTest.testSelectById2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void testSelectById2() throws Exception{
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession1 = sqlSessionFactory.openSession();
CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
Car car1 = mapper1.selectById(83L);
System.out.println(car1);
// 关键一步
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);
Car car2 = mapper2.selectById(83L);
System.out.println(car2);
}二级缓存的失效:只要两次查询之间出现了增删改操作。二级缓存就会失效。【一级缓存也会失效】
二级缓存的相关配置:
eviction:指定从缓存中移除某个对象的淘汰算法。默认采用LRU策略。
LRU:Least Recently Used。最近最少使用。优先淘汰在间隔时间内使用频率最低的对象。(其实还有一种淘汰算法LFU,最不常用。)
FIFO:First In First Out。一种先进先出的数据缓存器。先进入二级缓存的对象最先被淘汰。
SOFT:软引用。淘汰软引用指向的对象。具体算法和JVM的垃圾回收算法有关。
WEAK:弱引用。淘汰弱引用指向的对象。具体算法和JVM的垃圾回收算法有关。
flushInterval:
二级缓存的刷新时间间隔。单位毫秒。如果没有设置。就代表不刷新缓存,只要内存足够大,一直会向二级缓存中缓存数据。除非执行了增删改。
readOnly:
true:多条相同的sql语句执行之后返回的对象是共享的同一个。性能好。但是多线程并发可能会存在安全问题。
false:多条相同的sql语句执行之后返回的对象是副本,调用了clone方法。性能一般。但安全。
size:
设置二级缓存中最多可存储的java对象数量。默认值1024。
集成EhCache缓存
缓存数据有内存和磁盘两级,无须担心容量问题。
缓存数据会在虚拟机重启的过程中写入磁盘。可以通过RMI、可插入API等方式进行分布式缓存。
具有缓存和缓存管理器的侦昕接口。
支持多缓存管理器实例以及一个实例的多个缓存区域。
添加项目依赖
1
2
3
4
5
6 <dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.3</version>
</dependency>
在 src/main/resources 目录下新增 ehcache.xml 文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="false" monitoring="autodetect"
dynamicConfig="true">
<!--指定数据在磁盘中的存储位置-->
<diskStore path="D:/ehcache"/>
<defaultCache
maxElementsInMemory="10000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
diskPersistent="true">
</defaultCache>
<cache name="com.nestor.mybatisdemo.mapper.SchoolMapper"
maxElementsInMemory="3000"
eternal="false"
copyOnRead="true"
copyOnWrite="true"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
overflowToDisk="true"
diskPersistent="true"/>
</ehcache>diskStore:指定数据在磁盘中的存储位置。
defaultCache:当借助CacheManager.add(“demoCache”)创建Cache时,EhCache便会采用指定的的管理策略。
以下属性是必须的:
maxElementsInMemory - 在内存中缓存的element的最大数目。不存放在内存中需要设置成1,Ehcache 2.0后设置成0表示不限制最大数目
maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断。
overflowToDisk - 设定当内存缓存溢出的时候是否将element缓存到磁盘上 。
以下属性是可选的:
timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB - 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区。
diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作。
memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候,移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)。
copyOnRead - 判断从缓存中读取数据时是返回对象的引用还是复制一个对象返回。默认情况下是false,即返回数据的引用,这种情况下返回的都是相同的对象,和MyBatis默认缓存中的只读对象是相同的。如果设置为true ,那就是可读写缓存,每次读取缓存时都会复制一个新的实例 。
copyOnWrite - 判断写入缓存时是直接缓存对象的引用还是复制一个对象然后缓存,默认也是false。如果想使用可读写缓存,就需要将这两个属性配置为true,如果使用只读缓存,可以不配置这两个属性,使用默认值false 即可 。ehcache-cache 提供了如下 2 个可选的缓存实现。
org.mybatis.caches.ehcache.EhcacheCache
org.mybatis.caches.ehcache.LoggingEhcache 这个是带日志的缓存。在Mapper.xml中添加如下配置即可
1
2
3
4 <mapper namespace="com.nestor.mybatisdemo.mapper.GradeParamMapper">
<cache type="org.mybatis.caches.ehcache.EhcacheCache" />
</mapper>
逆向工程
所谓的逆向工程是:根据数据库表逆向生成Java的pojo类,SqlMapper.xml文件,以及Mapper接口类等。
要完成这个工作,需要借助别人写好的逆向工程插件。
思考:使用这个插件的话,需要给这个插件配置哪些信息?
- pojo类名、包名以及生成位置。
- SqlMapper.xml文件名以及生成位置。
- Mapper接口名以及生成位置。
- 连接数据库的信息。
- 指定哪些表参与逆向工程。
逆向工程配置与生成
第一步:基础环境准备
新建模块:mybatis-011-generator
打包方式:jar
第二步:在pom中添加逆向工程插件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 <!--定制构建过程-->
<build>
<!--可配置多个插件-->
<plugins>
<!--其中的一个插件:mybatis逆向工程插件-->
<plugin>
<!--插件的GAV坐标-->
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.1</version>
<!--允许覆盖-->
<configuration>
<overwrite>true</overwrite>
</configuration>
<!--插件的依赖-->
<dependencies>
<!--mysql驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>配置generatorConfig.xml
该文件名必须叫做:
generatorConfig.xml
该文件必须放在类的
根路径下
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<generatorConfiguration>
<!--
targetRuntime有两个值:
MyBatis3Simple:生成的是基础版,只有基本的增删改查。
MyBatis3:生成的是增强版,除了基本的增删改查之外还有复杂的增删改查。
-->
<context id="DB2Tables" targetRuntime="MyBatis3">
<!--防止生成重复代码-->
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>
<commentGenerator>
<!--是否去掉生成日期-->
<property name="suppressDate" value="true"/>
<!--是否去除注释-->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--连接数据库信息-->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/powernode"
userId="root"
password="root">
</jdbcConnection>
<!-- 生成pojo包名和位置 -->
<javaModelGenerator targetPackage="com.powernode.mybatis.pojo" targetProject="src/main/java">
<!--是否开启子包-->
<property name="enableSubPackages" value="true"/>
<!--是否去除字段名的前后空白-->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- 生成SQL映射文件的包名和位置 -->
<sqlMapGenerator targetPackage="com.powernode.mybatis.mapper" targetProject="src/main/resources">
<!--是否开启子包-->
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- 生成Mapper接口的包名和位置 -->
<javaClientGenerator
type="xmlMapper"
targetPackage="com.powernode.mybatis.mapper"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 表名和对应的实体类名-->
<table tableName="t_car" domainObjectName="Car"/>
</context>
</generatorConfiguration>运行插件
在Maven管理面板,双击插件
测试逆向工程生成
第一步:环境准备
- 依赖:mybatis依赖、mysql驱动依赖、junit依赖、logback依赖
- jdbc.properties
- mybatis-config.xml
- logback.xml
第二步:编写测试程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 package com.powernode.mybatis.test;
import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
import com.powernode.mybatis.pojo.CarExample;
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 org.junit.Test;
import java.math.BigDecimal;
import java.util.List;
public class GeneratorTest {
public void testGenerator() throws Exception{
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
// 增
/*Car car = new Car();
car.setCarNum("1111");
car.setBrand("比亚迪唐");
car.setGuidePrice(new BigDecimal(30.0));
car.setProduceTime("2010-10-12");
car.setCarType("燃油车");
int count = mapper.insert(car);
System.out.println("插入了几条记录:" + count);*/
// 删
/*int count = mapper.deleteByPrimaryKey(83L);
System.out.println("删除了几条记录:" + count);*/
// 改
// 根据主键修改
/*Car car = new Car();
car.setId(89L);
car.setGuidePrice(new BigDecimal(20.0));
car.setCarType("新能源");
int count = mapper.updateByPrimaryKey(car);
System.out.println("更新了几条记录:" + count);*/
// 根据主键选择性修改
/*car = new Car();
car.setId(89L);
car.setCarNum("3333");
car.setBrand("宝马520Li");
car.setProduceTime("1999-01-10");
count = mapper.updateByPrimaryKeySelective(car);
System.out.println("更新了几条记录:" + count);*/
// 查一个
Car car = mapper.selectByPrimaryKey(89L);
System.out.println(car);
// 查所有
List<Car> cars = mapper.selectByExample(null);
cars.forEach(c -> System.out.println(c));
// 多条件查询
// QBC 风格:Query By Criteria 一种查询方式,比较面向对象,看不到sql语句。
CarExample carExample = new CarExample();
carExample.createCriteria()
.andBrandEqualTo("丰田霸道")
.andGuidePriceGreaterThan(new BigDecimal(60.0));
carExample.or().andProduceTimeBetween("2000-10-11", "2022-10-11");
mapper.selectByExample(carExample);
sqlSession.commit();
}
}
分页插件
mysql的limit后面两个数字:
- 第一个数字:startIndex(起始下标。下标从0开始。)
- 第二个数字:pageSize(每页显示的记录条数)
假设已知页码pageNum,还有每页显示的记录条数pageSize,第一个数字可以动态的获取吗?
- startIndex = (pageNum - 1) * pageSize
所以,标准通用的mysql分页SQL:
1
2
3
4
5
6 select
*
from
tableName ......
limit
(pageNum - 1) * pageSize, pageSize
PageHelper插件
第一步:引入依赖
1
2
3
4
5 <dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.1</version>
</dependency>第二步:在mybatis-config.xml文件中配置插件
1
2
3 <plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>第三步:编写Java代码
CarMapper接口
1 List<Car> selectAll();
CarMapper.xml
1
2
3 <select id="selectAll" resultType="Car">
select * from t_car
</select>关键点:
- 在查询语句之前开启分页功能。
- 在查询语句之后封装PageInfo对象。(PageInfo对象将来会存储到request域当中。在页面上展示。)
PageTest.testPageHelper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void testPageHelper() throws Exception{
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
// 开启分页
PageHelper.startPage(2, 2);
// 执行查询语句
List<Car> cars = mapper.selectAll();
// 获取分页信息对象
PageInfo<Car> pageInfo = new PageInfo<>(cars, 5);
System.out.println(pageInfo);
}对执行结果进行格式化:
1
2
3
4
5
6
7
8 PageInfo{
pageNum=2, pageSize=2, size=2, startRow=3, endRow=4, total=6, pages=3,
list=Page{count=true, pageNum=2, pageSize=2, startRow=2, endRow=4, total=6, pages=3, reasonable=false, pageSizeZero=false}
[Car{id=86, carNum='1234', brand='丰田霸道', guidePrice=50.5, produceTime='2020-10-11', carType='燃油车'},
Car{id=87, carNum='1234', brand='丰田霸道', guidePrice=50.5, produceTime='2020-10-11', carType='燃油车'}],
prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true,
navigatePages=5, navigateFirstPage=1, navigateLastPage=3, navigatepageNums=[1, 2, 3]
}