MyBatis日志管理

什么是日志

◆日志文件是用于记录系统操作事件的记录文件或文件集合 ◆日志保存历史数据,是诊断问题以及理解系统活动的重要依据

SLF4j和Logback

添加仓库

<dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

运行MyBatisTestor.java文件

preparing表示执行了sql语句,parameters表示?对应的数据,Total表示执行sql语句后的结果共有几条数据

logback允许对日志进行自定义 在resource文件夹下强制新增文件logback.xml,必须是这个名字

appender标签表示对什么地方进行日志输出 encoder表示对日志输出的格式,%d{HH:mm:ss.SSS}表示日志输出时间确认到毫秒 [%thread]表示输出线程的名字,%-5level表示日志输出等级,-5表示按5个字符进行右对齐,%logger{36}表示日志产生的类,%msg表示产生的日志输出内容 %n表示换行

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

用来表示日志输出等级,会输出等级以上的信息

日志输出级别(优先级高到低): error: 错误 - 系统的故障日志 warn: 警告 - 存在风险或使用不当的日志 info: 一般性消息 debug: 程序内部用于调试信息 trace: 程序运行的跟踪信息

建议生产环境设置为info,开发环境设置为debug级别

MyBatis动态SQL

比如淘宝搜索笔记本电脑,还有下面有许多动态筛选条件

动态sql

◆动态SQL是指根据参数数据动态组织SQL的技术

使用if条件语句进行筛选功能 在goods.xml中

<select id="dynamicSQL" parameterType="java.util.Map" resultType="com.imooc.mybatis.entity.Goods">
        select * from t_goods
        <where>
          <if test="categoryId != null">
              and category_id = #{categoryId}
          </if>
          <if test="currentPrice != null">
              and current_price &lt; #{currentPrice}
          </if>
        </where>
    </select>

注意,在xml文件中需要和html文件一样,里面内容的特殊符号需要转义,比如<转义为< 进行测试用例:

/**
     * 动态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);
        }
    }

使用where添加上<>标签符号可以动态根据查询数据的不同进行and关键字的添加与删除,防止运行出现问题

MyBatis二级缓存

如果需要多次查询同一数据,会两次从数据库进行提取,mysql是把数据存放到硬盘上,硬盘提取速度不是很快,第一次和第二次返回的是相同记录,如何进行优化?

◆一级缓存默认开启,缓存范围SqlSession会话 ◆二级缓存手动开启,属于范围Mapper Namespace

缓存范围

每个用户创造自己的一级缓存(蓝色方框),每个一级缓存可以共享二级缓存(红方框)

二级缓存运行规则

◆二级开启后默认所有查询操作均使用缓存 ◆写操作commit提交时对该namespace缓存强制清空 ◆配置useCache=false可以不用缓存 ◆配置flushCache=true代表强制清空缓存

<!-- 单参数传递,使用parameterType指定参数的数据类型即可,SQL中#{value}提取参数-->
    <select id="selectById" parameterType="Integer" resultType="com.imooc.mybatis.entity.Goods">
        select * from t_goods where  goods_id = #{value}
    </select>

测试一级缓存

/**
     * 测试一级缓存
     * @throws Exception
     */
    @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);
            //session.commit();//commit提交时对该namespace缓存强制清空
            Goods goods1 = session.selectOne("goods.selectById" , 1603);
            System.out.println(goods.hashCode() + ":" + goods1.hashCode());
        }catch (Exception e){
            throw e;
        }finally {
            MyBatisUtils.closeSession(session);
        }
    }

第一个实验执行使用相同sql语句,观察hash值,即内存地址

可以看出,hash值相同,指向同一地址,在运行第一个sql语句时,产生记录,然后第二次执行相同语句时,发现内存中已存在,将其指向第一条语句结果地址,不产生记录,总记录结果为1

下一段try…catch语句块中,重新建立了一个session的对象,会产生一条记录,但指向了和上面不一样的另外一个相同地址,其生存周期就是在一个sqlsession中

如果加上session.commit()语句,会清空缓存,这时在执行相同语句时,找不到,会重新建立一个地址进行保存,所以会执行两次sql语句

通过上面实验发现,session生命周期太短,开启和关闭间只执行了少量sql语句,缓存对象很快释放,缓存使用率不高,为了解决问题,使用二级缓存,需要手动开启

在goods.xml中mapper标签下,增加语句

<cache eviction="LRU" flushInterval="600000" size="512" readOnly="true"/>

