一、复杂查询环境搭建
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>
<settings>
<!--<setting name="logImpl" value="STDOUT_LOGGING"/>--><!--注意不能有空格-->
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
<environments default="test"> <!--default值写的是下面环境中id的哪一个,对应的就使用的哪套环境-->
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.kuang.dao.*Mapper"></mapper>
</mappers>
</configuration>
二、多对一(查所有学生和老师)
- 联表查询方式
<mapper namespace="com.kuang.dao.StudentMapper">
<resultMap id="stuMap" type="student" >
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="tid" column="tid"/>
<association property="teacher" javaType="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
</association>
</resultMap>
<select id="getStuList" resultMap="stuMap">
SELECT s.id id,s.name name,t.id tid,t.name tname FROM Student s,teacher t WHERE s.tid = t.id
</select>
-
嵌套子表查询方式
<select id="getStu" resultMap="stuTeach"> select * from student </select> <resultMap id="stuTeach" type="Student" > <!--id标签用来表明主键,可用result标签代替 子查询中参数是对象时用association,参数是集合时用collection--> <result column="id" property="id"/> <result column="name" property="name"/> <association property="teacher" column="tid" javaType="Teacher" select="getTea"/> <!--column="tid"当作参数参与下一级的查询,--> </resultMap> <select id="getTea" resultType="Teacher"> select * from teacher where id=#{tid} </select>
三、一对多
1.按结果嵌套查询
<resultMap id="teaMap" type="teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!-- 实体类属性为list集合时要用collection标签,并且Java Type类型要使用ofType,ofType才能就收到集合中的泛型信息
javaType时用来指定属性类型的
实体类属性为对象时要用association标签-->
<collection property="students" ofType="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="stid"/>
</collection>
</resultMap>
<!-- 按结果嵌套查询-->
<select id="getTeach" resultMap="teaMap">
SELECT s.id sid,s.name sname,t.id id ,t.name NAME,s.tid stid
FROM teacher t,student s WHERE t.id=s.tid
</select>
2.子查询
<select id="getTeach1" resultMap="getStuMap">
select * from teacher
</select>
<!-- type对应结果集的实体类型 teacher类中的字段可以省略不写-->
<resultMap id="getStuMap" type="Teacher">
<collection property="students" select="getStu" column="id" >
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
<!-- #{aid}对应collection中column字段,里面的值可以跟aid不一样 -->
<select id="getStu" resultType="Student">
select * from student where tid=#{aid}
</select>
小结
1.关联–association 多对一 实体类使用
2.集合–collection 一对多 实体类属性为list集合时使用
3.javaType 用来指定实体类中属性的类型
4.ofType用来指定映射到list或者集合中的pojo类型,泛型中的约束类型
四,有参数的查询
List<Teacher> getTeaById(int id);
<resultMap id="teaMap" type="teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!-- 实体类属性为list集合时要用collection标签,并且Java Type类型要使用ofType,ofType才能就收到集合中的泛型信息
javaType时用来指定属性类型的
实体类属性为对象时要用association标签-->
<collection property="students" ofType="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="stid"/>
</collection>
</resultMap>
<!-- 按结果嵌套查询-->
<select id="getTeach" resultMap="teaMap">
SELECT s.id sid,s.name sname,t.id id ,t.name NAME,s.tid stid
FROM teacher t,student s WHERE t.id=s.tid and t.id=#{id}
</select>
或者下面这种方式
<!-- 子查询-->
<select id="getTeaById1" resultMap="getStuMap1">
select * from teacher where id=#{id} <!--#{id}对应接口方法中传递的参数 -->
</select>
<!-- type对应结果集的实体类型 teacher类中的字段可以省略不写-->
<resultMap id="getStuMap1" type="Teacher">
<!-- collection中property对应实体类中创建的实体类属性,column对应实体类属性,即外键-->
<collection property="students" select="getStu1" column="id" >
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
<!-- #{aid}对应collection中column字段,里面的值可以跟aid不一样 -->
<select id="getStu1" resultType="Student">
select * from student where tid=#{aid} <!--#{aid}对应collection中column值 -->
</select>
五、动态sql:if/choose(when,otherwise)/trim(where,set)/foreach
1.if语句 test里写参数的表达式
<select id="getBlogIf" parameterType="blog" resultType="blog"> select * from blog where 1=1 <if test="title != null"> and title like #{title} </if> <if test="author != null"> or author like #{author} </if></select>
@Testpublic void getBlogIfTest(){ SqlSession sqlSession = MyUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); Blog blog = new Blog(); blog.setAuthor("%za%"); blog.setTitle("%sdf%"); List<Blog> blogIf = mapper.getBlogIf(blog); for (Blog blog1 : blogIf) { System.out.println(blog1); } sqlSession.close();}
UUID的生成方法,基本不会重复!
public static String getId(){ return UUID.randomUUID().toString().replaceAll("-","");}
2.choose(when,otherwise)语句
@Testpublic void getChooseTest(){ SqlSession sqlSession = MyUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); Blog blog = new Blog(); // blog.setAuthor("%za%"); blog.setTitle("%sdf%"); List<Blog> blogIf = mapper.getChoose(blog); for (Blog blog1 : blogIf) { System.out.println(blog1); } sqlSession.close();}
<select id="getChoose" resultType="blog" parameterType="blog"> select * from blog where 1=1 <choose> <when test="author !=null"> and author like #{author} </when> <when test="author !=null and title !=null"> and title like #{title} </when> <otherwise> and views=3 </otherwise> </choose> </select>
3.trim(where,set)语句
@Testpublic void updateBlogTest(){ SqlSession sqlSession = MyUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); Blog blog = new Blog(); blog.setAuthor("lisi"); blog.setTitle("nisagdslg"); blog.setId("21d20e21bc724363974888ce1fbabcf9"); int blogIf = mapper.updateBlog(blog); if (blogIf>0){ System.out.println("更新成功!"); } sqlSession.commit(); sqlSession.close();}
<update id="updateBlog" parameterType="blog" > update blog <set> <if test="title !=null"> title = #{title}, </if> <if test="author !=null"> author = #{author}, </if> </set> where id=#{id};</update>
set标签对于多写的“,”可以自动过滤,
或者用sql片段实现
<sql id="sql_title_author" > <if test="title !=null"> title = #{title}, </if> <if test="author !=null"> author = #{author}, </if></sql><update id="updateBlog1" parameterType="blog"> update blog <set> <include refid="sql_title_author"></include> </set> where id=#{id};</update>
4.foreach语句
<!-- select * from blog where id=1 or id=2 or id=3; --> <select id="getForEach" parameterType="map" resultType="blog"> select * from blog <where> <foreach collection="ids" item="id" open="(" close=")" separator="or"> id=#{id} </foreach> </where> </select>
@Testpublic void getForEachTest(){ SqlSession sqlSession = MyUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); ArrayList<Integer> ids = new ArrayList<>(); ids.add(1); ids.add(2); Map map = new HashMap(); map.put("ids",ids); List<Blog> forEach = mapper.getForEach(map); for (Blog each : forEach) { System.out.println(each); } sqlSession.close();}
六,缓存
mybatis中默认定义了两级缓存:一级缓存sqlSession和二级缓存cache
默认情况下,只有一级缓存开启,即sqlSession级别的缓存,也称为本地缓存;
二级缓存需要手动开启和配置,是基于namespace级别的缓存
1.一级缓存
默认是开启的,只在一次sqlsession中有效,sqlsession关闭即消失,也就是拿到连接到连接关闭这个区间段!
-
测试在一个session中查询两次相同记录
@Test public void getBlogById(){ SqlSession sqlSession = MyUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); Blog blogById = mapper.getBlogById("1"); System.out.println(blogById); Blog blogById1 = mapper.getBlogById("1"); System.out.println(blogById1); sqlSession.close(); }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xo4gDea6-1628298109933)(C:\Users\56504\AppData\Roaming\Typora\typora-user-images\image-20210806171306636.png)]
-
缓存失效的情况
- 查询不同的东西
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存
- 查询不同的Mapper.xml时
- 手动清理缓存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-niAO8524-1628298109937)(C:\Users\56504\AppData\Roaming\Typora\typora-user-images\image-20210806172027295.png)]
@Testpublic void getBlogById(){ SqlSession sqlSession = MyUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); Blog blogById = mapper.getBlogById("1"); //添加记录,改变原来的数据,所以必定会刷新缓存 mapper.addBlog(new Blog(MyUtils.getId(), "asdsfs", "zhangsa", new Date(), 3)); sqlSession.commit(); Blog blogById1 = mapper.getBlogById("1"); sqlSession.close();}三个方法,三个查询,
@Testpublic void getBlogById(){ SqlSession sqlSession = MyUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); Blog blogById = mapper.getBlogById("1"); sqlSession.clearCache();//手动清理缓存,缓存即失效 Blog blogById1 = mapper.getBlogById("1"); sqlSession.close();}
2.二级缓存
-
也是一个全局缓存,弥补一级缓存的作用域太低的问题
-
基于namespace级别的缓存,即一个命名空间对应一个二级缓存
-
工作机制:
- 一个会话查询一条数据,这个数据就被放在当前的一级缓存中;
- 如果当期会话关闭了,对应的一级缓存就没了,此时我们就需要把一级缓存中的数据保存到二级缓存中去
- 新的会话查询信息就从二级缓存中获取
- 不同的mapper查的数据放在自己对应的缓存中
-
开启二级缓存的步骤
-
开启全局缓存,在mybatis-config.xml中配置
<settings><!-- 显示的开启缓存(二级缓存)--> <setting name="cacheEnabled" value="true"/> </settings>
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CXuwMpwq-1628298109942)(C:\Users\56504\AppData\Roaming\Typora\typora-user-images\image-20210806175749286.png)]
-
在要使用二级缓存的mapper中开启
没有配置参数的情况<cache/>
或者自定义参数的方式
<!-- 先进先出,不定时刷新=60s, 保存列表或对象的1024个引用(默认的),只读--> <cache eviction="FIFO" flushInterval="60000" size="1024" readOnly="true"/>
使用二级缓存后,上图改进后的结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KmIGMo6U-1628298109944)(C:\Users\56504\AppData\Roaming\Typora\typora-user-images\image-20210806180853547.png)]
总结
1. 只要开启了二级缓存,在同一个mapper下就有效
2. 所有的数据都会先放到一级缓存中
3. 只有当会话提交,或者关闭的时候,才会提交到二级缓存中
*** 缓存查询顺序:***
1.先看二级缓存有没有;
2.再看一级缓存有没有;
3.最后查询数据库