MyBatis进阶

MyBatis进阶

  • 1.MyBatis日志管理与动态SQL
    • 1.1.MyBatis日志管理
      • 1.1.1.导入依赖
      • 1.1.2.再次运行
      • 1.1.2.自定义logback日志细则
    • 1.2.动态SQL
  • 2.MyBatis二级缓存
    • 2.1.Mybatis的一级缓存原理(sqlsession级别)
    • 2.2.二级缓存原理(mapper级别)
    • 2.3.二级缓存运行规则
    • 2.4.代码测试(验证运行规则)
      • 2.4.1.测试一级缓存
      • 2.4.2.一级缓存测试commit操作
    • 2.5.开启MyBatis二级缓存
      • 2.5.1.useCache属性
      • 2.5.2.flushCache属性
  • 3.MyBatis多表级联查询(嵌套查询)
    • 3.1.一对多关系查询
    • 3.1.多对一关系查询
  • 4.分页插件PageHelper
    • 4.1.使用流程
      • 4.1.1.maven引入PageHelper与jsqlparser
      • 4.1.1.mybatis-config.xml增加Plugin配置
      • 4.1.1.代码中使用PageHelper.startPage()自动分页
    • 4.2.不同数据库分页的实现原理
      • 4.2.1.MySQL分页
      • 4.2.2.Oracle分页
      • 4.2.3.SQL Server 2000分页
      • 4.2.3.SQL Server 2012+分页
  • 5.MyBatis整合C3P0连接池
  • 6.MyBatis批处理
    • 6.1.批量插入标签:
    • 6.2.批量删除标签:
      • 6.3.批量操作存在的问题
  • 7.MyBatis注解开发
  • 7.1.常用注解
  • 7.2.开发
    • 7.2.1.创建注解开发接口
    • 7.2.2.配置mybatis-config.xml文件
    • 7.2.3.开发注释接口
        • 7.2.3.1.查询
        • 7.2.3.2.插入
        • 7.2.3.2.结果映射
        • 7.2.3.2.嵌套查询结果映射

1.MyBatis日志管理与动态SQL

1.1.MyBatis日志管理

  • 日志文件是用于记录系统操作事件的记录文件或文件集合
  • 日志保存历史数据,是诊断问题以及理解系统活动的重要依据
    MyBatis进阶_第1张图片
    MyBatis的底层通过SLF4J,支持logback

1.1.1.导入依赖

		<dependency>
            <groupId>ch.qos.logbackgroupId>
            <artifactId>logback-classicartifactId>
            <version>1.2.3version>
        dependency>

1.1.2.再次运行

再次运行mybatis的增删查改操作,控制台就会输出日志信息。
MyBatis进阶_第2张图片
我们可以看到我们执行的sql语句(上图倒数第三行)
以及执行SQL语句时我们传入的参数(上图倒数第二行)
和查询返回的条数(上图倒数第一行)

1.1.2.自定义logback日志细则

在项目src/main/resources目录下,新建logback.xml(固定文件名)文件。


<configuration>
    
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            
            
            <pattern>[%thread] %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%npattern>
        encoder>
    appender>

    
    
    <root level="debug">
        
        <appender-ref ref="console"/>
    root>
    
configuration>

具体信息参考官网:logback中文网,logback官网

1.2.动态SQL

  • 动态SQL是指根据参数数据动态组织SQL的技术
  • 比如:购物网站搜索商品是,备用小标签。

例如如下mapper:

	<select id="dynamicSQL" parameterType="java.util.Map" resultType="com.lhj.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>

当传入的Map的属性中,不存在categoryId和currentPrice的值,则sqlsession执行该查询时,执行的sql语句为select *
from t_goods

当传入的map中,存在categoryId的值,不存在currentPrice的值,则sqlsession执行该查询时,执行的sql语句为select *
from t_goods where category_id=#{categoryId}

当传入的map中,不存在categoryId的值,存在currentPrice的值,则sqlsession执行该查询时,执行的sql语句为select *
from t_goods where current_price < #{currentPrice}

当传入的map中,存在categoryId的值,存在currentPrice的值,则sqlsession执行该查询时,执行的sql语句为select *
from t_goods where category_id=#{categoryId} and current_price < #{currentPrice}

除了标签多了一个 useCache 属性,用来设置该查询是否写入和使用内存,
值可为"true"或“false”,默认为“true”
一般重用性低的查询,设置为“false”,查询结果不写入内存

	<select id="showAll" resultType="com.lhj.mybatis.entity.Goods" useCache="false">
        select *
        from t_goods
        order by goods_id desc
        limit 10
    select>

在select标签中设置useCache=false,可以禁用当前select语句的二级缓存,即每次查询都是去数据库中查询,默认为true,即使用二级缓存。

2.5.2.flushCache属性

在开启二级缓存之后,,,)的flushCache默认为false,如果标签,输出结果用resulMap进行映射。
的type属性表示最后输出的实体类的类型。
用来进行主键映射。
column属性用来输入需要传入goods_id字段的值来进行查询。column="goods_id"这个column指的是select * from t_goods limit 0,1的查询结果中的goods_id字段。