eviction="LRU":缓存清空策略 flushInterval="600000":刷新间隔,表示间隔多长时间清空缓存 size=“512”:缓存最大数量,512个对象 readOnly=“true”:表示缓存只读 进行用例测试:

/**
     * 测试二级缓存
     * @throws Exception
     */
    @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);
        }
    }

根据日志可以看出,缓存命中,只执行一次,但获得两次结果,与上面一级缓存的区别在于,即使二次缓存创建了两个session对象,也能进行缓存,得到相同结果,生命周期不再局限于session对象,二级缓存实在namespace命名空间上,不会跟着session销毁

缓存命中率越高,表示缓存效率越好,对程序优化效果越好,此处命中率为0.5,l两条sql语句,一句执行,一句缓存,缓存命中率为1/2即0.5

eviction是缓存的清除策略,当缓存对象数量达到上限后,自动触发对应算法对缓存对象清除

  1. LRU – 最近最久未使用:移除最长时间不被使用的对象。 O1 O2 O3 O4 .. O512 14 99 83 1 893 当达到第513对象时,会清除第512个的缓存,当达到第514个对象,会清除第2个对象缓存 LFU-最近最少使用移除最近访问频率最低的对象
  2. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  3. SOFT – 软引用:移除基于垃圾收集器状态和软引用规则的对象。
  4. WEAK – 弱引用:更积极的移除基于垃圾收集器状态和弱引用规则的对象。

flushInterval代表间隔多长时间自动清空缓存,单位毫秒,600000毫秒=10分钟 size缓存存储上限,用于保存对象或集合(1个集合算1个对象)的数量上限 readOnly设置为true,代表返回只缓存,每次从缓存取出的是缓存对象本身。这利执行效率较高 设置为false,代表每次取出的是缓存对象的”副本”,每一次取出的对象都是不同的,这种安全性较高

当使用全局查询的时候,由于查询记录很大,被当成一个对象存储,对内存开销很大,效率低,应该默认不使用缓存。可以在标签中书写useCache=‘false’

标签对里书写flushCache=“true”在sql执行后强制清空缓存,这是显式清空,不必等到提交commit的时候强制清空缓存了

MyBatis多表级联查询

通过对象获得其它对象信息,叫关联查询,可以减少工作时的繁琐操作,让所有sql放在xml配置文件中,让mybatis自动为我们完成,减少出错风险,提高开发效率

实体关系分析

多对多关系一般要抽出一张表出来,设置学生编号和课程编号,学生表与中间表关联,课程表与中间表关联

分析下商品与商品详情的实体关系,一件商品有多张详情图片,一的一方属于商品goods,多的一方属于商品详情goods_detail,下面从两个角度进行关联查询

OneToMany对象关联查询

从一的一方查询多的一方

打开数据表t_goods_detail,是商品的详细描述信息 在实体类(com.imooc.mybatis.entity)下新建java文件GoodsDetail.java

package com.imooc.mybatis.entity;
 
public class GoodsDetail {
    private Integer gdId;
    private Integer goodsId;
    private String gdPicUrl;
    private Integer gdOrder;
 
    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;
    }
 
 
}

新建mapper映射文件,命名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.imooc.mybatis.entity.GoodsDetail">
        select * from t_goods_detail where goods_id = #{value}
    </select>
</mapper>

在mybatis-config.xml中进行注册

<mapper resource="mappers/goods_detail.xml"/>

goods.java文件下新增属性goodsDetails

private List<GoodsDetail> goodsDetails;

同时设置好get与set方法

public List<GoodsDetail> getGoodsDetails() {
        return goodsDetails;
    }
 
    public void setGoodsDetails(List<GoodsDetail> goodsDetails) {
        this.goodsDetails = goodsDetails;
    }

在goods.xml文件中对list集合进行说明

<!--
        resultMap可用于说明一对多或者多对一的映射逻辑
        id 是resultMap属性引用的标志
        type 指向One的实体(Goods)
    -->
    <resultMap id="rmGoods1" type="com.imooc.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>

<id column="goods_id" property="goodsId"></id>表示属性goodsId绑定字段goods_id goodsDetail.selectByGoodsId是goods_detail.xml下的selectByGoodsId标签id名,实现关联查询

进行测试

/**
     * 一对多对象关联查询
     * @throws Exception
     */
    @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);
        }
    }

通过goods对象获得商品描述信息

ManyToOne对象关联查询

从多的一方查询一的一方 在GoodsDetail.java下添加属性

private Goods goods;

添加set与get方法

