MyBatis
今天大年初一, 你在学习! 不学习做什么, 斗地主... 人都凑不齐. 学习吧, 学习使我快乐! 除了诗和远方还有责任, 我也想担当, 我也想负责, 可臣妾做不到啊, 怎么办? 你说怎么办, 为啥人家能做到你做不到, 因为人家比你多做了那么一点点. 哪一点点? 就那么一点点, 只要你也多做那么一点点, 不就做到了!... 就那么一点点呀, 我回顾 SE 去了. 万丈高楼平地起, 基础打的牢, 怕什么狂风暴雨
MyBatis 动态 SQL
MyBatis 为了解决通过一些不确定性的条件进行 SQL 语句的拼接操作的问题, 提供了动态 SQL. 具体来说, 就是提供了一些标签 <if> <where> <trim> <set> <choose> <foreach> 等. 写出可扩展 SQL 语句
MyBatis 采用功能强大的基于 OGNL 的表达式来简化操作
OGNL( Object Graph Navigation Language )对象图导航语言, 这是一种强大的表达式语言, 通过它可以非常方便的来操作对象属性. 类似于 EL 表达式, 例:
访问对象属性: person.name
调用方法: person.getName()
调用静态属性 / 方法: @java.lang.Math@PI
调用构造方法: new com.bean.Person('admin').name
运算符: +,-*,/,%
逻辑运算符: in,not in,>,>=,<,<=,==,!=
注意: xml 中特殊符号如 ",>,<等这些都需要使用转义字符
标签
1)<if>: 用于完成简单的判断. 只有一个属性 test 用于判断条件是否成立
2) <where>: 在 SQL 语句中添加 WHERE 关键字, 作用: 去掉 where 后面第一个条件前面的 and / or .
- <select id="getBook" resultType="main.beans.Book">
- SELECT id,title,author,price
- FROM books
- <where>
- <if test="id != null"> and id= #{id}</if>
- <if test="title != null"> and title = #{title}</if>
- </where>
- </select>
3)<trim> : 可以在条件判断完的 SQL 语句的前后 添加内容 或者去掉指定的内容. 去掉第一个或最后一个
prefix: 添加前缀 prefixOverrides: 去掉前缀
suffix: 添加后缀 suffixOverrides: 去掉后缀
- <delete id="deleteBook">
- DELETE FROM books
- <trim prefix="WHERE" suffixOverrides="and">
- <if test="id != null">id = #{id} and</if>
- <if test="title != null">itle = #{title} and</if>
- </trim>
- </delete>
4) <set> : 在修改的操作中, 去掉 SQL 语句中多出的逗号, 即在 sql 语句中最后句可能多出的逗号
- <update id="updateBook">
- UPDATE books
- <set>
- <if test="title != null">title = #{title},</if>
- <if test="author != null">author = #{author},</if>
- </set>
- where id = #{id}
- </update>
5) <sql> 标签是用于抽取可重用的 sql 片段, 将使用频繁的 SQL 片段抽取出来, 不仅仅只提取整条 sql 语句, 字段也可以提取
id: 指定被提取的 sql 片段唯一标识被引用
引用: 在任何需要插入此 sql 片段的语句中使用 <include refid="id 标识"></include> 引入即可
- <sql id="bookFields">
- id,title,author,price,sales,stock,img_path
- </sql>
- <insert id="insertBook" useGeneratedKeys="true" keyProperty="id">
- INSERT INTO books (<include refid="bookFields"></include>)
- VALUES(#{id},#{title},#{author},#{price},#{sales},#{stock},#{imgPath})
- </insert>
6) <choose> <when> <otherwise> : 用于分支判断, 最终只会满足其中的一个分支. 类似于 switch case 语句.
- <select id="selectBookPrice" resultType="main.beans.Book">
- SELECT <include refid="bookFields"></include>
- FROM books
- <where>
- <choose>
- <when test="id != null">id = #{id}</when>
- <otherwise>price> #{price}</otherwise>
- </choose>
- </where>
- </select>
7) <foreach>: 主要用于循环迭代
collection: 要迭代的集合
item: 当前从集合中迭代出的元素赋值的变量
open: 开始字符
close: 结束字符
separator: 指定元素与元素之间的分隔符
index:
迭代的是 List 集合: index 表示当前元素的下标
迭代的 Map 集合: index 表示当前元素的 key
注意: 此操作属于批量操作需在 properties 配置的 url 中添加 allowMultiQueries=true 开启批处理
- <select id="getBooks" resultType="main.beans.Book">
- SELECT <include refid="bookFields"></include>
- FROM books where id in
- <foreach collection="ids" item="id" separator="," open="(" close=")">
- #{id}
- </foreach>
- </select>
MyBatis 缓存机制
MyBatis 包含一个非常强大的查询缓存特性, 它可以非常方便地配置和定制. 缓存可以极大的提升查询效率
MyBatis 系统中默认定义了两级缓存: 一级缓存, 二级缓存
默认情况下, 只有一级缓存 (SqlSession 级别的缓存, 也称为本地缓存) 开启.
二级缓存需要手动开启和配置, 他是基于 namespace 级别的缓存. 为了提高扩展性. MyBatis 定义了缓存接口 Cache, 支持第三方缓存.
一级缓存
1) 一级缓存(local cache), 即本地缓存, 作用域默认为 sqlSession. 每个 sqlSession 对象都有自己的一级缓存, 相互独立不共享. 当 Session flush 或 close 后, 该 Session 中的所有 Cache 将被清空.
2) 本地缓存不能被关闭, 但可以调用 clearCache() 来清空本地缓存, 或者改变缓存的作用域.
3) 在 mybatis3.1 之后, 可以配置本地缓存的作用域. 在 mybatis.xml 中配置
4) 一级缓存的工作机制, 同一次会话期间只要查询过的数据都会保存在当前 SqlSession 的一个 Map 中
key: hashCode + 查询的 SqlId + 编写的 sql 查询语句 + 参数
缓存机制:
基于相同 sqlSession 多次查询, 每次查询都会先从缓存中获取数据, 如果缓存中没获取到数据, 则从数据库中获取数据, 之后, 将数据存放到一级缓存.
一级缓存的失效问题
1)不同的 SqlSession 对应不同的一级缓存
2)同一个 SqlSession 但是查询条件不同
3)同一个 SqlSession 两次查询期间执行了任何一次增删改操作
增删改操作, 会默认清空缓存.
4)同一个 SqlSession 两次查询期间手动清空了缓存
总结: 当同一 SqlSession 多次查询同一语句时, 且中间未有增删改或手动刷新, 关闭, 清空 clearCache() 过缓存, 便会直接从缓存中取数据. 若开启了二级缓存则会先从二级缓存读取, 若二级缓存里没有再去一级缓存读取, 如果只想从一级缓存中读取可在对应 <select> 配置属性中设置 useCache="false" 来关闭当前二级缓存, 注意增删改不涉及缓存机制
二级缓存
默认关闭, 使用需要满足三个条件才被开启
二级缓存: namespace 级别缓存 (sqlSessionFacotry) 级别的缓存, 作用域更广但是需要在 sqlSessionFacotry 内的 sqlSession 提交或关闭以后才会生效. 即才会将 sqlSession 中的缓存存到二级缓存中.
二级缓存使用的步骤:
1 全局配置文件中开启二级缓存 < setting name="cacheEnabled" value="true"/>
2 在需要使用二级缓存的映射文件 ( <mapper> 配置文件) 使用 cache 标签配置缓存 < cache />
3 注意: POJO 需要实现 Serializable 接口
<cache> 标签属性
1 eviction="LRU": 缓存回收策略: 默认的是 LRU.
LRU - 最近最少使用的: 移除最长时间不被使用的对象.
FIFO - 先进先出: 按对象进入缓存的顺序来移除它们.
SOFT - 软引用: 移除基于垃圾回收器状态和软引用规则的对象.
WEAK - 弱引用: 更积极地移除基于垃圾收集器状态和弱引用规则的对象.
2 flushInterval: 刷新间隔, 单位毫秒
默认情况是不设置, 也就是没有刷新间隔, 缓存仅仅调用语句时刷新
3 size: 引用数目, 正整数
代表缓存最多可以存储多少个对象, 太大容易导致内存溢出
4 readOnly: 只读, 默认是 false. 虽然设为 true 能提高效率但是为了安全, 因此不需设置此项
5 type: 引入缓存类库(第三方缓存)
其他相关属性设置
1) 全局 setting 的 cacheEnable:
配置二级缓存的开关, 一级缓存一直是打开的.
2) select 标签的 useCache 属性:
配置这个 select 是否使用二级缓存. 一级缓存一直是使用的
3) sql 标签的 flushCache 属性:
增删改默认 flushCache=true.sql 执行以后, 会同时清空一级和二级缓存.
查询默认 flushCache=false.
4) sqlSession.clearCache(): 只是用来清除一级缓存.
二级缓存机制
基于相同 sqlSessionFactory 下, 多次查询, 优先去二级缓存中获取数据, 二级缓存获取不到数据, 去一级缓存中获取数据, 一级缓存中也获取不到数据, 直接去数据库中查询数据.
查询后, 将数据直接存放一级缓存, 提交或关闭 sqlSession 时, 才将一级缓存中的数据, 缓存到二级缓存中.
EhCache 第三方缓存
EhCache 是一个纯 Java 的进程内缓存框架, 具有快速, 精干等特点, 是 Hibernate 中默认的 CacheProvider
整合 EhCache 缓存的步骤:
1 导入 ehcache 包, 以及整合包, 日志包
- ehcache-core-2.6.8.jar,mybatis-ehcache-1.0.3.jar
- slf4j-API-1.6.1.jar,slf4j-log4j12-1.6.2.jar
2 编写 ehcache.xml 配置文件
3 配置 cache 标签
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
第三方缓存的使用机制, 同二级缓存一样也需要 setting 的 cacheEnable 设置为 true 并且实现序列化接口 Serializable
- <?xml version="1.0" encoding="UTF-8"?>
- <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
- <!-- 磁盘保存路径 -->
- <diskStore path="F:\code\mysql\ehcache" />
- <defaultCache
- maxElementsInMemory="1000"
- maxElementsOnDisk="10000000"
- eternal="false"
- overflowToDisk="true"
- timeToIdleSeconds="120"
- timeToLiveSeconds="120"
- diskExpiryThreadIntervalSeconds="120"
- memoryStoreEvictionPolicy="LRU">
- </defaultCache>
- </ehcache>
MyBatis 逆向工程
MyBatis Generator: 简称 MBG, 是一个专门为 MyBatis 框架使用者定制的代码生成器, 可以快速的根据表生成对应的映射文件, 接口, 以及 bean 类. 支持基本的增删改查, 以及 QBC 风格的条件查询. 但是表连接, 存储过程等这些复杂 sql 的定义需要我们手工编写
Mybatis 使用逆向工程步骤
1. 加入逆向工程相关的 jar 包.
mybatis-generator-core-1.3.2.jar
2. 配置逆向工程的配置文件: mbg.xml ==> 生成的版本 , javaBean,Mapper 接口, 映射文件的生成策略 , 分析的表 .
mbg.xml 文件直接放在项目工程根目录下方便路径配置
<?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE generatorConfiguration
- PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
- "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
- <generatorConfiguration>
- <!--
- targetRuntime: 执行生成的逆向工程的版本
- MyBatis3Simple: 生成基本的 CRUD
- MyBatis3: 生成带条件的 CRUD
- -->
- <context id="DB2Tables" targetRuntime="MyBatis3">
- <!-- 数据库连接设置 -->
- <jdbcConnection driverClass="com.mysql.jdbc.Driver"
- connectionURL="jdbc:mysql://localhost:3306/bookstore?allowMultiQueries=true"
- userId="root"
- password="12345">
- </jdbcConnection>
- <!-- javaBean 的生成策略 bean 的存放路径 -->
- <javaModelGenerator targetPackage="main.beans" targetProject=".\src">
- <property name="enableSubPackages" value="true"/>
- <property name="trimStrings" value="true"/>
- </javaModelGenerator>
- <!-- SQL 映射文件的生成策略 mapper.xml 文件路径 -->
- <sqlMapGenerator targetPackage="main.mapper" targetProject=".\conf">
- <property name="enableSubPackages" value="true"/>
- </sqlMapGenerator>
- <!-- Mapper 接口的生成策略 mapper 接口路径 -->
- <javaClientGenerator type="XMLMAPPER" targetPackage="main.mapper" targetProject=".\src">
- <property name="enableSubPackages" value="true"/>
- </javaClientGenerator>
- <!-- 逆向分析的表 表名对应的 javaBean 名 -->
- <table tableName="books" domainObjectName="Book"></table>
- <table tableName="users" domainObjectName="User"></table>
- </context>
- </generatorConfiguration>
3. 执行生成代码.
- @Test
- public void testMbg() throws Exception {
- List<String> warnings = new ArrayList<String>();
- boolean overwrite = true;
- File configFile = new File("mbg.xml");
- ConfigurationParser cp = new ConfigurationParser(warnings);
- Configuration config = cp.parseConfiguration(configFile);
- DefaultShellCallback callback = new DefaultShellCallback(overwrite);
- MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,callback, warnings);
- myBatisGenerator.generate(null);
- }
PageHelper 分页插件
PageHelper 是 MyBatis 中非常方便的第三方分页插件. 内部提供了 PageHelper 和 PageInfo 两个非常强大的类库.
使用步骤
1) 导入相关包 pagehelper-5.0.0.jar 和 jsqlparser-0.9.5.jar
2) 在 MyBatis 全局配置文件中配置分页插件, 注意标签位置不可乱序
- <plugins>
- <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
- </plugins>
3) 使用: 在查询出集合之前开启分页查询
- Page<Book> page = PageHelper.startPage(3,2 );
- List<Book> books = mapper.selectByExample(bookExample);
- // 查询出当前为第 3 页, 每页显示 2 条的图书信息, 其还可得到更多页码相关值
- books.forEach((book -> System.out.println("book =" + book)));
- PageInfo<Book> info = new PageInfo<>(books,3);
- System.out.println("============= 获取详细分页相关的信息 =================");
- System.out.println("当前页:" + info.getPageNum());
- System.out.println("总页码:" + info.getPages());
- System.out.println("总条数:" + info.getTotal());
- System.out.println("每页显示的条数:" + info.getPageSize());
- System.out.println("是否是第一页:" + info.isIsFirstPage());
- System.out.println("是否是最后一页:" + info.isIsLastPage());
- System.out.println("是否有上一页:" + info.isHasPreviousPage());
- System.out.println("是否有下一页:" + info.isHasNextPage());
- System.out.println("============ 分页逻辑 ===============");
- int[] nums = info.getNavigatepageNums();
- for (int num : nums) {
- System.out.println("num =" + num);
- }
Page 对象
在查询之前通过 PageHelper.startPage(页码, 条数)设置分页信息, 该方法返回 Page 对象
PageInfo 对象
在查询完数据后, 使用 PageInfo 对象封装查询结果, 可以获取更详细的分页信息以及可以完成分页逻辑
SSM 配置文件
来源: https://www.cnblogs.com/Open-ing/p/12232989.html