以上为个人理解,可能并非正确,具体参考官方文档:MyBatis高级结果映射

3.1.多对一关系查询

已知商品表与商品详情表存在着1对n的关系。
现在我们想要通过查询商品详情列出相应的商品信息。
先修改商品详情实体类,增加用来存放商品信息的属性以及对应的get和set方法:

private Integer gdId; 
    private Integer goodsId;  
    private String gdPicUrl;  
    private Integer gdOrder;
	....
	对应get和set方法

商品详情的mapper文件goods_detail.xml文件中,添加select标签实现目标功能:

	<resultMap id="rmGoodsDetail" type="com.lhj.mybatis.entity.GoodsDetail">
        <id property="gdId" column="gd_id">id>
        <result property="goodsId" column="goods_id"/>
        <association property="goods" select="goods.selectById" column="goods_id"/>
    resultMap>
    <select id="selectGoods" resultMap="rmGoodsDetail">
        select *
        from t_goods_detail
        limit 1,20
    select>

顺带贴上goods.selectById的代码:

<mapper namespace="goods">
	...
	<select id="selectById" parameterType="java.lang.Integer" resultType="com.lhj.mybatis.entity.Goods">
        select *
        from t_goods
        where goods_id = #{id}
    select>
    ...
mapper>

测试:

	public void testManyToOne() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.getSqlSession();
            List<GoodsDetail> goodsDetails = session.selectList("goodsDetail.selectGoods");
            for (GoodsDetail g : goodsDetails) {
                System.out.println(g.getGdPicUrl() + "" + g.getGoods().getTitle());
            }
        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtils.closeSqlSession(session);
        }
    }

个人理解:
多对一关系查询与一对多关系查询原理差不多
只有以下两点区别:

1.用的标签不同:

一对多关系进行查询时候,里用的是进行嵌套查询,返回单条数据。
association是联合的意思。

2.返回的实体可能存在属性空值的情况

造成原因:结果映射的时候把原查询结果的某个字段的值优先用于标签进行嵌套查询,以至于原实体类中相应字段的属性造成空值。
解决办法:只要在标签前再用标签进行映射。
个人理解:之所以一对多查询时不存在空值问题情况,可能是一般一对多查询是用原查询的主键字段作为嵌套查询的参数,而主键字段在结果映射里用赋值过,而多对一查询一般是用原查询的非主键字段作为嵌套查询的参数,所以造成空值。

4.分页插件PageHelper

用来便捷分页列出所有的数据,官网:PageHelper

4.1.使用流程

4.1.1.maven引入PageHelper与jsqlparser

可以通过maven引入依赖:

<dependency>
            <groupId>com.github.pagehelpergroupId>
            <artifactId>pagehelperartifactId>
            <version>5.1.11version>
        dependency>
        <dependency>
            <groupId>com.github.jsqlparsergroupId>
            <artifactId>jsqlparserartifactId>
            <version>3.1version>
        dependency>

4.1.1.mybatis-config.xml增加Plugin配置

编辑mybatis的配制文件:



<configuration>
	...
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <property name="helperDialect" value="mysql"/>
            <property name="reasonable" value="true"/>
        plugin>
    plugins>
    ...
configuration>

interceptor属性用于配置拦截器插件,新版拦截器是 com.github.pagehelper.PageInterceptor

用来添加分页插件参数,具体参考官方文档
helperDialect:分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置helperDialect属性来指定分页插件使用哪种方言。配置时,可以使用下面的缩写值:
oracle,mysql,mariadb,sqlite,hsqldb,postgresql,db2,sqlserver,informix,h2,sqlserver2012,derby
reasonable:分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。

4.1.1.代码中使用PageHelper.startPage()自动分页

先写一个列出数据的查询:

	<select id="selectPage" resultType="com.imooc.mybatis.entity.Goods">
        select * from t_goods where current_price < 1000
    select>

