MyBatis 进阶

MyBatis 进阶

MyBatis所有学习案例:https://gitee.com/pikachu2333/mybatis-simple

MyBatis日志管理

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

SLF4j与Logback
在这里插入图片描述
早期的Java项目中,基本都是使用的log4j,log4j和logback这两个日志管理实现都是由一个人开发的。英文log4j虽然经过多次更新迭代,仍然有些问题积重难返,所以作者另起炉灶开发了另一款日志管理实现logback,而且logback的性能要好的多。在MyBatis底层可以通过SLF4J支持logback!

案例:

pom.xml 通过maven增加logback依赖

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

在引用外部库中出现了2个底层依赖个slf4j的引用。

此时运行测试用例,即可在控制台打印日志信息。

**自定义日志信息:**在resources目录下新建强制要求名为logback.xml的文件

src/main/resources/logback.xml

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <!-- 指定在控制台中输出日志 -->
    <!-- name属性可以随意,如果要在控制台输出,一般称之为console -->
    <!-- class属性指定何处打印输出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 编码节点 -->
        <encoder>
            <!--
                %d{HH:mm:ss.SSS}:输出时间格式,精确到毫秒
                [%thread]:当前操作的线程
                %-5level:以5个字符右对齐以及级别
                %logger{36}:具体哪个类的日志(只显示36个字符)
                %msg:日志信息
                %n:换行
                这些表达式在logback官网上都有详细说明
            -->
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <!--
        日志输出级别(优先级高到低):
        error: 错误 - 系统的故障日志
        warn: 警告 - 存在风险或使用不当的日志
        info: 一般性消息
        debug: 程序内部用于调试信息
        trace: 程序运行的跟踪信息
        下方root标签表示日志的最低输出级别为debug,即debug级别以下的信息不进行输出
    -->
    <root level="debug">
        <appender-ref ref="console"></appender-ref>
    </root>
</configuration>

MyBatis动态SQL

在我们使用SQL语句时,有的时候参数是不固定的。比如用户可以指定多个检索条件,也可能单独只指定一个检索条件。在这个时候,我们无法确定条件参数的数量,只能使用动态SQL完成。在实际的开发中,动态SQL的使用非常普遍。

动态SQL是指根据参数数据动态组织SQL的技术,它有些类似于对SQL执行拼接。

动态SQL的应用场景:
在这里插入图片描述
可以使用<where>标签和<if>组合使用,或是单独使用<if>标签来实现动态SQL。

goods.xml

<select id="dynamicSQL" parameterType="java.util.Map" resultType="com.dodoke.mybatis.entity.Goods">
    select * from t_goods
    <!-- 不需要写where关键字,只需要利用where标签智能判断and是否要添加 -->
    <where>
        <!-- 针对map中的key进行判断对应的value值是否为null和空 -->
        <if test="categoryId != null and categoryId!=''">
            and category_id = #{categoryId}
        </if>
        <if test="currentPrice != null and categoryId!=''">
            and current_price &lt; #{currentPrice}
        </if>
    </where>
</select>

测试代码:

    @Test
    public void testDynamicSQL() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSqlSession();
            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.closeSqlSession(session);
        }
    }

MyBatis二级缓存

  • 一级缓存默认开启,缓存范围SqlSession会话
  • 二级缓存手动开启,数据范围Mapper Namespace

缓存的范围

默认一级缓存开启,每一个SqlSession查询是不一样的对象,查询一次就被释放,使用率不高,而二级缓存为每一个SqlSession共享,提高利用率。
在这里插入图片描述

二级缓存运行规则

  • 二级缓存开启后默认所有查询操作均使用缓存
  • 写操作commit提交时对该namespace缓存强制清空(保证数据的一致性)
  • 配置useCache=false可以不使用缓存
  • 配置fulshCache=true代表强制清空缓存

