思考:为什么要分页?
语法:SELECT * from user limit startIndex,pageSize
SELECT * from user limit 0,2
1,何锦涛,123456
2,严瑶,123456
使用Mybatis实现分页,核心SQL
package com.atctbu.hjt.dao;
import com.atctbu.hjt.pojo.User;
import java.util.List;
import java.util.Map;
public interface UserMapper {
//根据ID查询用户
User getUserById(int id);
//分页
List<User> getUserByLimit(Map<String, Integer> map);
}
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> userList = mapper.getUserByLimit(map);
for (User user:userList
) {
System.out.println(user);
}
}
<select id="getUserByLimit" parameterType="map" resultType="user" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
select>
不建议使用
接口
package com.atctbu.hjt.dao;
import com.atctbu.hjt.pojo.User;
import java.util.List;
import java.util.Map;
public interface UserMapper {
//RowBounds分页
List getUserByRowBounds(Map map);
}
SQL
<resultMap id="UserMap" type="User">
<result column="pwd" property="password"/>
resultMap>
<select id="getUserByRowBounds" parameterType="map" resultType="user" resultMap="UserMap">
select * from mybatis.user
select>
测试类
@Test
public void getUserByRowBounds(){
//RoolBounds实现
RowBounds rowBounds = new RowBounds(1, 2);
SqlSession sqlSession = MybatisUtils.getSqlSession();
List<User> userList = sqlSession.selectList("com.atctbu.hjt.dao.UserMapper.getUserByRowBounds",null,rowBounds);
for (User user: userList
) {
System.out.println(user);
}
sqlSession.close();
}
大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
根本原因: 解耦 ,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得
开发变得容易,规范性更好
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;
而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
关于接口的理解
三个面向区别
这些东西不需要了:
UserMapper.xml
<mappers>
<package name="com.atctbu.hjt.dao"/>
mappers>
接口类:
package com.atctbu.hjt.dao;
import com.atctbu.hjt.pojo.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
}
绑定接口
<mappers>
<mapper class="com.atctbu.hjt.dao.UserMapper"/>
mappers>
测试类
public class UserMapperTest {
@Test
public void Test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//底层主要应用反射
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.getUsers();
for (User user:users
) {
System.out.println(user);
}
sqlSession.close();
}
}
User{
id=1, name='何锦涛', password='null'}
User{
id=2, name='严瑶', password='null'}
User{
id=3, name='李四', password='null'}
password为null
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
本质:反射机制实现
底层:动态代理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ABb60DfP-1616654175262)(https://m.qpic.cn/psc?/V11Mzdsz1sKJyi/ruAMsa53pVQWN7FLK88i5prTFvIfciN5g5dWsG.MM2bql8rwSmkNAkY9sChUYJ1pFZyasfUXRsL69Vp0Zh8rZya8SCfxXodiBM.h*3.PxwM!/b&bo=RQIzAQAAAAADB1c!&rf=viewer_4)]
Mybatis详细的执行流程! P16,看不大懂,有机会回去补!
我们可以在工具类创建的时候自动提交事务
public static SqlSession getSqlSession(){
// SqlSession sqlSession = sqlSessionFactory.openSession();
// return sqlSession;
return sqlSessionFactory.openSession(true); //这里设置为true(autocommit)
}
增删改查
package com.atctbu.hjt.dao;
import com.atctbu.hjt.pojo.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
//方法存在多个参数,所有的参数前必须加上@Param("id")注解,默认按照@Param里的查找,如@Delete方法里面那个一样
@Select("select * from user where id = #{id}")
User getUserById(@Param("id") int id);
@Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
int addUser(User user);
@Update("update user set name=#{name},pwd=#{password} where id=#{id}")
int updateUser(User user);
@Delete("delete from user where id = #{uid}")
int deleteUser(@Param("uid") int id);
}
测试类
【注意,我们必须将接口注册绑定到我们的核心配置文件中】
<mappers>
<mapper class="com.atctbu.hjt.dao.UserMapper"/>
mappers>
import com.atctbu.hjt.dao.UserMapper;
import com.atctbu.hjt.pojo.User;
import com.atctbu.hjt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserMapperTest {
@Test
public void Test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
/*
List users = mapper.getUsers();
for (User user:users
) {
System.out.println(user);
}
User userById = mapper.getUserById(1);
System.out.println(userById);
mapper.addUser(new User(4,"芜湖","123456")); //设置了自动提交事务,所以不用手动提交
mapper.updateUser(new User(4,"起飞","123456"));
*/
mapper.deleteUser(4);
sqlSession.close();
}
}
关于@Param()注解
Lombok项目是一个Java库,它会自动插入您的编辑器和构建工具中,从而使您的Java更加有趣。
永远不要再写另一个getter或equals方法,带有一个注释的您的类有一个功能全面的生成器,自动化您的日志记录变量等等。
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.12version>
dependency>
@Getter and @Setter
@FieldNameConstants //字段属性常量
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@Data:无参构造、get、set、toString、hashcode、equal
import lombok.Data;
@Data
public class User {
private int id;
private String name;
private String password;
}
@AllArgsConstructor:有参构造
@NoArgsConstructor无参构造
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String password;
}
多对一:
association – 一个复杂类型的关联;许多结果将包装成这种类型
嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
collection – 一个复杂类型的集合
嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
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=utf8INSERT 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');
<mappers>
<mapper resource="com/atctbu/hjt/dao/StudentMapper.xml"/>
<mapper resource="com/atctbu/hjt/dao/TeacherMapper.xml"/>
mappers>
6.测试查询是否成功
学生Mapper
package com.atctbu.hjt.dao;
import com.atctbu.hjt.pojo.Student;
import java.util.List;
public interface StudentMapper {
//按照查询嵌套处理:查询所有的学生信息以及所有的老师信息!
public List<Student> getStudent();
//按照结果嵌套处理
public List<Student> getStudent2();
}
Student类
public class Student {
private int id;
private String name;
//学生需要关联一个老师
private Teacher teacher;
<mapper namespace="com.atctbu.hjt.dao.StudentMapper">
<select id="getStudent" resultMap="StudentAndTeacher">
select * from student
select>
<resultMap id="StudentAndTeacher" 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>
mapper>
Student{id=1, name='小何', teacher=Teacher{id=1, name='何老师'}}
Student{id=2, name='小瑶', teacher=Teacher{id=1, name='何老师'}}
Student{id=3, name='小张', teacher=Teacher{id=1, name='何老师'}}
Student{id=4, name='小王', teacher=Teacher{id=1, name='何老师'}}
Student{id=5, name='小米', teacher=Teacher{id=1, name='何老师'}}
<mapper namespace="com.atctbu.hjt.dao.StudentMapper">
<select id="getStudent2" resultMap="StudentAndTeacher2">
select s.id sid,s.name sname,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="name" column="tname"/>
association>
resultMap>
mapper>
Student{id=1, name='小何', teacher=Teacher{id=0, name='何老师'}}
Student{id=2, name='小瑶', teacher=Teacher{id=0, name='何老师'}}
Student{id=3, name='小张', teacher=Teacher{id=0, name='何老师'}}
Student{id=4, name='小王', teacher=Teacher{id=0, name='何老师'}}
Student{id=5, name='小米', teacher=Teacher{id=0, name='何老师'}}
因为没有查id,id没设置就是默认值0
回顾Mysql多对一查询方式:
比如:一个老师拥有多个学生
对于老师而言,就是一对多的关系!
实体类Student
public class Student {
private int id;
private String name;
private int tid;
实体类Teacher
public class Teacher {
private int id;
private String name;
//老师拥有多个学生
private List<Student> students;
接口TeacherMapper
package com.atctbu.hjt.dao;
import com.atctbu.hjt.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface TeacherMapper {
//获取老师
//List getTeacher();
//获取老师的信息和老师下的所有学生信息
Teacher getTeacher(@Param("tid")int id);
//按子查询
Teacher getTeacher2(@Param("tid")int id);
}
<mapper namespace="com.atctbu.hjt.dao.TeacherMapper">
<select id="getTeacher" resultMap="TeacherAndStudent">
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="TeacherAndStudent" 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>
mapper>
测试类
@Test
public void getTeacher(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);
/*
查询结果:
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}]}
*/
sqlSession.close();
}
<mapper namespace="com.atctbu.hjt.dao.TeacherMapper">
<select id="getTeacher2" resultMap="TeacherAndStudent2">
select * from mybatis.teacher where id =#{tid}
select>
<resultMap id="TeacherAndStudent2" type="Teacher">
<collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId"/>
resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from mybatis.student where tid = #{id}
select>
mapper>
测试类
@Test
public void getTeacher2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher2(1);
System.out.println(teacher);
/*
查询结果:没查teacherID所以为0
Teacher{id=0, 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}]}
*/
sqlSession.close();
}
注意点:
面试高频
什么是动态 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
创建一个基础工程
导包
编写配置文件
mybatis-config.xml
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
IDUtils
package com.atctbu.hjt.utils;
import org.junit.Test;
import java.util.UUID;
public class IDUtils {
public static String getId(){
return UUID.randomUUID().toString().replaceAll("-","");
}
@Test
public void test(){
System.out.println(IDUtils.getId());
}
}
//4abb3e9f14d54e6ba1b0c30035f408ad
编写实体类
public class Blog {
private String id;
private String title;
private String author;
private Date createtime; //属性名和字段名不一致
private int views;
编写实体类对应Mapper接口和Mapper.xml文件
插入数据
@Test
public void Test01(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IDUtils.getId());
blog.setTitle("Mybatis从入门到放弃");
blog.setAuthor("何锦涛");
blog.setCreatetime(new Date());
blog.setViews(9999);
mapper.addBlog(blog);
blog.setId(IDUtils.getId());
blog.setTitle("Java从入门到入土");
mapper.addBlog(blog);
blog.setId(IDUtils.getId());
blog.setTitle("Spring从入门到放弃");
mapper.addBlog(blog);
blog.setId(IDUtils.getId());
blog.setTitle("微服务从入门到入土");
mapper.addBlog(blog);
sqlSession.close();
}
例子:
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
if>
select>
这条语句提供了可选的查找文本功能。如果不传入 “title”,那么所有处于 “ACTIVE” 状态的 BLOG 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果(细心的读者可能会发现,“title” 的参数值需要包含查找掩码或通配符字符)。
如果希望通过 “title” 和 “author” 两个参数进行可选搜索该怎么办呢?首先,我想先将语句名称修改成更名副其实的名称;接下来,只需要加入另一个条件即可。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
if>
select>
接口类:
package com.atctbu.hjt.dao;
import com.atctbu.hjt.pojo.Blog;
import java.util.List;
import java.util.Map;
public interface BlogMapper {
//查询博客
List<Blog> queryBlogIF(Map map);
}
BlogMapper.xml
<mapper namespace="com.atctbu.hjt.dao.BlogMapper">
<insert id="addBlog" parameterType="blog">
insert into mybatis.blog (id,title,author,create_time,views)
value (#{id},#{title},#{author},#{createtime},#{views})
insert>
<select id="queryBlogIF" resultType="blog" parameterType="map" >
select * from mybatis.blog
where 1=1
<if test="title != null">
and title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
select>
mapper>
where标签解决where 1=1
<select id="queryBlogIF" resultType="blog" parameterType="map" >
select * from mybatis.blog
<where>
<if test="title != null">
title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
where>
select>
测试代码
@Test
public void queryBlogIF(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title","Java从入门到入土");
map.put("title","Mybatis从入门到放弃");
List<Blog> blogs = mapper.queryBlogIF(map);
for (Blog blog:blogs
) {
System.out.println(blog);
}
/*
select * from mybatis.blog WHERE title = ?
* 不加参数,全查出来了
* map.put("title","Java从入门到入土"):Blog{id=d0d1d39093ca458c84ed00bb7bc350f1, title='Java从入门到入土', author='何锦涛', createtime=Tue Nov 24 15:48:39 CST 2020, views=1000}
* title不为空了,就执行后面的and了
* put两个title好像只能查到最后面那个
* */
sqlSession.close();
}
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG(这可能是管理员认为,与其返回大量的无意义随机 Blog,还不如返回一些由管理员精选的 Blog)。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
when>
<otherwise>
AND featured = 1
otherwise>
choose>
select>
接口类BlogMapper:
package com.atctbu.hjt.dao;
import com.atctbu.hjt.pojo.Blog;
import java.util.List;
import java.util.Map;
public interface BlogMapper {
//Choose查询博客
List<Blog> queryBlogChoose(Map map);
}
BlogMapper.xml
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<choose>
<when test="title != null">
title = #{title}
when>
<when test="author != null">
and author = #{author}
when>
<otherwise>
and views = #{views}
otherwise>
choose>
where>
select>
测试类:
@Test
public void queryBlogChoose(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title","Java从入门到入土");
map.put("author","何锦涛");
map.put("views",9999);
List<Blog> blogs = mapper.queryBlogChoose(map);
for (Blog blog:blogs
) {
System.out.println(blog);
}
/*
select * from mybatis.blog WHERE title = ?
* 只会选择其中满足的一个去实现,类似switch
**/
sqlSession.close();
}
如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:
SELECT * FROM BLOG
WHERE
这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:
SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’
这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。
MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
if>
<if test="title != null">
AND title like #{title}
if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
if>
where>
select>
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},if>
<if test="password != null">password=#{password},if>
<if test="email != null">email=#{email},if>
<if test="bio != null">bio=#{bio}if>
set>
where id=#{id}
update>
这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
来看看与 set 元素等价的自定义 trim 元素吧:
<trim prefix="SET" suffixOverrides=",">
...
trim>
例子:
接口
package com.atctbu.hjt.dao;
import com.atctbu.hjt.pojo.Blog;
import java.util.List;
import java.util.Map;
public interface BlogMapper {
//更新博客
int updateBlog(Map map);
}
BlogMapper.xml
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title != null">
title = #{title},
if>
<if test="author != null">
author = #{author}
if>
set>
where id = #{id}
update>
测试类
@Test
public void updateBlog(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
// map.put("title","Java从入门到入土2");
map.put("author","何锦涛2");
map.put("id", "2");
mapper.updateBlog(map);
/*
update mybatis.blog SET author = ? where id = ?
*/
sqlSession.close();
}
所谓的动态SQL,本质还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码
有的时候,我们可能会将一些公共的部分抽取出来,方便复用
<sql id="if-title-author">
<if test="title != null">
title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
sql>
<select id="queryBlogIF" resultType="blog" parameterType="map" >
select * from mybatis.blog
<where>
<include refid="if-title-author">include>
where>
select>
注意事项:
select * from user where 1=1 and
<foreach item="id" collection="ids"
open="(" separator="or" close=")">
#{id}
</foreach>
(id=1 or id=2 or id=3)
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
foreach>
select>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tvxMtMDR-1616654175267)(http://m.qpic.cn/psc?/V11Mzdsz1sKJyi/45NBuzDIW489QBoVep5mcRf7qlGoXUECFojtHnIozPMZg.RgO7oIKdOIVm73ckiiX10uKMDU3Yb3zxKnsk56nibm5gR7irQ1O1JpAJUJ8!/b&bo=pgOIAQAAAAADJy4!&rf=viewer_4)]
例子:
接口类:
package com.atctbu.hjt.dao;
import com.atctbu.hjt.pojo.Blog;
import java.util.List;
import java.util.Map;
public interface BlogMapper {
//查询1-2-3号记录的博客
List<Blog> queryBlogForeach(Map map);
}
BlogMapper.xml
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id = #{id}
foreach>
where>
select>
测试类:
@Test
public void queryBlogForeach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
ArrayList<Integer> ids = new ArrayList<Integer>();
ids.add(1); //select * from mybatis.blog WHERE ( id = ? )
ids.add(2); //select * from mybatis.blog WHERE ( id = ? or id = ? )
ids.add(3); //select * from mybatis.blog WHERE ( id = ? or id = ? or id = ? )
map.put("ids",ids); //不加参数时全查出来 select * from mybatis.blog
List<Blog> blogs = mapper.queryBlogForeach(map);
for (Blog blog:blogs
) {
System.out.println(blog);
}
sqlSession.close();
}
动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就好了
建议:
查询 连接数据库 耗资源
一次查询的结果给它暂存在一个可以直接取到的地方!---》内存 : 缓存
我们再次查询相同数据的时候,直接走缓存,就不用走数据库了
什么是缓存[ Cache ]?
为什么使用缓存?
什么样的数据能使用缓存?
1、开启日志
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
2、测试在一个Session中查询两次记录
public void queryUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
User user2 = mapper.queryUserById(1); //只走一次查询select * from mybatis.user where id=?
System.out.println(user2);
System.out.println(user==user2); //true
sqlSession.close();
}
//查询两个ID相同:只走一次查询select * from mybatis.user where id=?
//查询两个ID不同:有两个SQL语句,并且为false
3、测试在一个Session中查询一个记录再更新一个记录再查询一个记录
public void queryUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
mapper.updateUser(new User(3,"王大拿","12121212"));
User user2 = mapper.queryUserById(3); //select * from mybatis.user where id=?
System.out.println(user2);
System.out.println(user==user2); //false
sqlSession.close();
}
//select * from mybatis.user where id=?
//update mybatis.user set name=?,pwd=? where id=?
//select * from mybatis.user where id=? 又执行了一次查询,缓存没了
4、缓存失效的情况:
增删改操作,可能会改变原来的数据,所以必定会刷新缓存
查询不同的东西
查询不同的Mapper
手动清除缓存
sqlSession.clearCache();//手动清理缓存
@Test
public void queryUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
// mapper.updateUser(new User(3,"王大拿","12121212"));
sqlSession.clearCache();//手动清理缓存
User user2 = mapper.queryUserById(1); //select * from mybatis.user where id=?
System.out.println(user2);
System.out.println(user==user2); //false
sqlSession.close();
}
//手动清理缓存后,即使查询相同的用户还是有两句SQL语句
一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段!
一级缓存相当于一个Map
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
这些属性可以通过 cache 元素的属性来修改。比如:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
步骤:
1. 开启全局缓存
设置名 | 描述 | 有效值 |
---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false |
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="cacheEnabled" value="true"/>
settings>
2. **开启二级缓存**
<mapper namespace="com.atctbu.hjt.dao.UserMapper">
<cache/>
<select id="queryUserById" resultType="User" parameterType="int">
select * from mybatis.user where id=#{id}
select>
<update id="updateUser" parameterType="user">
update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}
update>
mapper>
(可以手动设置useCache为true|false)
<mapper namespace="com.atctbu.hjt.dao.UserMapper">
<cache/>
<select id="queryUserById" resultType="User" parameterType="int" useCache="true">
select * from mybatis.user where id=#{id}
select>
mapper>
也可以自定义参数
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
3. **测试**
public void queryUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
sqlSession.close(); //结束后会把缓存丢到二级去
User user2 = mapper2.queryUserById(1); //select * from mybatis.user where id=?
System.out.println(user2);
System.out.println(user==user2); //false
sqlSession2.close();
}
/*
两个不同的sqlSession,一级缓存会查询两次
开启二级缓存后
二级缓存在一级缓存close()死掉后被丢到二级去
只执行了一次查询SQL操作
而却user=user2
*/
1、问题:我们需要将实体类序列化,否则就会报错
Caused by: java.io.NotSerializableException: com.kuang.pojo.User
小结
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I1Q38201-1616654175271)(http://m.qpic.cn/psc?/V11Mzdsz1sKJyi/45NBuzDIW489QBoVep5mcQgmwGHT7hjSanKdwxGBiWFizv.f1PPMO33dJns3m7Vm3dA6vrj2TVA0tCEtQ5EPuYokyc2Yyub99ijgG.f.Vps!/b&bo=awRIAgAAAAADJyc!&rf=viewer_4)]
Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存
要在程序中使用Ehcache,先导包!
<dependency>
<groupId>org.mybatis.cachesgroupId>
<artifactId>mybatis-ehcacheartifactId>
<version>1.1.0version>
dependency>
使用自定义缓存
除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。
<cache type="com.domain.something.MyCustomCache"/>
使用Ehcache
<cache type="org.mybatis.caches.ehcache.EhBlockingCache"/>
自定义配置Ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<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>
之后会使用Ridis数据库来做缓存