实现:

	@Test
    /**
     * PageHelper分页查询
     */
    public void testSelectPage() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.getSqlSession();
            /*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());
            }
            System.out.println("");
        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtils.closeSqlSession(session);
        }
    }

4.2.不同数据库分页的实现原理

4.2.1.MySQL分页

	select * from table limit 10,20;

表示从第10行开始,选取之后20行数据

4.2.2.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

第三行是核心查询语句,取行号小于20的数据,获取从第12行开始的数据

4.2.3.SQL Server 2000分页

	select top 3 * from table
	where
		id not in
		(select top 15 id from table)

表示从第16行开始,选取之后3行数据

4.2.3.SQL Server 2012+分页

	select * from table order by id
	offset 4 rows fetch next 5 rows only

表示从第5行开始,选取之后5行数据

5.MyBatis整合C3P0连接池

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。
导入依赖:

	<dependency>
    	<groupId>com.mchangegroupId>
   	 	<artifactId>c3p0artifactId>
	    <version>0.9.5.5version>
	dependency>

创建C3P0与MyBatis兼容使用的数据源工厂类:

package com.lhj.mybatis.datasource;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;

//继承UnpooledDataSourceFactory类,使MyBatis能够良好支持
public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
    public C3P0DataSourceFactory() {
    	//dataSource 属性来自父类,ComboPooledDataSource类由c3p0提供符合规格的数据源
        this.dataSource = new ComboPooledDataSource();
    }
}

修改mybatis-config.xml配置文件:

		...
		<environment id="dev">
            <transactionManager type="JDBC">transactionManager>
            
            <dataSource type="com.lhj.mybatis.datasource.C3P0DataSourceFactory">
            	
                <property name="driverClass" value="com.mysql.jdbc.Driver"/>
                <property name="jdbcUrl" value="jdbc:mysql://localhost:3309/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>
        environment>
        ...

6.MyBatis批处理

6.1.批量插入标签:

	
	<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>

标签属性
collection:必须指定,

  • 当传入的参数为list类型,为list
  • 当传入的参数为array类型,为array
  • 当传入的参数为map类型,为map的键key(较少使用)

item:迭代集合时,元素的别名
index:当foreach遍历的数list或=者数组时,index代表就是下标,foreach遍历map时index代表key,此时item表示value。
separator:设置被迭代元素之间的分割符

6.2.批量删除标签:


	<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>

标签属性
open:包裹被迭代集合元素的开始符号。
close:包裹被迭代集合元素的结束符号。

6.3.批量操作存在的问题

1.无法获得插入的数据的id
2.批量生产的SQL太长,可能会被服务器拒绝

7.MyBatis注解开发

注释开发一般用于简单sql语句的开发。

7.1.常用注解

MyBatis进阶_第7张图片

7.2.开发

7.2.1.创建注解开发接口

一般在项目下创建一个名为dao的包,专门用来放注解开发接口

package com.lhj.mybatis.dao;

public interface GoodsDAO {

}

7.2.2.配置mybatis-config.xml文件

有两种配置方法:
一种是用标签,用的class属性指向具体的用于注释开发的接口类
一种是用标签,用的name的属性指向用来放注解接口类的包,运行时mybatis会自动扫描类下的有注释的接口来应用

	...
<configuration>
	...
    <mappers>
        <mapper class="com.lhj.mybatis.dao.GoodsDAO"/>
        //上下两种只要选一种写就好了
        <package name="com.lhj.mybatis.dao"/>
    mappers>
configuration>

7.2.3.开发注释接口

7.2.3.1.查询

	@Select("Select * from t_goods where current_price between #{min} and #{max} limit 0,#{limit}")
    public List<Goods> selectByRange(@Param("min") float min, @Param("max") float max, @Param("limit") int limit);

@Select(" “)中填写SQL语句
方法的返回值为查询结果,方法的参数为传入SQL语句的参数,带参方法前用@Param(” ")指定SQL语句中对应的元素。

实现:

	@Test
    public void selectByRange() {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSession();
            //得到映射器,传入带有Mybatis注解的接口类
            //在运行时,session会根据接口类中的配置信息动态生成实现类
            GoodsDAO goodsDAO = session.getMapper(GoodsDAO.class);
            //调用实现类中的方法完成SQL查询
            List<Goods> goods = goodsDAO.selectByRange(100, 500, 20);
            for (Goods g : goods) {
                System.out.println(g.getTitle());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MyBatisUtils.closeSession(session);
        }
    }

7.2.3.2.插入

	@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(statement = "SELECT last_insert_id()", keyProperty = "good_id", before = false, resultType = Integer.class)
    public int insert(Goods goods);

@SelectKey()注解用来返回插入后的插入记录数的id
有四个必须的属性:
statement = , 用来填写返回id的sql语句
keyProperty = , 用来指定主键的字段名
before = , 是否输出执行插入语句前的id值
resultType = 用来指定返回id的类型,例如Integer.class

7.2.3.2.结果映射

也可以参考一下这个点击这里

	@Select("Select goods_id,title,current_price from t_goods limit 0,20")
    @Results({
            @Result(column = "goods_id", property = "goodsId", id = true),
            @Result(column = "title", property = "title"),
            @Result(column = "current_price", property = "currentPrice")
    })
    public List<GoodsDTO> selectAll();

@Results()相当于xml文件中的,{ }表示一个集合用来放@Result( )注解
@Result( )相当于xml文件中的或者
column 数据库的列名
Property需要装配的属性名
主键映射时,相当于用映射主键时,用@Result( )的 id 属性来设置

7.2.3.2.嵌套查询结果映射

	@Result(column=" ",property="",one=@One(select=""))

@One注解(一对一)代替了标签,在注解中用来指定子查询返回单一对象。@One里的select属性用来指向另一个抽象接口的查询方法。

	@Result(property="",column="",many=@Many(select=""))

@Many(多对一)代替了标签,在注解中用来指定子查询返回对象集合。select属性用来指向另一个抽象接口的查询方法。

你可能感兴趣的:(MyBatis)