一级缓存验证案例:

    @Test
    public void testLv1Cache() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSqlSession();
            Goods goods = session.selectOne("goods.selectById", 1603);
            Goods goods1 = session.selectOne("goods.selectById", 1603);
            System.out.println(goods.hashCode());
            System.out.println(goods1.hashCode());
        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtils.closeSqlSession(session);
        }
    }

goods和goods1两个对象打印的哈希值地址是一样的,也就是两个对象指向同一块空间,所以已经缓存是属性SqlSession对象的。

继续验证:

负责两个session对象执行块,上面和下面执行的两个结果不一样。

    @Test
    public void testLv1Cache() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSqlSession();
            Goods goods = session.selectOne("goods.selectById", 1603);
            Goods goods1 = session.selectOne("goods.selectById", 1603);
            System.out.println(goods.hashCode());
            System.out.println(goods1.hashCode());
        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtils.closeSqlSession(session);
        }

        try {
            session = MyBatisUtils.openSqlSession();
            Goods goods = session.selectOne("goods.selectById", 1603);
            Goods goods1 = session.selectOne("goods.selectById", 1603);
            System.out.println(goods.hashCode());
            System.out.println(goods1.hashCode());
        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtils.closeSqlSession(session);
        }
    }

但是如果执行commit之后,一级缓存就会被强制清空,下面两个对象的hashCode值不一样:

    @Test
    public void testLv1Cache() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSqlSession();
            Goods goods = session.selectOne("goods.selectById", 1603);
            session.commit();
            Goods goods1 = session.selectOne("goods.selectById", 1603);
            System.out.println(goods.hashCode());
            System.out.println(goods1.hashCode());
        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtils.closeSqlSession(session);
        }
    }

开启二级缓存:

goods.xml 在mapper下配置:

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

开启二级缓存后,两个对象地址相同:

    @Test
    public void testLv2Cache() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSqlSession();
            Goods goods = session.selectOne("goods.selectById", 1603);
            System.out.println(goods.hashCode());
        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtils.closeSqlSession(session);
        }

        try {
            session = MyBatisUtils.openSqlSession();
            Goods goods = session.selectOne("goods.selectById", 1603);
            System.out.println(goods.hashCode());
        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtils.closeSqlSession(session);
        }
    }

二级缓存是把对象存到命名空间的地址上,并不会随着session的开启与关闭销毁。

二级缓存详细配置:

    <!-- 开启二级缓存
        eviction 缓存的清除策略 ,当缓存对象数量达到上限后,自动触发对应算法对缓存对象清楚
            1. LRU - 最近最久未使用: 移除最长时间不被使用的对象。
            2. LFU - 最近最少使用: 移除最近访问频率最低的对象。
            3. FIFO - 先进先出: 按对象进入缓存的顺序来移除它们。
            下面两个一般不推荐因为不是MyBatis自己控制的而是依靠java中的垃圾回收机制
            4. SOFT - 软引用: 移除基于垃圾收集器状态和软引用规则的对象。
            5. WEAK - 弱引用: 更积极的移除基于垃圾收集器状态和弱引用规则的对象。
        flushInterval 代表间隔多久自动清空缓存,单位毫秒,例如: 60000毫秒 - 10分钟  根据设备内存大小分配
        size 缓存存储上限,用于保存对象或集合(1个集合算一个对象)的数量上限
            推荐不要用集合对象,size不要太小,比如根据id查询,表中有1400个商品,缓存的大小就要大于1400
        readOnly 设置为true,代表只读缓存,每次从缓存取出的是缓存对象本身,执行效率高
                 设置为false,每次取出的是缓存对象的"副本",每一次取出的对象都是不同的,安全率较高
     -->
    <cache eviction="LRU" flushInterval="600000" size="512" readOnly="true"></cache>

在其他的标签中也提供了一些响应的缓存设置:

useCache="false"代表不使用缓存,比如这种查询全部数据压力太大了

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

