目录
方式2: 使用RowBounds集合来保存分页需要数据,来进行分页 编辑
一、动态SQL注解
分类:
- 脚本sql:XML配置方式的动态SQL,是 用<script>的方式把它照搬过来,用注解来实现。适用于xml 配置转换到注解配置
- 方法中构建sql:@SelectProvider @InsertProvider @UpdateProvider @DeleteProvider 这些方法的使用
- SQL 语句构造器:实现动态 SQL 的内容
二、脚本动态sql
在sql语句中加入标签,按照之前sqlmap中的动态sql的样式书写
实现:
StudentMapper.java
//脚本动态sg1 @Select("<script>" + "select *from student" +"<where>" +"<if test=\"ssex != null\" > and ssex=#{ssex}</if>" +"<if test=\"classid != 0\"> and classid=#{classid}</if>" +"</where>" +"</script>") public List<Student>findStudentScript(Student s);
测试:
SqlSession sqlSession = DaoUtil.getSqlSession(); StudentMapper stuMapper = sqlSession.getMapper(StudentMapper.class); Student s = new Student(); s.setClassid(1); s.setSsex("男"); // s.setSname("zkt89"); // s.setBirthday(new Date()); // s.setSid(4); List<Student> sList = stuMapper.findStudentScript(s); sList.forEach(System.out::println); DaoUtil.closeResource(sqlSession);
三、方法中构建sql
在接口中定义内部类,来构建需要的动态sql语句,比使用标签的方式结构更加清晰
@SelectProvider 的用法
步骤
- 创建 SQL 语句类 该类包含需要动态生成的 SQL 语句;
- 创建Mapper接口类 该类和配置文件的接口文件一 样,用来处理数据库操作;
- 利用@SelectProvider 将 SQL 语句类和 Mapper 接 口类关联,利用 @SelectProvider 的 type 属 性和 method 属性;
- 测试验证 编写测试类,测试动态生成的 SQL 语句是否准确。
实现:
需要写一个内部类 type 对应内部类名 method 对应其方法名
StudentMapper.java
@SelectProvider(type = SqlStudent.class,method ="selectStudentFunc" ) public List<Student>findStudentFunc(Student s); class SqlStudent{ public String selectStudentFunc(Student s) { String sql="select * from student where 1=1"; if (s.getSsex() != null) { sql += " and ssex = #{ssex}"; } if (s.getClassid() != 0) { sql += " and classid = #{classid}"; } return sql; } }
测试:
SqlSession sqlSession = DaoUtil.getSqlSession(); StudentMapper stuMapper = sqlSession.getMapper(StudentMapper.class); Student s = new Student(); s.setClassid(1); s.setSsex("男"); // s.setSname("zkt89"); // s.setBirthday(new Date()); // s.setSid(4); // List<Student> sList = stuMapper.findStudentScript(s); List<Student> sList = stuMapper.findStudentFunc(s); sList.forEach(System.out::println); DaoUtil.closeResource(sqlSession);
其他的动态 SQL 方法
- @InsertProvider 创建动态的 INSERT 语句
- @UpdateProvider 创建动态的 UPDATE 语句
- @DeleteProvider 创建动态的 DELETE 语句
四、SQL 语句构造器
- 功能 解决 Java 代码中嵌入 SQL 语句,通过简单地创建一个实例来调用方法生成SQL语句
- 特点 没有过多的使用类如 and的连接词
SQL 语句构造器的常用方法
属性名 说明 SELECT 开始或插入到 SELECT 子句,可以被多次调用,参数也会添加到 SELECT子句。 FROM 开始或插入到 FROM 子句,可以被多次调用,参数也会添加到 FROM 子句 WHERE 插入新的 WHERE 子句条件,可以多次被调用 OR / AND 使用 OR / AND 来分隔当前的 WHERE 子句的条件 DELETE_FROM 开始一个 delete 语句并指定需要从哪个表删除的表名。 INSERT_INTO 开始一个 insert 语句并指定需要插入数据的表名 VALUES 插入到 insert 语句中。第一个参数是要插入的列名,第二个参数则是该列的值。 UPDATE 开始一个 update 语句并指定需要更新的表名 SET 针对 update 语句,插入到 "set" 列表中 实现:
StudentMapper.java
@SelectProvider(type = SqlStudent.class, method = "selectStudentGZQ") public List<Student> findStudentGZQ(Student s); @InsertProvider(type = SqlStudent.class, method = "insertStudentGZQ") public int addStudentGZQ(Student s); @UpdateProvider(type = SqlStudent.class, method = "updateStudentGZQ") public int editStudentGZQ(Student s); @DeleteProvider(type = SqlStudent.class, method = "deleteStudentGZQ") public int delStudentGZQ(int sid); class SqlStudent{ public String selectStudentFunc(Student s) { String sql = "select * from student where 1=1 "; if(s.getSsex() != null) { sql += " and ssex = #{ssex}"; } if(s.getClassid() != 0) { sql += " and classid = #{classid}"; } return sql; } public String selectStudentGZQ(Student s) { return new SQL() { { SELECT("*"); FROM("student"); if(s.getSsex() != null) { WHERE("ssex = #{ssex}"); } if(s.getClassid() != 0) { WHERE("classid = #{classid}"); } } }.toString(); } public String insertStudentGZQ(Student s) { return new SQL() { { INSERT_INTO("student"); if(s.getBirthday() != null) { VALUES("birthday","#{birthday}"); } if(s.getSsex() != null) { VALUES("ssex","#{ssex}"); } if(s.getSname() != null) { VALUES("sname","#{sname}"); } if(s.getClassid() != 0) { VALUES("classid","#{classid}"); } } }.toString(); } public String updateStudentGZQ(Student s) { return new SQL() { { UPDATE("student"); if(s.getSname() != null) { SET("sname = #{sname}"); } if(s.getBirthday() != null) { SET("birthday = #{birthday}"); } if(s.getSsex() != null) { SET("ssex = #{ssex}"); } if(s.getClassid() != 0) { SET("classid = #{classid}"); } WHERE("sid = #{sid}"); } }.toString(); } public String deleteStudentGZQ(int sid) { return new SQL() { { DELETE_FROM("student"); WHERE("sid = #{sid}"); } }.toString(); } }
测试:
public class Test01 { public static void main(String[] args) { SqlSession sqlSession = DaoUtil.getSqlSession(); StudentMapper stuMapper = sqlSession.getMapper(StudentMapper.class); Student s = new Student(); s.setClassid(1); s.setSsex("男"); // s.setSname("zkt89"); // s.setBirthday(new Date()); // s.setSid(4); // List<Student> sList = stuMapper.findStudentScript(s); // List<Student> sList = stuMapper.findStudentFunc(s); List<Student> sList = stuMapper.findStudentGZQ(s); sList.forEach(System.out::println); // int ret = stuMapper.addStudentGZQ(s); // int ret = stuMapper.editStudentGZQ(s); // int ret = stuMapper.delStudentGZQ(3); // if (ret > 0) { // sqlSession.commit(); // System.out.println("成功"); // } else { // sqlSession.rollback(); // System.out.println("失败"); // } DaoUtil.closeResource(sqlSession); } }
查询
添加
修改
删除
五、分页
方式1: 使用Map集合来保存分页需要数据,来进行分页
使用注解实现:
StudentMapper.java
@Select("select * from student limit #{param1}, #{param2}") public List<Student> findStudentPageLimit(int curpage, int sizepage);
测试:
SqlSession sqlSession = DaoUtil.getSqlSession(); StudentMapper stuMapper = sqlSession.getMapper(StudentMapper.class); // 物理分页 List<Student> slist = stuMapper.findStudentPageLimit((1 - 1) * 3, 3); slist.forEach(System.out::println); DaoUtil.closeResource(sqlSession);
方式2: 使用RowBounds集合来保存分页需要数据,来进行分页
实现:
StudentMapper.java
@Select("select * from student") public List<Student> findStudentPageRb(RowBounds rb);
测试:
SqlSession sqlSession = DaoUtil.getSqlSession(); StudentMapper stuMapper = sqlSession.getMapper(StudentMapper.class); // 物理分页 // List<Student> slist = stuMapper.findStudentPageLimit((1-1)*3, 3); // 逻辑分页 RowBounds rb = new RowBounds((1 - 1) * 3, 3); List<Student> slist = stuMapper.findStudentPageRb(rb); slist.forEach(System.out::println); DaoUtil.closeResource(sqlSession);
方式3: 使用分页插件来进行分页
实现:
导入jar包
配置插件 在主配置文件中写入
<!-- 第三方插件的引入 --> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin> </plugins>
StudentMapper.java
@Select("select * from student") public List<Student> findStudentPagePh();
测试:
SqlSession sqlSession = DaoUtil.getSqlSession(); StudentMapper stuMapper = sqlSession.getMapper(StudentMapper.class); // 物理分页 // List<Student> slist = stuMapper.findStudentPageLimit((1-1)*3, 3); // 逻辑分页 // RowBounds rb = new RowBounds((1-1)*3, 3); // List<Student> slist = stuMapper.findStudentPageRb(rb); Page<Student> startPage = PageHelper.startPage(5, 3); List<Student> slist = stuMapper.findStudentPagePh(); PageInfo<Student> pageInfo = new PageInfo<>(slist); slist.forEach(System.out::println); System.out.println(startPage); System.out.println("-------------------"); System.out.println(pageInfo); DaoUtil.closeResource(sqlSession);
分页插件说明
六、延迟加载和立即加载(表联查)
立即加载是: 不管用不用信息,只要调用,马上发起查询并进行加载
比如: 当我们查询学生信息时,就需要知道学生在哪个班级中,所以就需要立马去查询班级的信息
延迟加载是: 在真正使用数据时才发起查询,不用的时候不查询,按需加载(也叫 懒加载) 比如: 在查询班级信息,每个班级都会有很多的学生(假如每个班有100个学生),如果我们只是查看 班级信息,但是学生对象也会加载到内存中,会造成浪费。 所以我门需要进行懒加载,当确实需要查看班级中的学生信息,我门在进行加载班级中的学生信息。 通常: 一对多,或者多对多的是需要使用延迟加载
延迟加载的配置
如果设置 lazyLoadingEnabled = false,则禁用延迟加载,会级联加载所有关联对象的数据 如果设置 lazyLoadingEnabled = true,默认情况下mybatis 是按层级延时加载的。 aggressiveLazyLoading = true,mybatis 是按层级延时加载
aggressiveLazyLoading = false,mybatis 按需求加载。
延迟加载的sqlmap
实现:
StudentMapper.java
// FetchType.EAGER 立即加载 // FetchType.LAZY @Results({ @Result(column = "classid",property = "bj", one=@One( select = "com.ape.mapper.BanjiMapper.findBanjiByClassid", fetchType = FetchType.LAZY)) }) @Select("select * from student ") public List<Student> findStudentAndClass();
测试:
SqlSession sqlSession = DaoUtil.getSqlSession(); StudentMapper stuMapper = sqlSession.getMapper(StudentMapper.class); List<Student> slist = stuMapper.findStudentAndClass(); System.out.println(slist.get(0)); DaoUtil.closeResource(sqlSession);
七、了解一级缓存和二级缓存的功能(基本上不用,面试)
缓存
缓存(cache),数据交换的缓冲区,当应用程序需要读取数据时,先从数据库中将数据取出,放置在 缓冲区中,应用程序从缓冲区读取数据。
- 特点 数据库取出的数据保存在内存中,具备快速读取和 使用。
- 限制 读取时无需再从数据库获取,数据可能不是最新的;
缓存的术语
针对缓存数据:
- 命中 需要的数据在缓存中找到结果。
- 未命中 需要的数据在缓存中未找到,重新获取。
MyBatis 缓存
- 功能 减少Java Application 与数 据库的交互次数,从而提升程 序的运行效率;
- 方式 通过配置和定制。
缓存的适用性
- 适合使用缓存: 经常查询并且不经常改变的 数据的正确与否对最终结果影响不大的 比如:一个公司的介绍,新闻等
- 不适合用于缓存: 经常改变的数据 数据的正确与否对最终结果影响很大 比如商品的库存,股市的牌价等
MyBatis 缓存分类
- 一级缓存 会话 session 级别的缓存,针对一 次会话操作内;
- 二级缓存 映射器级别的缓存,针对不同 Namespace 的映射器;
- 自定义缓存 根据各类不同的缓存机制,自定义 缓存的实现方式。
一级缓存
关闭sqlsession 或者情况sqlsession缓存都可以实现
注意:当调用sqlsession的修改,添加,删除,commit(),close() 等方法是, 就会清空一级缓存
一级缓存的配置
一级缓存工作流程
一级缓存失效情况
- 不同SqlSession对应不同的一级缓存
- 同一个SqlSession单查询条件不同
- 同一个SqlSession两次查询期间执行了任何一次增删改操作
- 同一个SqlSession两次查询期间手动清空了缓存
实现:
StudentMapper.java
@Select("select * from student where sid = #{v}") public Student findStudentBySid(int sid);
测试:
SqlSession sqlSession = DaoUtil.getSqlSession(); StudentMapper stuMapper = sqlSession.getMapper(StudentMapper.class); Student s = stuMapper.findStudentBySid(5); System.out.println(s); // 业务 -- DML 一级缓存失效 // int ret = stuMapper.addStudent(new Student()); // 主动清空缓存 sqlSession.clearCache(); Student s2 = stuMapper.findStudentBySid(5); System.out.println(s2); System.out.println(s == s2); DaoUtil.closeResource(sqlSession);
二级缓存(了解)
配置步骤:
二级缓存—注解
配置步骤:
- Mybatis 的二级缓存相对于一级缓存来说, 实现了缓存数据的共享,可控性也更强;
- 极大可能会出现错误数据,有设计上的缺陷, 安全使用的条件比较苛刻;
- 分布式环境下,必然会出现读取到错误 数据,所以不推荐使用。
垃圾中的垃圾 绝对不用
示例完整代码
StudentMapper.java
//注解二级缓存开始 @CacheNamespace(blocking = true) public interface StudentMapper { @Select("select * from student limit #{param1}, #{param2}") public List<Student> findStudentPageLimit(int curpage, int sizepage); @Select("select * from student") public List<Student> findStudentPageRb(RowBounds rb); @Select("select * from student") public List<Student> findStudentPagePh(); // FetchType.EAGER 立即加载 // FetchType.LAZY @Results({ @Result(column = "classid",property = "bj", one=@One( select = "com.ape.mapper.BanjiMapper.findBanjiByClassid", fetchType = FetchType.LAZY)) }) @Select("select * from student ") public List<Student> findStudentAndClass(); @Select("select * from student where sid = #{v}") public Student findStudentBySid(int sid); @Insert("insert into student(sname) values(#{sname})") public int addStudent(Student s); }
主配置文件
<?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> <properties resource="jdbc.properties"> </properties> <!-- <settings> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> </settings> --> <typeAliases> <package name="com.zkt.bean"/> </typeAliases> <!-- 第三方插件的引入 --> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin> </plugins> <!-- 和spring整合后 environments配置将废除 --> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理 --> <transactionManager type="JDBC" /> <!-- 数据库连接池 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments> <mappers> <!-- <mapper resource="com/zkt/mapper/StudentMapper.xml" /> --> <package name="com.zkt.mapper"/> </mappers> </configuration>