• Mybatis笔记二


    一对一查询

    案例:查询所有订单信息,订单信息中显示下单人信息。

    注意:因为一个订单信息只会是一个人下的订单,所以从查询订单信息出发关联查询用户信息为一对一查询。如果从用户信息出发查询用户下的订单信息则为一对多查询,因为一个用户可以下多个订单。

    方法一:

    使用resultType,定义订单信息po类,此po类中包括了订单信息和用户信息:

    Sql语句:

    SELECT

      orders.*,

      user.username,

      user.address

    FROM

      orders,

      USER

    WHERE orders.user_id = user.id

    定义po

    Po类中应该包括上边sql查询出来的所有字段,如下:

    public class UserOrder extends Orders {
    
        private String username;// 用户姓名
        private String address;// 地址
    get/set。。。。

    UserOrder类继承Orders类后UserOrder类包括了Orders类的所有字段,只需要定义用户的信息字段即可。

    Mapper.xml
    <!-- 查询所有订单信息 -->
        <select id="findOrdersList" resultType="cn.itcast.mybatis.po.UserOrder">
        SELECT
        orders.*,
        user.id user_id,
        user.username,
        user.address
        FROM
        orders,    USER
        WHERE orders.user_id = user.id 
        </select>
    Mapper接口:
    public List<UserOrder> findOrdersList() throws Exception;
    测试:
    public void testfindOrdersList()throws Exception{
            //获取session
            SqlSession session = sqlSessionFactory.openSession();
            //获限mapper接口实例
            UserMapper userMapper = session.getMapper(UserMapper.class);
            //查询订单信息
            List<UserOrder> list = userMapper.findOrdersList();
            System.out.println(list);
            //关闭session
            session.close();
        }
    总结:

    定义专门的po类作为输出类型,其中定义了sql查询结果集所有的字段。此方法较为简单,企业中使用普遍。

    方法二:

    使用resultMap,定义专门的resultMap用于映射一对一查询结果。

    Sql语句:

    SELECT

      orders.*,

      user.username,

      user.address

    FROM

      orders,

      USER

    WHERE orders.user_id = user.id

    定义po

    Orders类中加入User属性。

    Mapper.xml
    <select id="findOrdersList2" resultMap="userordermap">
        SELECT
        orders.*,
        user.username,
        user.address
        FROM
        orders,    USER
        WHERE orders.user_id = user.id 
        </select>

    这里resultMap指定userordermap

    定义resultMap
    <!-- 订单信息resultmap -->
    <resultMap type="cn.itcast.mybatis.po.Orders" id="userordermap">
        <!-- 这里的id,是mybatis在进行一对一查询时将user字段映射为user对象时要使用,必须写 -->
            <id property="id" column="id" />
            <result property="user_id" column="user_id" />
            <result property="order_number" column="order_number" />
            <association property="user" javaType="cn.itcast.mybatis.po.User">
               <!-- 这里的id为user的id,如果写上表示给user的id属性赋值 -->
               <id property="id" column="user_id" />
                <result property="username" column="username" />
                <result property="address" column="address" />
            </association>
        </resultMap>

    association:表示进行关联查询单条记录

    property:表示关联查询的结果存储在cn.itcast.mybatis.po.Ordersuser属性中

    javaType:表示关联查询的结果类型

    <id property="id" column="user_id" />:查询结果的user_id列对应关联对象的id属性,这里是<id />表示user_id是关联查询对象的唯一标识。

    <result property="username" column="username" />:查询结果的username列对应关联对象的username属性。

    Mapper接口:
    public List<Orders> findOrdersList2() throws Exception;
    测试:
    public void testfindOrdersList2()throws Exception{
            //获取session
            SqlSession session = sqlSessionFactory.openSession();
            //获限mapper接口实例
            UserMapper userMapper = session.getMapper(UserMapper.class);
            //查询订单信息
            List<Orders> list = userMapper.findOrdersList2();
            System.out.println(list);
            //关闭session
            session.close();
        }
    总结:

    此种方法使用了mybatisassociation标签用于一对一关联查询,将查询结果映射至对象中。

    一对多查询

    案例:查询所有订单信息及订单下的订单明细信息。

    订单信息与订单明细为一对多关系,一个订单包括多个商品信息。

    使用resultMap实现如下:

    ql语句:

    SELECT

      orders.*,

      user.username,

      user.address,

      orderdetail.id orderdetail_id,

      orderdetail.item_id,

      orderdetail.item_num,

      orderdetail.item_price

    FROM

      orders,USER ,orderdetail

    WHERE orders.user_id = user.id

    AND orders.id = orderdetail.orders_id

    定义po

    Orders类中加入User属性。

    Orders类中加入List<Orderdetail> orderdetails 属性

    Mapper.xml
    <select id="findOrdersDetailList" resultMap="userorderdetailmap">
        SELECT
        orders.*,
        user.username,
        user.address,
        orderdetail.id orderdetail_id,
        orderdetail.item_id,
        orderdetail.item_num,
        orderdetail.item_price
        FROM orders,USER ,orderdetail
        WHERE orders.user_id = user.id
        AND orders.id = orderdetail.orders_id
    </select>

    定义resultMap

    <!-- 订单信息resultmap -->
        <resultMap type="cn.itcast.mybatis.po.Orders" id="userorderdetailmap">
            <id property="id" column="id" />
            <result property="user_id" column="user_id" />
            <result property="order_number" column="order_number" />
            <association property="user" javaType="cn.itcast.mybatis.po.User">
                <id property="id" column="user_id" />
                <result property="username" column="username" />
                <result property="address" column="address" />
            </association>
            <collection property="orderdetails" ofType="cn.itcast.mybatis.po.Orderdetail">
                <id property="id" column="orderdetail_id" />
                 <result property="item_id" column="item_id" />
                 <result property="item_num" column="item_num" />
                 <result property="item_price" column="item_price" />
            </collection>
        </resultMap>

    collection部分定义了查询订单明细信息。

    collection:表示关联查询结果集

    property="orderdetails"关联查询的结果集存储在cn.itcast.mybatis.po.Orders上哪个属性。

    ofType="cn.itcast.mybatis.po.Orderdetail"指定关联查询的结果集中的对象类型即List中的对象类型。

    <id /><result/>的意义同一对一查询。

    Mapper接口:
    public List<Orders> findOrdersDetailList () throws Exception;
    测试:
    public void testfindOrdersDetailList()throws Exception{
            //获取session
            SqlSession session = sqlSessionFactory.openSession();
            //获限mapper接口实例
            UserMapper userMapper = session.getMapper(UserMapper.class);
            //查询订单信息
            List<Orders> list = userMapper.findOrdersDetailList();
            System.out.println(list);
            //关闭session
            session.close();
        }
    总结:

    此种方法使用了mybatiscollection标签用于一对多关联查询,将查询结果映射至集合对象中。

    resultMap使用继承

    上边定义的resultMap中user部分和一对一查询订单信息的resultMap相同,这里使用继承可以不再填写重复的内容,如下:

    <resultMap type="cn.itcast.mybatis.po.Orders" id="userorderdetailmap2" extends="userordermap">
            <collection property="orderdetails" ofType="cn.itcast.mybatis.po.Orderdetail">
                <id property="id" column="orderdetail_id" />
                 <result property="item_id" column="item_id" />
                 <result property="item_num" column="item_num" />
                 <result property="item_price" column="item_price" />
            </collection>
        </resultMap>

    使用extends继承订单信息userordermap

    多对多查询

    案例:查询所有订单信息及订单明细的商品信息。

    订单信息与商品信息为多对多关系,因为一个订单包括多个商品信息,一个商品可以在多个订单中存在,订单信息与商品信息的多对多关系是通过订单明细表进行关联。

    Sql语句:

    SELECT

      orders.*,

      user.username,

      user.address,

      orderdetail.id orderdetail_id,

      orderdetail.item_id,

      orderdetail.item_num,

      orderdetail.item_price,

      items.item_name,

      items.item_detail

    FROM

      orders,USER ,orderdetail,items   

    WHERE orders.user_id = user.id

    AND orders.id = orderdetail.orders_id

    AND orderdetail.item_id = items.id

    定义po

    Orders类中加入User属性。

    Orders类中加入List<Orderdetail> orderdetails 属性,存储订单明细信息

    在Orderdetail类中加入Items items 属性存储商品信息

    Mapper.xml
    <select id="findOrdersItemsList" resultMap="userorderitemsmap">
            SELECT
            orders.*,
            user.username,
            user.address,
            orderdetail.id orderdetail_id,
            orderdetail.item_id,
            orderdetail.item_num,
            orderdetail.item_price,
            items.item_name,
            items.item_detail
            FROM
            orders,USER ,orderdetail,items
    
            WHERE orders.user_id = user.id
            AND orders.id = orderdetail.orders_id
            AND orderdetail.item_id = items.id
        </select>
    定义resultMap
    <!-- 订单商品信息resultmap -->
        <resultMap type="cn.itcast.mybatis.po.Orders" id="userorderitemsmap"
            extends="userordermap">
            <collection property="orderdetails" ofType="cn.itcast.mybatis.po.Orderdetail">
                <id property="id" column="orderdetail_id" />
                <result property="item_id" column="item_id" />
                <result property="item_num" column="item_num" />
                <result property="item_price" column="item_price" />
                <!-- 商品信息 -->
                <association property="items" javaType="cn.itcast.mybatis.po.Items">
                    <id property="id" column="item_id" />
                    <result property="item_name" column="item_name" />
                    <result property="item_detail" column="item_detail" />
                </association>
            </collection>
        </resultMap>

    collection中加入association通过订单明细表关联查询商品信息

    Mapper接口:
    public List<Orders> findOrdersItemsList () throws Exception;
    测试:
    public void findOrdersItemsList()throws Exception{
            //获取session
            SqlSession session = sqlSessionFactory.openSession();
            //获限mapper接口实例
            UserMapper userMapper = session.getMapper(UserMapper.class);
            //查询订单信息
            List<Orders> list = userMapper.findOrdersItemsList();
            System.out.println(list);
            //关闭session
            session.close();
        }
    总结:

    所谓一对多查询、多对多查询都对于具体的业务分析来说,使用mybatis提交的collection和association可以完成不同的关联查询需求,通常在实际应用时association用自定义pojo方式代替,关联查询结果集使用collection完成。

    延迟加载

    需要查询关联信息时,使用mybatis延迟加载特性可有效的减少数据库压力,首次查询只查询主要信息,关联信息等用户获取时再加载。

    打开延迟加载开关

    mybatis核心配置文件中配置:

    lazyLoadingEnabled、aggressiveLazyLoading

    设置项

    描述

    允许值

    默认值

    lazyLoadingEnabled

    全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。

    true | false

    false

    aggressiveLazyLoading

    当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。

    true | false

    true

    <settings>
             <setting name="lazyLoadingEnabled" value="true"/>
             <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

    一对一查询延迟加载

    Sql语句: 

    SELECT

      orders.*

    FROM

      orders 

    定义po

    Orders类中加入User属性。

    定义resultMap
      <!-- 订单信息resultmap -->
        <resultMap type="cn.itcast.mybatis.po.Orders" id="userordermap2">
            <id property="id" column="id" />
            <result property="user_id" column="user_id" />
            <result property="order_number" column="order_number" />
            <association property="user" javaType="cn.itcast.mybatis.po.User" select="selectUserById" column="user_id" />
        </resultMap>

    association:

    select="selectUserById":指定关联查询sqlselectUserById

    column="user_id":关联查询时将user_id列的值传入selectUserById

    最后将关联查询结果映射至cn.itcast.mybatis.po.User

    Mapper.xml
    <select id="findOrdersList3" resultMap="userordermap2">
        SELECT
        orders.*
        FROM
        orders
    </select>
    Mapper接口:
    public List<Orders> findOrdersList3() throws Exception;

    测试:

    public void testfindOrdersList3()throws Exception{
            //获取session
            SqlSession session = sqlSessionFactory.openSession();
            //获限mapper接口实例
            UserMapper userMapper = session.getMapper(UserMapper.class);
            //查询订单信息
            List<Orders> list = userMapper.findOrdersList3();
            System.out.println(list);
            //开始加载,通过orders.getUser方法进行加载
    for(Orders orders:list){
                System.out.println(orders.getUser());
            }
            //关闭session
            session.close();
        }
    总结:

    使用延迟加载提高数据库查询性能,默认不查询关联数据,按需要发出sql请求关联查询信息。

    一级缓存

    Mybatis一级缓存的作用域是同一个SqlSession

    第一个例子:

    //获取session
            SqlSession session = sqlSessionFactory.openSession();
            //获限mapper接口实例
            UserMapper userMapper = session.getMapper(UserMapper.class);
            //第一次查询
            User user1 = userMapper.selectUserById(1);
            System.out.println(user1);
    //第二次查询,由于是同一个session则不再向数据发出语句直接从缓存取出
            User user2 = userMapper.selectUserById(1);
            System.out.println(user2);
    原理:

    Mybatis首先去缓存中查询结果集,如果没有则查询数据库,如果有则从缓存取出返回结果集就不走数据库。

    Mybatis内部存储缓存使用一个HashMapkeyhashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象

    第二个例子:

    //获取session
            SqlSession session = sqlSessionFactory.openSession();
            //获限mapper接口实例
            UserMapper userMapper = session.getMapper(UserMapper.class);
            //第一次查询
            User user1 = userMapper.selectUserById(1);
            System.out.println(user1);
            //在同一个session执行更新
            User user_update = new User();
            user_update.setId(1);
            user_update.setUsername("李奎");
            userMapper.updateUser(user_update);
            session.commit();
            //第二次查询,虽然是同一个session但是由于执行了更新操作session的缓存被清空,这里重新发出sql操作
            User user2 = userMapper.selectUserById(1);
            System.out.println(user2);
    原理

    该例子与第一个例子不同的是在两次查询中间加入了更新,更新操作执行后mybatis执行了清除缓存即清空HashMap

    二级缓存

    Mybatis的二级缓存即查询缓存,它的作用域是一个mappernamespace,即在同一个namespace中查询sql可以从缓存中获取数据。

    二级缓存是可以跨SqlSession的。

    启二级缓存:

    1. 在核心配置文件SqlMapConfig.xml中加入

    <setting name="cacheEnabled" value="true"/>

    描述

    允许值

    默认值

    cacheEnabled

    对在此配置文件下的所有cache 进行全局性开/关设置。

    true false

    true

    1. 要在你的Mapper映射文件中添加一行:  <cache /> 
    2. select语句中useCache=false可以禁用当前的语句的二级缓存,即每次查询夸session 的查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

    实现序列化:

    注意:将查询结果的pojo对象进行序列化实现 java.io.Serializable接口

    例子:

    //获取session1
            SqlSession session1 = sqlSessionFactory.openSession();
            UserMapper userMapper = session1.getMapper(UserMapper.class);
            //使用session1执行第一次查询
            User user1 = userMapper.selectUserById(1);
            System.out.println(user1);
            //关闭session1
            session1.close();
            //获取session2
            SqlSession session2 = sqlSessionFactory.openSession();
            UserMapper userMapper2 = session2.getMapper(UserMapper.class);
            //使用session2执行第二次查询,由于开启了二级缓存这里从缓存中获取数据不再向数据库发出sql
            User user2 = userMapper2.selectUserById(1);
            System.out.println(user2);
            //关闭session2
            session2.close();

    刷新缓存

    mapper的同一个namespace中,如果有其它insertupdatedelete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

    sql中的 flushCache="true" 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。 

    如下:

    <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">

    ache  的其它参数:

    flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。

    size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024

    readOnly(只读)属性可以被设置为truefalse。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false

    如下例子:

    <cache  eviction="FIFO"  flushInterval="60000"  size="512"  readOnly="true"/>

    这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。可用的收回策略有默认的是 LRU:

    1. LRU  最近最少使用的:移除最长时间不被使用的对象。
    2. FIFO  先进先出:按对象进入缓存的顺序来移除它们。
    3. SOFT  软引用:移除基于垃圾回收器状态和软引用规则的对象。
    4. WEAK  弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

    应用场景:

    1、针对复杂的查询或统计的功能,用户不要求每次都查询到最新信息,使用二级缓存,通过刷新间隔flushInterval设置刷新间隔时间,由mybatis自动刷新。

    比如:实现用户分类统计sql,该查询非常耗费时间。

    将用户分类统计sql查询结果使用二级缓存,同时设置刷新间隔时间:flushInterval(一般设置时间较长,比如30分钟,60分钟,24小时,根据需求而定)

    2、针对信息变化频率高,需要显示最新的信息,使用二级缓存。

    将信息查询的statement与信息的增、删、改定义在一个mapper.xml中,此mapper实现二级缓存,当执行增、删、修改时,由mybatis及时刷新缓存,满足用户从缓存查询到最新的数据。

    比如:新闻列表显示前10条,该查询非常快,但并发大对数据也有压力。

    将新闻列表查询前10条的sql进行二级缓存,这里不用刷新间隔时间,当执行新闻添加、删除、修改时及时刷新缓存。

    二级缓存使用Ehcache

    Mybatis与缓存框架ehcache进行了整合,采用ehcache框架管理缓存数据。

    第一步:引入缓存的依赖包

    第二步:引入缓存配置文件

    ehcache.xml

    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <diskStore path="F:developehcache" />
    <defaultCache
    maxElementsInMemory="1000"
    maxElementsOnDisk="10000000"
    eternal="false"
    overflowToDisk="false"
    timeToIdleSeconds="120"
    timeToLiveSeconds="120"
    diskExpiryThreadIntervalSeconds="120"
    memoryStoreEvictionPolicy="LRU">
    </defaultCache>
    </ehcache>

    defaultCache配置说明:

    maxElementsInMemory 内存中最大缓存对象数.当超过最大对象数的时候,ehcache会按指定的策略去清理内存
    eternal 缓存对象是否永久有效,一但设置了,timeout将不起作用.
    timeToIdleSeconds 设置Element在失效前的允许闲置时间.仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大.
    timeToLiveSeconds:设置Element在失效前允许存活时间.最大时间介于创建时间和失效时间之间.仅当element是永久有效时使用,默认是0.,也就是element存活时间无穷大.
    overflowToDisk 配置此属性,当内存中Element数量达到maxElementsInMemory,Ehcache将会Element写到磁盘中.
    diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
    maxElementsOnDisk 磁盘中最大缓存对象数,若是0表示无穷大.
    diskPersistent 是否在重启服务的时候清楚磁盘上的缓存数据.true不清除.
    diskExpiryThreadIntervalSeconds 磁盘失效线程运行时间间隔.
    memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存.默认策略是LRU(最近最少使用).你可以设置为FIFO(先进先出)或是LFU(较少使用).

    第三步:修改mapper文件中缓存类型

    cache中指定type

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

  • 相关阅读:
    边推改革边上“保险” 央行“双降”两大亮点带来哪些变化
    今天走势将冲高回落后重新回归下跌周期
    小心!资本正在流出中国:国际收支表里被遗漏的-2547亿美元
    价格改革确立时间表和路线图 六大重点领域破题
    避免在办公室体重上升的三大良策
    别再说自己有多忙
    沪指可能展开一波3个交易日的调整
    专车新政博弈 垄断行业改革样本
    JS和CS互访【后台前台代码调用JavaScript变量以及JavaScript调用代码变量】
    net9:图片文件转换成二进制流存入SQL数据库,以及从数据库中读取二进制流输出文件
  • 原文地址:https://www.cnblogs.com/lm970585581/p/9563026.html
Copyright © 2020-2023  润新知