flushCache="true"执行完当前sql之后立即清空缓存,可以用在insert语句,虽然insert,commit后也会清空缓存,但设置后是插入后立即清空缓存;也可以使用在select语句查询完后立即清空缓存,并且该条sql语句执行结果同样不会被放入缓存。

多表级联查询

注意和多表关联查询不一样:

  • 多表关联查询:两个表通过主外键在一条SQL中完成所有数据的提取。
  • 多表级联查询:通过一个对象来获取与它关联的另外一个对象,执行的SQL语句分为多条。

OneToMany 一对多对象关联

实体关系分析

学生和课程是多对多关系,需要抽象出一张中间表
在这里插入图片描述

商品详情对象关联查询

商品和详情对象是一对多的关系

t_goods_detail是商品详情表

mybatis/entity/GoodsDetail.java 新建GoodsDetail实体类

package 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;
    }
}

mappers/goods_detail.xml 在mappers下新建对应的xml文件,namespace改为goodsDetail,resultType指向刚创建的实体类

建号后在文件下

<?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="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 打开Goods实体类,在对象关联时,Goods和GoodsDetail是一对多的关系,Goods是一,在Goods类中增加List<GoodsDetail>集合对象,并为其添加get,set方法

package mybatis.entity;

import java.util.List;

public class Goods {
    private Integer goodsId;//商品编号
    private String title;//标题
    private String subTitle;//子标题
    private Float originalCost;//原始价格
    private Float currentPrice;//当前价格
    private Float discount;//折扣率
    private Integer isFreeDelivery;//是否包邮 ,1-包邮 0-不包邮
    private Integer categoryId;//分类编号
    private List<GoodsDetail> goodsDetails;

    public List<GoodsDetail> getGoodsDetails() {
        return goodsDetails;
    }

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

    public void setGoodsId(Integer goodsId) {
        this.goodsId = goodsId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getSubTitle() {
        return subTitle;
    }

    public void setSubTitle(String subTitle) {
        this.subTitle = subTitle;
    }

    public Float getOriginalCost() {
        return originalCost;
    }

    public void setOriginalCost(Float originalCost) {
        this.originalCost = originalCost;
    }

    public Float getCurrentPrice() {
        return currentPrice;
    }

    public void setCurrentPrice(Float currentPrice) {
        this.currentPrice = currentPrice;
    }

    public Float getDiscount() {
        return discount;
    }

    public void setDiscount(Float discount) {
        this.discount = discount;
    }

    public Integer getIsFreeDelivery() {
        return isFreeDelivery;
    }

    public void setIsFreeDelivery(Integer isFreeDelivery) {
        this.isFreeDelivery = isFreeDelivery;
    }

    public Integer getCategoryId() {
        return categoryId;
    }

    public void setCategoryId(Integer categoryId) {
        this.categoryId = categoryId;
    }
}

测试两个对象在结构上就完成了关联,但是数据还没获取,需要在goods.xml也就是一的一方进行说明:

    <!--
        resultMap可用于一对多或者多对一的映射逻辑
        id是resultMap属性引用的标准
        type指向One的实体
    -->
    <resultMap id="rmGoods1" type="mybatis.entity.Goods">
        <!-- 映射goods对象的主键到goods_id字段 -->
        <!-- 小技巧:property的属性值都是实体类的属性 -->
        <id column="goods_id" property="goodsId"></id>
        <!--
            collection的含义是,在
            select * from t_goods limit 0,10 得到结果后,对所有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>

测试:

@Test
public void testOneToMany() throws Exception {
    SqlSession session = null;
    try {
        session = MyBatisUtils.openSqlSession();
        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.closeSqlSession(session);
    }
}

ManyToOne 多对一对象关联

只需要在多的一方实体类中增加一的一方的实体:

GoodsDetail.java 增加goods类属性和get,set方法

    private Goods goods;

goods_detail.xml 多的一方添加,注意resultMap中用的标签变成了association,引用之前写好的goods.selectById,20条多数据对goods中多对一