public Goods getGoods() {
return goods;
}
 
public void setGoods(Goods goods) {
    this.goods = goods;
}

然后在goods_detail.xml文件下进行描述

<resultMap id="rmGoodsDetail" type="com.imooc.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,20
    </select>

association表示从多的一方关联到一的一方,connection标签是从一的一方关联到多的一方

进行测试

/**
     * 测试多对一对象关联映射
     */
    @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);
        }
    }

<association property="goods" select="goods.selectById" column="goods_id"></association>中goods_id优先将查询结果给goods实体类,导致goods树属性值为null,解决办法通过result标签使其映射 <result column="goods_id" property="goodsId"/>

分页插件PageHelper

每个知识点都是为了解决在开发过程中遇到的问题,知识点孕育在解决矛盾中,我们学习的首要目标就是抓住这个矛盾,才能从知识的汪洋大海中抓住线索,不至于迷糊,否则学了就忘,不会运用,这就是知识的体系,在学习复杂知识点之前多问自己为什么这个知识点是为了解决什么问题,知其然知其所以然,才能熟练用于项目开发中,否则就只能从项目经验中反过来学习知识点,夸大项目经验的重要性,同样是主观片面的,所以才说项目不在多,而在精

数据库每次查询会有成千上万条信息,给服务器造成巨大压力,所以出现分页查询,每次查询几百条即可,为了解决这个问题,需要攻克下面问题

分页查询的麻烦事

◆当前页数据查询-selectfrom tab limit0,10 ◆总记录数查询-select count()from tab ◆程序计算总页数、上一页页码、下一页页码

使用mybatis分页插件,可以解决问题,不重复造轮子,要学会站在前人的肩膀上 MyBatis 分页插件 PageHelper

PageHelper使用流程

◆maven入PageHelper与jsqlparser ◆mybatis-config.xml增加Plugin配置 ◆代码中使用PageHelper..startPage()自动分页

1.maven引入插件

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

5.1.10以上的pagehelper分页插件必须使用2.0以上的sql解释器jsqlparse 分页插件自动根据sql语句分析实现分页功能,分析sql语句就交给解释器jsqlparse,所以需要引入

在mybatis-config.xml的configuration标签下添加插件

<!--启用Pagehelper分页插件-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!--设置数据库类型-->
            <property name="helperDialect" value="mysql"/>
            <!--分页合理化-->
            <property name="reasonable" value="true"/>
        </plugin>
    </plugins>

如何使用分页插件 (pagehelper.github.io)

在goods.xml中添加sql语句配置

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

测试调用

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

分析下日志观察插件为我们做了那些事

插件自动运行select count语句查询共有多少记录,这条语句我们没有写,是插件自动生成的,这里的total:1是返回一条数据,如1808,不是返回1808数据记录,这条sql语句由sql解释器进行运行

后面插件自动添加limit关键字,自动填写区间数字,sql语句的执行还是jsqlparser为我们执行

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

MySQL分页

Oracle分页

SQL Server 2000

SQL Server 2012+

MyBatis配置C3P0连接池

mybatis是自带的连接池,没有第三方连接池功能强大,我们需要自己配置

maven引入

<dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.4</version>
        </dependency>

新建com.imooc.mybatis.datasource 创建工厂类C3P0DataSourceFactory.java文件

package com.imooc.mybatis.datasource;
 
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
 
/**
 * C3P0与MyBatis兼容使用的数据源工厂类
 */
public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
    public C3P0DataSourceFactory(){
        this.dataSource = new ComboPooledDataSource();
    }
}

注意使用其他连接池,需要继承UnpooledDataSourceFactory,然后在构造函数中,指明数据源由对应连接池创建即可

修改掉mybatis自带的连接池

<!--采用连接池方式管理数据库连接-->
            <!--<dataSource type="POOLED">-->
            <dataSource type="com.imooc.mybatis.datasource.C3P0DataSourceFactory">
                <property name="driverClass" value="com.mysql.jdbc.Driver"/>
                <!--&符号不认识,需要转义为&amp;-->
                <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/babytun?useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="user" value="root"/>
                <property name="password" value="20020829"/>
                <property name="initialPoolSize" value="5"/>
                <property name="maxPoolSize" value="20"/>
                <property name="minPoolSize" value="5"/>
                <!--...-->
            </dataSource>

driver改为driverClass,url改为jdbcUrl,username改为user,每种连接池配置名不一样,这点需要注意 运行观察日志文件

MyBatis批处理

