MyBatis日志管理
- 日志文件是用于记录系统操作事件的记录文件或文件集合
- 日志保存历史数据是诊断问题以及理解系统活动的重要依据
SLF4j与Logback
演示
添加依赖
pom.xml中
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
添加完成后 控制台出现日志信息
日志自定义
在resources目录下新增logback.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%thread] %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!--
日志输出级别(优先级高到低):
error: 错误 - 系统的故障日志
warn: 警告 - 存在风险或使用不当的日志
info: 一般性消息
debug: 程序内部用于调试信息
trace: 程序运行的跟踪信息
-->
<root level="debug">
<appender-ref ref="console"/>
</root>
</configuration>
再次运行时 日志时间换了位置
动态SQL
- 动态SQL是指根据参数数据动态组织SQL技术
goods.xml中
<select id="dynamicSQL" parameterType="java.util.Map" resultType="com.mybatis.entity.Goods">
select * from t_goods
<where>
<if test="categoryId != null">
and category_id = #{categoryId}
</if>
<if test="currentPrice != null">
and current_price < #{currentPrice}
</if>
</where>
</select>
MybatisTestor中
/**
* 动态SQL语句
* @throws Exception
*/
@Test
public void testDynamicSQL() throws Exception {
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
Map param = new HashMap();
param.put("categoryId", 44);
param.put("currentPrice", 500);
//查询条件
List<Goods> list = session.selectList("goods.dynamicSQL", param);
for(Goods g:list){
System.out.println(g.getTitle() + ":" +
g.getCategoryId() + ":" + g.getCurrentPrice());
}
}catch (Exception e){
throw e;
}finally {
MyBatisUtils.closeSession(session);
}
}
运行结果
MyBatis二级缓存
- 一级缓存默认开启,缓存范围SqlSession会话
- 二级缓存手动开启,属于范围Mapper Namespace
SqlSession一级缓存使用率不高
二级缓存被所有SqlSession共享
二级缓存运行规则
- 二级开启后默认所有查询操作均使用缓存
- 写操作commit提交时对该namespace缓存强制清空
- 配置useCache=false可以不用缓存
- 配置flushCache=true代表强制清空缓存
验证一级缓存
测试代码
MybatisTestor.java中
@Test
public void testLv1Cache() throws Exception{
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
Goods goods = session.selectOne("goods.selectById",1603);
Goods goods1 = session.selectOne("goods.selectById",1603);
System.out.println(goods.hashCode()+"--"+goods1.hashCode();
}catch (Exception e){
throw e;
}finally {
MyBatisUtils.closeSession(session);
}
}
输出的哈希地址一致
两个session情况
@Test
public void testLv1Cache() throws Exception{
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
Goods goods = session.selectOne("goods.selectById",1603);
Goods goods1 = session.selectOne("goods.selectById",1603);
System.out.println(goods.hashCode()+"--"+goods1.hashCode());
}catch (Exception e){
throw e;
}finally {
MyBatisUtils.closeSession(session);
}
try{
session = MyBatisUtils.openSession();
Goods goods = session.selectOne("goods.selectById",1603);
Goods goods1 = session.selectOne("goods.selectById",1603);
System.out.println(goods.hashCode()+"--"+goods1.hashCode());
}catch (Exception e){
throw e;
}finally {
MyBatisUtils.closeSession(session);
}
}
一级缓存只存在一个session中
执行commit强制清空
@Test
public void testLv1Cache() throws Exception{
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
Goods goods = session.selectOne("goods.selectById",1603);
session.commit();
Goods goods1 = session.selectOne("goods.selectById",1603);
System.out.println(goods.hashCode()+"--"+goods1.hashCode());
}catch (Exception e){
throw e;
}finally {
MyBatisUtils.closeSession(session);
}
}
开启二级缓存
goods.xml中添加
<cache eviction="LRU" flushInterval="600000" size="512" readOnly="true"/>
验证二级缓存
@Test
public void testLv2Cache() throws Exception{
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
Goods goods = session.selectOne("goods.selectById",1603);
System.out.println(goods.hashCode());
}catch (Exception e){
throw e;
}finally {
MyBatisUtils.closeSession(session);
}
try{
session = MyBatisUtils.openSession();
Goods goods = session.selectOne("goods.selectById",1603);
System.out.println(goods.hashCode());
}catch (Exception e){
throw e;
}finally {
MyBatisUtils.closeSession(session);
}
}
<cache eviction="LRU" flushInterval="600000" size="512" readOnly="true"/>
<!--
eviction是缓存的清除策略,当缓存对象数量达到上限后,自动触发对应算法对缓存对象清除
1.LRU – 最近最久未使用:移除最长时间不被使用的对象。
O1 O2 O3 O4 .. O512
14 99 83 1 893
2.FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
3.SOFT – 软引用:移除基于垃圾收集器状态和软引用规则的对象。
4.WEAK – 弱引用:更积极的移除基于垃圾收集器状态和弱引用规则的对象。
flushInterval 代表间隔多长时间自动清空缓存,单位毫秒
size 缓存存储上限,用于保存对象或集合(1个集合算1个对象)的数量上限
readOnly 设置为true,代表返回只读缓存,每次从缓存取出的是缓存对象本身,执行效率较高
设置为false,代表每次取出的是缓存对象的“副本”,每一次取出的对象都是不同的,安全性较高
-->
MyBatis多表级联查询
商品和详情对象关联查询
在entity目录下创建GoodsDetail.java
package com.mybatis.entity;
public class GoodsDetail {
private Integer gdId;
private Integer goodsId;
private String gdPicUrl;
public Integer getGdId() {
return gdId;
}
public void setGdId(Integer gdId) {
this.gdId = gdId;
}
public Integer getGoodsId() {
return goodsId;
}
public void setGoodsId(Integer goodsId) {
this.goodsId = goodsId;
}
public String getGdPicUrl() {
return gdPicUrl;
}
public void setGdPicUrl(String gdPicUrl) {
this.gdPicUrl = gdPicUrl;
}
public Integer getGdOrder() {
return gdOrder;
}
public void setGdOrder(Integer gdOrder) {
this.gdOrder = gdOrder;
}
private Integer gdOrder;
}
mappers目录下创建goods_detail.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="goodsDetail">
<select id="selectByGoodsId" parameterType="Integer"
resultType="com.mybatis.entity.GoodsDetail">
select * from t_goods_detail where goods_id = #{value}
</select>
</mapper>
在Goods.java中添加一个List
goods.xml中添加
<!--
resultMap可用于说明一对多或者多对一的映射逻辑
id 是resultMap属性引用的标志
type 指向One的实体(Goods)
-->
<resultMap id="rmGoods1" type="com.mybatis.entity.Goods">
<!-- 映射goods对象的主键到goods_id字段 -->
<id column="goods_id" property="goodsId"></id>
<!--
collection的含义是,在
select * from t_goods limit 0,1 得到结果后,对所有Goods对象遍历得到goods_id字段值,
并代入到goodsDetail命名空间的findByGoodsId的SQL中执行查询,
将得到的"商品详情"集合赋值给goodsDetails List对象.
-->
<collection property="goodsDetails" select="goodsDetail.selectByGoodsId"
column="goods_id"/>
</resultMap>
<select id="selectOneToMany" resultMap="rmGoods1">
select * from t_goods limit 0,10
</select>
mybatis-config.xml中添加
<mapper resource="mappers/goods_detail.xml"/>
测试用例:
@Test
public void testOneToMany() throws Exception {
SqlSession session = null;
try {
session = MyBatisUtils.openSession();
List<Goods> list = session.selectList("goods.selectOneToMany");
for(Goods goods:list) {
System.out.println(goods.getTitle() + ":" + goods.getGoodsDetails().size());
}
} catch (Exception e) {
throw e;
} finally {
MyBatisUtils.closeSession(session);
}
}
ManyToOne对象关联查询
在GoodsDetail.java中添加
private Goods goods;
public Goods getGoods() {
return goods;
}
public void setGoods(Goods goods) {
this.goods = goods;
}
在goods_detail.xml中添加描述
<resultMap id="rmGoodsDetail" type="com.mybatis.entity.GoodsDetail">
<id column="gd_id" property="gdId"/>
<result column="goods_id" property="goodsId"/>
<association property="goods" select="goods.selectById" column="goods_id"></association>
</resultMap>
<select id="selectManyToOne" resultMap="rmGoodsDetail">
select * from t_goods_detail limit 0,1
</select>
测试用例
/**
* 测试多对一对象关联映射
*/
@Test
public void testManyToOne() throws Exception {
SqlSession session = null;
try {
session = MyBatisUtils.openSession();
List<GoodsDetail> list = session.selectList("goodsDetail.selectManyToOne");
for(GoodsDetail gd:list) {
System.out.println(gd.getGdPicUrl() + ":" + gd.getGoods().getTitle());
}
} catch (Exception e) {
throw e;
} finally {
MyBatisUtils.closeSession(session);
}
}
数据量增到20条后
PageHelper分页插件
分页查询的麻烦事
- 当前页数据查询-select * from tab limit 0,10
- 总记录数查询-select count(*) from tab
- 程序计算总页数、上一页页码、下一页页码
PageHelper使用流程
- maven引入PageHelper与jsqlparser
- mybatis-config.xml增加Plugin配置
- 代码中使用PageHelper.startPage()自动分页
在pom.xml中引入插件
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>2.0</version>
</dependency>
在mybatis-config.xml配置插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--设置数据库类型-->
<property name="helperDialect" value="mysql"/>
<!--分页合理化-->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
在goods.xml中添加
<select id="selectPage" resultType="com.mybatis.entity.Goods">
select * from t_goods where current_price < 1000
</select>
测试
MybatisTestor.java中
@Test
public void testSelectPage(){
SqlSession session=null;
try{
session=MyBatisUtils.openSession();
/*startPage方法会自动将下一次查询进行分页*/
PageHelper.startPage(2,10);
Page<Goods> page = (Page) session.selectList("goods.selectPage");
System.out.println("总页数:"+page.getPages());
System.out.println("总记录数:"+page.getTotal());
System.out.println("开始行号:"+page.getStartRow());
System.out.println("结束行号:"+page.getEndRow());
System.out.println("当前页码:"+page.getPageNum());
List<Goods> data = page.getResult();//当前页数据
for(Goods g:data){
System.out.println(g.getTitle());
}
}catch (Exception e){
throw e;
}finally {
MyBatisUtils.closeSession(session);
}
}
不同数据库分页的实现原理
MySQL分页:select * from table limit 10,20;
Oracle:
select t3.* from(
select t2.*,rownum as row_num from(
select * from table order by id asc
)t2 where rownum<=20
)t3
where t2.row_num>11
SQL Server 2000:
select top 3 * from table
where
id not in
(select top 15 id from table)
SQL Server 2012+
select * from table order by id
offset 4 rows fetch next 5 rows only
MyBatis配置C3P0连接池
添加依赖
pom.xml中
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.4</version>
</dependency>
创建C3P0DataSourceFactory.java
package com.mybatis.datasource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
public C3P0DataSourceFactory(){
this.dataSource=new ComboPooledDataSource();
}
}
修改连接池配置
<dataSource type="com.mybatis.datasource.C3P0DataSourceFactory">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/babytun?useUnicode=true&characterEncoding=UTF-8"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
<property name="initialPoolSize" value="5"/>
<property name="maxPoolSize" value="20"/>
<property name="minPoolSize" value="5"/>
<!--...-->
</dataSource>
MyBatis批处理
批量插入
在goods.xml中编写
<insert id="batchInsert" parameterType="java.util.List">
INSERT INTO t_goods(title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)
VALUES
<foreach collection="list" item="item" index="index" separator=",">
(#{item.title},#{item.subTitle}, #{item.originalCost}, #{item.currentPrice}, #{item.discount}, #{item.isFreeDelivery}, #{item.categoryId})
</foreach>
</insert>
测试 MybatisTestor.java
@Test
public void testInsert1() throws Exception {
SqlSession session = null;
try{
long st = new Date().getTime();
session = MyBatisUtils.openSession();
List list = new ArrayList();
for(int i = 0 ; i < 10000 ; i++) {
Goods goods = new Goods();
goods.setTitle("测试商品");
goods.setSubTitle("测试子标题");
goods.setOriginalCost(200f);
goods.setCurrentPrice(100f);
goods.setDiscount(0.5f);
goods.setIsFreeDelivery(1);
goods.setCategoryId(43);
//insert()方法返回值代表本次成功插入的记录总数
session.insert("goods.insert" , goods);
}
session.commit();//提交事务数据
long et = new Date().getTime();
System.out.println("执行时间:" + (et-st) + "毫秒");
// System.out.println(goods.getGoodsId());
}catch (Exception e){
if(session != null){
session.rollback();//回滚事务
}
throw e;
}finally {
MyBatisUtils.closeSession(session);
}
}
批量删除
goods.xml
<delete id="batchDelete" parameterType="java.util.List">
DELETE FROM t_goods WHERE goods_id in
<foreach collection="list" item="item" index="index" open="(" close=")" separator=",">
#{item}
</foreach>
</delete>
测试 MybatisTestor.java
@Test
public void testBatchDelete() throws Exception {
SqlSession session = null;
try {
long st = new Date().getTime();
session = MyBatisUtils.openSession();
List list = new ArrayList();
for(int i=1931;i<=11930;i++)
list.add(i);
session.delete("goods.batchDelete", list);
session.commit();//提交事务数据
long et = new Date().getTime();
System.out.println("执行时间:" + (et - st) + "毫秒");
// System.out.println(goods.getGoodsId());
} catch (Exception e) {
if (session != null) {
session.rollback();//回滚事务
}
throw e;
} finally {
MyBatisUtils.closeSession(session);
}
}
Mybatis注解开发方式
MyBatis常用注解
注解 | 对应XML | 说明 |
@Insert | 新增SQL | |
@Update | 更新SQL | |
@Delete | 删除SQL | |
@Select | 查询SQL | |
@Param | – | 参数映射 |
@Results | 结果映射 | |
@Result | 字段映射 |
创建GoodsDAO接口
public interface GoodsDAO {
@Select("select * from t_goods where current_price between #{min} and #{max} order by current_price limit 0,#{limt}")
public List<Goods> selectByPriceRange(@Param("min") Float min ,@Param("max") Float max ,@Param("limt") Integer limt);
@Insert("INSERT INTO t_goods(title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id) VALUES (#{title} , #{subTitle} , #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery}, #{categoryId})")
//<selectKey>
@SelectKey(statement = "select last_insert_id()" , before = false , keyProperty = "goodsId" , resultType = Integer.class)
public int insert(Goods goods);
@Select("select * from t_goods")
//<resultMap>
@Results({
//<id>
@Result(column = "goods_id" ,property = "goodsId" , id = true) ,
//<result>
@Result(column = "title" ,property = "title"),
@Result(column = "current_price" ,property = "currentPrice")
})
public List<GoodsDTO> selectAll();
}
mybatis-config.xml中配置
<mappers>
<!--<mapper class="com.imooc.mybatis.dao.GoodsDAO"/>-->
<package name="com.imooc.mybatis.dao"/>
</mappers>
测试
//JUNIT单元测试类
public class MyBatisTestor {
@Test
public void testSelectByPriceRange() throws Exception {
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
List<Goods> list = goodsDAO.selectByPriceRange(100f, 500f, 20);
System.out.println(list.size());
}catch (Exception e){
throw e;
} finally {
MyBatisUtils.closeSession(session);
}
}
/**
* 新增数据
* @throws Exception
*/
@Test
public void testInsert() throws Exception {
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
Goods goods = new Goods();
goods.setTitle("测试商品");
goods.setSubTitle("测试子标题");
goods.setOriginalCost(200f);
goods.setCurrentPrice(100f);
goods.setDiscount(0.5f);
goods.setIsFreeDelivery(1);
goods.setCategoryId(43);
GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
//insert()方法返回值代表本次成功插入的记录总数
int num = goodsDAO.insert(goods);
session.commit();//提交事务数据
System.out.println(goods.getGoodsId());
}catch (Exception e){
if(session != null){
session.rollback();//回滚事务
}
throw e;
}finally {
MyBatisUtils.closeSession(session);
}
}
@Test
public void testSelectAll() throws Exception {
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
List<GoodsDTO> list = goodsDAO.selectAll();
System.out.println(list.size());
}catch (Exception e){
throw e;
} finally {
MyBatisUtils.closeSession(session);
}
}
}