    <resultMap id="rmGoodsDetail" type="mybatis.entity.GoodsDetail">
        <id column="gd_id" property="gdId"></id>
        <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>

测试:打印出多个详细信息图片和对应的一个标题

这里有个大坑,因为之前学习删除语句测试时把goods_id=739这条数据删掉了,然后就一致报空指针异常,找了好久仔细检查了所有属性和调用关系也没找到问题,打断点确实就是空(回头想想我测试了前20条数据,而其中的前14条对应的goods_id=739是null,后6条不是空的,可是我看了前几个都是null就没往后看到第15个,也没寻思能一个个点15个对象的属性啊,长记性了。。。,啊啊啊啊~),找了半天灵机一动想到了之前删过的这条数据,费半天劲塞回去还真是这个问题!!!。。。。。。。

    @Test
    public void testManyToOne() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSqlSession();
            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.closeSqlSession(session);
        }
    }

通过在MyBatis中配置对象的一对多或多对一可以很大程度降低开发的工作量,所有的sql放在xml中,由MyBatis自动管理执行,降低了出错风险。

PageHelper分页插件

国人开发的分页插件,官方网站:

https://pagehelper.github.io/

使用流程

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

pom.xml

        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.2.1</version>
        </dependency>
        <dependency>
            <groupId>com.github.jsqlparser</groupId>
            <artifactId>jsqlparser</artifactId>
            <version>4.1</version>
        </dependency