使用MyBatis进行增删改查都是一条条执行,效率很慢 插入多条数据:

MyBatis批量插入

在goods.xml文件配置sql语句

INSERT INTO table
    VALUES ("a" , "a1" , "a2"),("b" , "b1" , "b2"),(....)

如果参数是一个集合,进行插入,该如何写 使用子标签foreach

<!--    INSERT INTO table-->
<!--    VALUES ("a" , "a1" , "a2"),("b" , "b1" , "b2"),(....)-->
    <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>
    <!--in (1901,1902)-->
    <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>

<foreach collection="list" item="item" index="index" separator=","> collection表示迭代数据来源为“list”,需要传参 item表示迭代时拿出的每一项进行指代 index表示循环索引 separator表示使用逗号分隔,最后一项foreach会自动判断,不加逗号

用例测试

/**
     * 批量插入测试
     * @throws Exception
     */
    @Test
    public void testBatchInsert() 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()方法返回值代表本次成功插入的记录总数
 
                list.add(goods);
            }
            session.insert("goods.batchInsert", 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);
        }
    }

上面通过集合传入,大概需要1700多毫秒,接下来使用通过执行一次传入insert一次的方法来比较

/**
     * 10000次数据插入对比测试用例
     * @throws Exception
     */
    @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);
        }
    }

大概选哟4500多毫秒。时间多了大概3倍 所以使用第一种方式更好,插入效率更高 但是使用这种方法有两点局限: 批量插入数据的局限:1、无法获得插入数据的id 2、批量生成的SQL太长,可能会被服务器拒绝

为了解决sql太长问题,可以通过分段插入,如果要求10000000数据,可以分成100000嵌套for循环的分别插入(100*100000),效率提高,也能避免被拒绝

MyBatis批量删除

批量删除数据:

delete from t_goods where goods_id in (1920,1921)

批量通过集合方式删除,也要使用foreach标签

<!--in (1901,1902)-->
    <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>

删除操作时,要求前端传入集合,集合内不再是一个个实体,而是goodsId编号,goodsId是主键编号,既便于前端书写,也便于后端操作 open=”(” close=”)” 指in后面的括号()

/**
     * 批量删除测试
     * @throws Exception
     */
    @Test
    public void testBatchDelete() throws Exception {
        SqlSession session = null;
        try {
            long st = new Date().getTime();
            session = MyBatisUtils.openSession();
            List list = new ArrayList();
            list.add(1920);
            list.add(1921);
            list.add(1922);
            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常用注解

通过注解,可以将写在xml文件中的sql语句放在java程序中,方便我们理解,开发体验更好

使用注解,可以不使用mapper映射文件了,使用接口+实现+注解可以替代掉mapper文件 创建dao包,创建一系列的接口,在GoodsDAO.java中

package com.imooc.mybatis.dao;
 
import com.imooc.mybatis.dto.GoodsDTO;
import com.imooc.mybatis.entity.Goods;
import org.apache.ibatis.annotations.*;
 
import java.util.List;
 
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")//这条语句会按照下面的转换规则将结果转换为GoodsDTO对象
    //实现<resultMap>
    @Results({
            //<id> GoodsDTO并不是我们的实体,需要驼峰转换
          @Result(column = "goods_id" ,property = "goodsId" , id = true) ,
            //<result>
            @Result(column = "title" ,property = "title"),
            @Result(column = "current_price" ,property = "currentPrice")
    })
    public List<GoodsDTO> selectAll();
}

使用@param()将实参与sql参数进行对应 List<Goods>表示每一条查询出来的结果都包裹成Goods对象,其中Goods里面的属性会根据驼峰命名转换的方式自动与字段名对应,不需我们手动转换 现在mybatis还不知道这个接口的用途,需要在mybatis-config.xml文件说明

使用insert方法我们希望获得最新插入主键的编号,使用selectkey子标签进行获取,执行完insert语句后才执行selectkey语句,所以设置before为false,keyproperty表示哪个字段为主键,resultType表示返回主键类型

<mappers>
        <!--<mapper class="com.imooc.mybatis.dao.GoodsDAO"/>-->
        <package name="com.imooc.mybatis.dao"/>
    </mappers>

上面方式二选一即可 打开测试用例

package com.imooc.mybatis;
 
import com.imooc.mybatis.dao.GoodsDAO;
import com.imooc.mybatis.dto.GoodsDTO;
import com.imooc.mybatis.entity.Goods;
import com.imooc.mybatis.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
 
import java.util.List;
 
//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);
 
        }
    }
}