mybatis
Table of Contents generated with DocToc (opens new window)
# mybatis常用配置
<?xml version="1.0" encoding="UTF8" ?>
<!--究极之恶心的 如果xml文件的第一行的 encoding=UTF-8就会报错 改成UTF8才不会报错-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration为核心配置文件-->
<configuration>
<!-- 引入外部配置文件-->
<properties resource="db.properties">
<!-- 在db.properties文件中不写以下两个熟悉也是可以的 只需在properties中加入property标签添加相应属性即可-->
<!-- <property name="username" value="root"/>-->
<!-- <property name="password" value="root"/>-->
<!-- 对比可得出 若属性名相同,会优先使用外部配置文件db.properties中的属性-->
</properties>
<!-- 配置日志-->
<settings>
<!-- 标准日志工厂实现-->
<!-- <setting name="logImpl" value="STDOUT_LOGGING"/>-->
<!-- LOG4J需要先导包 然后需要log4j.properties配置文件-->
<setting name="logImpl" value="LOG4J"/>
</settings>
<!-- 1 可以给实体类起别名-->
<typeAliases>
<typeAlias type="com.zdk.pojo.User" alias="User"/>
<!-- 2 扫描实体类的包 它的默认别名就为这个类的类名的小写字母形式-->
<!-- 实测 全小写或全大写都能识别出 官方建议 全小写-->
<!-- 实体类较少时 建议使用起别名方式;实体类较少时,建议使用扫描包的方式-->
<!--还可通过 @Alias() 注解的方式给实体类起别名,但注解方式失败 原因未知!!!!!!!-->
<package name="com.zdk.pojo.User"/>
</typeAliases>
<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>
<!-- 每一个Mapper.xml都需要在mybatis核心配置文件中注册!-->
<mappers>
<mapper resource="com/zdk/dao/UserMapper.xml"/>
<!-- 通过接口路径进行映射时 接口和xml文件必须和类在同一个包下 且必须同名-->
<!-- <mapper class="com.zdk.dao.UserMapper"></mapper>-->
<!-- 包扫描绑定 将包下的所有xml注册 注意点同上-->
<!-- <package name="com.zdk.dao"/>-->
</mappers>
</configuration>
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
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
# 资源过滤器
使用此种方法时 必须配置资源过滤
<!-- 配置过滤 防止资源导出失败-->
<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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 常用增删改查、分页xml配置文件
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.zdk.dao.UserMapper">
<!-- 查询语句-->
<!-- id对应接口方法名称-->
<!-- 结果集映射 将select中的返回类型改成resultMap 值即为以下的id 然后其中的column为数据库中的字段名 property即为实体类中的属性名-->
<resultMap id="UserMap" type="User">
<!-- <result column="id" property="id"></result>-->
<!-- <result column="name" property="name"></result>-->
<!-- 哪条字段不一致就映射哪一条-->
<result column="password" property="password"></result>
</resultMap>
<select id="getUserList" resultMap="UserMap">
select * from user ;
</select>
<select id="getUserById" resultType="User" parameterType="int">
select * from user where id=#{id};
</select>
<insert id="addUser" parameterType="User">
insert into user (id, name, password) values (#{id},#{name},#{password});
</insert>
<update id="modifyUser" parameterType="User">
update user set name=#{name},password=#{password} where id=#{id};
</update>
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id};
</delete>
<!-- 分页1-->
<select id="getUserListByLimit" parameterType="map" resultType="user">
select * from user limit #{startIndex},#{pageSize};
</select>
<!-- 分页2-->
<select id="getUserListByRowBounds" resultType="user">
select * from user;
</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
43
44
45
46
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
# 简单注解的使用
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
@Delete("delete from user where id=#{id}")
int deleteUser(@Param("id") int id);
@Insert("insert into user(id,name,password) values (#{id},#{name},#{password})")
int addUser(User user);
@Update("update user set name=#{name},password=#{password} where id=#{id}")
int updateUser(User user);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 多对一的处理
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
private Teacher teacher;
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
因为每个Student对象中都有一个Teacher,使用常规查询无法得到teacher,需要嵌套
<?xml version="1.0" encoding="UTF8" ?>
<!--究极之恶心的 如果xml文件的第一行的 encoding=UTF-8就会报错 改成UTF8才不会报错-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zdk.dao.StudentMapper">
<!-- 一.按照查询进行嵌套处理
1.查询所有学生信息
2.按照查询出的tid,查询对应的老师信息
-->
<select id="getStudentList" resultMap="StudentAndTeacher">
select * from student;
</select>
<resultMap id="StudentAndTeacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!-- 复杂属性 单独处理 association处理对象 collection用于处理集合-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id=#{id};
</select>
<!-- 二.按照结果嵌套处理-->
<select id="getStudentList2" resultMap="StudentAndTeacher2">
select s.id sid,s.name sname,t.id ttid,t.name tname
from student s,teacher t
where s.tid=t.id;
</select>
<resultMap id="StudentAndTeacher2" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="id" column="ttid"/>
<result property="name" column="tname"/>
</association>
</resultMap>
</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
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
# 一对多的处理
# 一个老师对应多个学生:实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
private int tid;
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
//一个老师拥有多个学生
private List<Student> students;
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 老师的mapper
public interface TeacherMapper {
//获取所有老师
List<Teacher> getTeacher();
//获取一个老师下的所有学生以及这个老师的信息
Teacher getTeacherS(@Param("tid") int id);
//获取一个老师下的所有学生以及这个老师的信息
Teacher getTeacherS2(@Param("tid") int id);
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# TeacherMapper.xml文件配置
# 方法一 按结果嵌套查询
# xml
<!-- 方法一 按结果嵌套查询-->
<!-- 先将所有要查的字段全部查出来-->
<select id="getTeacherS" resultMap="getTeacherSMap">
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>
<!-- 再将查出来的字段映射到老师对应的属性中去 collection表示集合 association表示对象-->
<resultMap id="getTeacherSMap" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 测试
public class MyTest {
@Test
public void test1(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher= mapper.getTeacherS(1);
System.out.println(teacher);
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 利用子查询 然后映射
# xml
<!-- 方法2 子查询-->
<!-- 先直接查老师的所有信息-->
<select id="getTeacherS2" resultMap="getTeacherSMap2">
select *
from teacher
where id=#{tid};
</select>
<!-- 子查询语句-->
<select id="getStudentByTeacherId" resultType="Student">
select *
from student where tid=#{tid};
</select>
<!-- 对Teacher中的List<Student> students进行映射-->
<resultMap id="getTeacherSMap2" type="Teacher">
<!-- 这句话得加上 不然老师查出的id为0-->
<result property="id" column="id"/>
<!-- Javatype是表示students属性的类型,oftype表示这个类型的泛型类型-->
<collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
</resultMap>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 测试
public class MyTest {
@Test
public void test2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher= mapper.getTeacherS2(1);
System.out.println(teacher);
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 结果均可查出
Teacher(id=1, name=秦老师, students=[Student(id=1, name=小明, tid=1),
Student(id=2, name=小红, tid=1), Student(id=3, name=小张, tid=1),
Student(id=4, name=小李, tid=1), Student(id=5, name=小王, tid=1)])
1
2
3
2
3
# 动态SQL(重点)
# 环境搭建
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
public interface BlogMapper {
int addBlog(Blog blog);
//查询博客 通过if
List<Blog> queryBlogIf(Map map);
}
1
2
3
4
5
6
2
3
4
5
6
public class MyTest {
@Test
public void addInitBlog(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Blog blog=new Blog();
blog.setId(IdUtils.getUUID());
blog.setTitle("Mybatis如此简单");
blog.setAuthor("狂神说");
blog.setCreateTime(new Date());
blog.setViews(9999);
mapper.addBlog(blog);
blog.setId(IdUtils.getUUID());
blog.setTitle("Java如此简单");
mapper.addBlog(blog);
blog.setId(IdUtils.getUUID());
blog.setTitle("Spring如此简单");
mapper.addBlog(blog);
blog.setId(IdUtils.getUUID());
blog.setTitle("微服务如此简单");
mapper.addBlog(blog);
sqlSession.close();
}
}
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
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
# if语句使用
<?xml version="1.0" encoding="UTF8" ?>
<!--究极之恶心的 如果xml文件的第一行的 encoding=UTF-8就会报错 改成UTF8才不会报错-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zdk.mapper.UserMapper">
<insert id="addBlog" parameterType="Blog">
insert into blog (id, title, author, create_time, views)
values (#{id},#{title},#{author},#{createTime},#{views});
</insert>
<select id="queryBlogIf" parameterType="map" resultType="Blog">
select * from blog where 1=1
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</select>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 在使用if时 应适当加上where标签 防止出现where and这种情况
<!-- 使用动态SQL的where和if语句-->
<select id="queryBlogIf" parameterType="map" resultType="Blog">
# where元素只会在至少有一个if子元素条件返回SQL语句的情况下才去插入WHERE
# 子语句。而且,若语句的开头为AND或OR,where元素也会将它们去除
select * from blog
<where>
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</where>
</select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- where元素只会在至少有一个if子元素条件返回SQL语句的情况下才去插入WHERE 子语句。而且,若语句的开头为AND或OR,where元素也会将它们去除
# 测试结果
public class MyTest {
@Test
public void queryBlogIf(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap<>();
//map.put("title", "Java如此简单");
map.put("author", "狂神说");
List<Blog> blogs = mapper.queryBlogIf(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Blog(id=5d18217da51c46f981909c65a5304b62, title=Mybatis如此简单, author=狂神说, createTime=Mon Apr 26 20:59:02 CST 2021, views=3000)
Blog(id=aa5ea0c0b60e4755897007da040a5901, title=Java如此简单, author=狂神说, createTime=Mon Apr 26 20:59:02 CST 2021, views=1000)
Blog(id=9e497893427141469a6a0b041ad0b34d, title=Spring如此简单, author=狂神说, createTime=Mon Apr 26 20:59:02 CST 2021, views=9999)
Blog(id=e7a2240692a44d7bba299db170b14cf0, title=微服务如此简单, author=狂神说, createTime=Mon Apr 26 20:59:02 CST 2021, views=9999)
1
2
3
4
2
3
4
# choose语句使用
<!-- 使用动态SQL的choose语句,类似于Java中的switch语句-->
<select id="queryBlogChoose" parameterType="map" resultType="Blog">
select * from blog
<where>
<choose>
<when test="title!=null">
title=#{title}
</when>
<when test="author!=null">
author=#{author}
</when>
<otherwise>
views=#{views}
</otherwise>
</choose>
</where>
</select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# set语句使用
<!-- 更新博客. 会自动将SET前置,如果最后一个没有匹配上,前一个语句后的,会被自动去除-->
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
author=#{author}
</if>
</set>
where id=#{id}
</update>
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# SQL片段
<sql id="if-title-author">
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</sql>
<!-- 使用动态SQL的where和if语句-->
<select id="queryBlogIf" parameterType="map" resultType="Blog">
# where元素只会在至少有一个if子元素条件返回SQL语句的情况下才去插入WHERE
# 子语句。而且,若语句的开头为AND或OR,where元素也会将它们去除
select * from blog
<where>
<include refid="if-title-author"></include>
</where>
</select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 缓存
1 什么是缓存
- 存在内存中的临时数据
- 将用户经常查询的数据放在缓存中,用户去查询数据就不用从磁盘(关系型数据库文件) 中查询,直接从缓存中查询,从而提高了查询效率,解决了高并发系统的性能问题
2 为什么使用缓存?
- 减少和数据库交互的次数,减少系统开销,提高系统效率
3 什么样的数据能使用缓存
- 经常查询并且不经常改变的数据
# 一级缓存
- 一级缓存也叫本地缓存:SqlSession级别的缓存
- 与数据库同一次会话期间查询到的数据会放在本地缓存中
- 以后如果需要获取相同的数据,直接从缓存中拿,不用再去查询数据库
# 缓存失效的情况
- 查询不同的东西
- 增删改操作,可能会改变原来的数据,所以不管是不是增删改要查的数据, 都会刷新缓存(比如在两次一样的查询之间添加一次增删改操作)
public class MyTest {
@Test
public void test1(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
// System.out.println("=======增加更新操作 来更新缓存");
// mapper.updateUser(new User(2, "张振明", "13215313"));
System.out.println("清理缓存==========");
sqlSession.clearCache();
System.out.println("第二次查询分割线===============");
User user1 = mapper.queryUserById(1);
System.out.println(user1);
System.out.println(user==user1);
sqlSession.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 查询不同的Mapper.xml(不同的SqlSession)
- 手动清理缓存
# 小结
- 一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到这个 连接到关闭这个连接的时间段内有效
# 二级缓存
也叫全局缓存,是基于namespace级别的缓存,一个名称空间对应一个二级缓存
工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果当前会话关闭了,一级缓存中的数据会被保存到二级缓存中
- 新的会话查询信息,就可以从二级缓存中获取内容
- 不同的mapper查询出的数据会放在自己对应的缓存(map)中
要启用全局的二级缓存,需要在mapper.xml映射文件中添加一行:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
1
2
3
4
5
2
3
4
5
- 显式的在mybatis-config.xml的settings属性中设置
<setting name="cacheEnabled" value="true"/>
1
public class MyTest {
@Test
public void test2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
sqlSession.close();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user1 = mapper2.queryUserById(1);
System.out.println("第二次查询分割线===============");
System.out.println(user1);
sqlSession2.close();
System.out.println(user==user1);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 小结
- 只要开启了二级缓存,在同一个mapper下就会生效
- 所有的数据都会先放在一级缓存中
- 只有当会话提交或者关闭时,数据才会被提交到二级缓存中
# 自定义缓存-Ehcache
# ehcache.xml
<?xml version="1.0" encoding="UTF-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!-- diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置
参数解释如下:
user.home:为用户主目录
user.dir:为用户当前工作目录
java.io.tmpdir:为默认临时文件路径
-->
<diskStore path="./tmpdir/Tmp_EhCache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
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
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
在 GitHub 上编辑此页 (opens new window)
最后更新: 2022/10/04, 16:10:00