mybatis-config.xml setting标签后面增加

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!-- 设置数据库类型 -->
            <property name="helperDialect" value="mysql"/>
            <!-- `开启分页合理化,小于0第一页,超过总数最后一页 -->
            <property name="reasonable" value="true"/>
        </plugin>
    </plugins>

测试:

    @Test
    /**
     * PageHelper分页查询
     */
    public void testSelectPage() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSqlSession();
            //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.closeSqlSession(session);
        }
    }

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

MySQL

select * from table limit 10,20;

Oracle

rownum是伪列,无需声明可以直接使用的隐藏列;最内侧是核心查询语句,外面两层基本是固定的。12到20条数据

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 16到18条数据

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

SQL Server 2012+ 第4行后5条,5,6,7,8,9条数据

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

MyBaits整合C3P0连接池

pom.xml 引入c3p0

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

mybaits/datasource/C3P0DataSourceFactory.java 创建数据源工厂类

package 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();
    }
}

mybatis-config.xml 将连接池类型修改为自己创建的数据源,并修改配置属性

<!--            <dataSource type="POOLED">-->
<!--                <property name="driver" value="com.mysql.jdbc.Driver"/>-->
<!--                <property name="url" value="jdbc:mysql://localhost:3306/babytun?serverTimezone=UTC&amp;characterEncoding=UTF-8"/>-->
<!--                <property name="username" value="root"/>-->
<!--                <property name="password" value="root"/>-->
<!--            </dataSource>-->
            <dataSource type="mybatis.datasource.C3P0DataSourceFactory">
                <property name="driverClass" value="com.mysql.jdbc.Driver"/>
                <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/babytun?serverTimezone=UTC&amp;characterEncoding=UTF-8"/>
                <property name="user" value="root"/>
                <property name="password" value="root"/>
<!--                初始连接池数量-->
                <property name="initialPoolSize" value="5"/>
                <property name="maxPoolSize" value="20"/>
                <property name="minPoolSize" value="5"/>
                <!--...-->
            </dataSource>

MyBatis批处理

利用集合保持批处理数据,在利用批处理SQL一次性完成

插入

goods.xml

<!--    一次插入多条数据-->
<!--    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
--         collection="list"代表迭代的数据源从哪来,一般情况下书写list,指代从外侧传来的List集合,这个名字是mybatis强制要求不能随意修改
--         item="item" 循环中的迭代遍历
--         indx="index" 循环的索引,当前是第几次循环
--         separator="," 分割器,生成文本时(("a","a1","a2"),("b","b1","b2"),...),每个记录用逗号分割
--         批量删除中传入的list中包含的是每一个要删除的数据的编号,foreach标签中要加入open="(" close=")"
        <foreach collection="list" item="item" index="index" separator=",">
            (#{item.title},#{item.subTitle},#{item.originalCost},#{item.currentPrice},#{item.discount},#{item.isFreeDelivery},#{item.categoryId})
        </foreach>
    </insert>

测试:使用批处理方式插入10000条数据,用时4164毫秒

    @Test
    public void testBatchInsert() throws Exception {
        SqlSession session = null;
        try {
            long st = new Date().getTime();
            session = MyBatisUtils.openSqlSession();
            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.closeSqlSession(session);
        }
    }

对比测试:直接插入10000条数据,同时9798毫秒,可以看到批处理方式快很多

    @Test
    public void testInsert1() throws Exception {
        SqlSession session = null;
        try{
            long st = new Date().getTime();
            session = MyBatisUtils.openSqlSession();
            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.closeSqlSession(session);
        }
    }

批量插入数据的局限,需要通过压力测试来调整

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

删除

goods.xml

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

测试:

    @Test
    public void testBatchDelete() throws Exception {
        SqlSession session = null;
        try {
            long st = new Date().getTime();
            session = MyBatisUtils.openSqlSession();
            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.closeSqlSession(session);
        }
    }

MyBatis注解开发

对于Mybatis而言,它提供了一种使用注解的开发方式,这种开发方式与使用xml进行开发比较,它更适合使用在一些小型敏捷的项目中

MyBatis常用注解
在这里插入图片描述
在之前的项目基础上新建一个项目:(pom.xml中依赖不变,工具类不变,logback自定义文件不变)
在这里插入图片描述
mybatis-config.xml中不在需要mapper对xml文件的引用,而是该成我们对接口的引用

    <mappers>
        <!-- mybatis如果使用注解开发,需要将接口当做mapper xml 进行注册-->
        <!--<mapper class="com.dodoke.mybatisannotation.dao.GoodsDao"/>-->
        <!-- 可以通过package标签将该包中接口全部注册 -->
        <package name="com.dodoke.mybatisannotation.dao"/>
    </mappers>

在mybaits包下新建dao包,此包中创建一系列接口:

GoodsDAO.java

package mybatis.dao;

import mybatis.dto.GoodsDTO;
import mybatis.entity.Goods;
import org.apache.ibatis.annotations.*;

import java.util.List;
import java.util.Map;

/**

 该接口用操作数据库
 */
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);
    /**

     对于注解开发来说,新增和删除,以及修改方法的返回值都要是int类型
     @param goods
     @return
     */
    @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()" ,before=false,keyProperty = "goodsId" ,resultType = Integer.class)
    public int insert(Goods goods);

    //增加结果映射
    @Select(" select * from t_goods order by goods_id desc limit 10")
//如果没有设置驼峰命令转换或者要设置数据转换类,或者多对一,一对多的时候,可以利用results注解
    @Results({
//设置id=true,明确指示id属性
            @Result(column = "goods_id",property = "goodsId" , id=true),
            @Result(column = "title",property = "title" ),
            @Result(column = "sub_title",property = "subTitle" ),
    })
    public List<GoodsDTO> selectLimit();
}

测试:利用GoodsDAO对象实现;查询测试,插入删除,修改都类似

    @Test
    public void testSelectByPriceRange() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSqlSession();
            GoodsDao goodsDao = session.getMapper(GoodsDao.class);
            List<Goods> list = goodsDao.selectByPriceRange(100f, 500f, 20);
            System.out.println(list.size());
        } catch (Exception e) {
            if (session != null) {
                session.rollback();//回滚事务
            }
            throw e;
        } finally {
            MyBatisUtils.closeSqlSession(session);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

摘星喵Pro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值