一对一查询

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

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

方法一:

使用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.Orders的user属性中

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();
}
总结:

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

一对多查询

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

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

使用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();
}
总结:

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

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":指定关联查询sql为selectUserById

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内部存储缓存使用一个HashMap,key为hashCode+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的二级缓存即查询缓存,它的作用域是一个mapper的namespace,即在同一个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中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

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

如下:

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

ache  的其它参数:

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

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

readOnly(只读)属性可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是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:\develop\ehcache" />
<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"/>

Mybatis笔记二的更多相关文章

  1. MyBatis笔记二:配置

    MyBatis笔记二:配置 1.全局配置 1.properites 这个配置主要是引入我们的 properites 配置文件的: <properties resource="db.pr ...

  2. Mybatis笔记二:接口式编程

    目录 旧方法的弊端 接口式编程 接口式编程的好处 接口式编程的增删改查 旧方法的弊端 在Mybatis笔记一中,我们使用命名空间+id的方式实现了Mybatis的执行,不过这里的命名空间是我们随便写的 ...

  3. mybatis笔记<二> 整合spring

    mybatis与spring整合需要添加几个jar包,mybatis-spring, spring-context, spring-jdbc 1. spring ioc只要一个jar包就ok 2. 我 ...

  4. Mybatis笔记二:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

    错误异常:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.test.dao.N ...

  5. 《CMake实践》笔记二:INSTALL/CMAKE_INSTALL_PREFIX

    <CMake实践>笔记一:PROJECT/MESSAGE/ADD_EXECUTABLE <CMake实践>笔记二:INSTALL/CMAKE_INSTALL_PREFIX &l ...

  6. jQuery源码笔记(二):定义了一些变量和函数 jQuery = function(){}

    笔记(二)也分为三部分: 一. 介绍: 注释说明:v2.0.3版本.Sizzle选择器.MIT软件许可注释中的#的信息索引.查询地址(英文版)匿名函数自执行:window参数及undefined参数意 ...

  7. Mastering Web Application Development with AngularJS 读书笔记(二)

    第一章笔记 (二) 一.scopes的层级和事件系统(the eventing system) 在层级中管理的scopes可以被用做事件总线.AngularJS 允许我们去传播已经命名的事件用一种有效 ...

  8. Python 学习笔记二

    笔记二 :print 以及基本文件操作 笔记一已取消置顶链接地址 http://www.cnblogs.com/dzzy/p/5140899.html 暑假只是快速过了一遍python ,现在起开始仔 ...

  9. WPF的Binding学习笔记(二)

    原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...

随机推荐

  1. 笔记-python-float(‘inf’)

    笔记-python-float(‘inf’) 看算法时发现了flaot(‘inf’). Python中可以用如下方式表示正负无穷: float("inf"), float(&quo ...

  2. 青岛Uber优步司机奖励政策(9月14日~9月20日)

    由于上周银行系统升级,工资延后 9/14-9/20奖励细则 滴滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不 ...

  3. P1803 凌乱的yyy

    P1803 凌乱的yyy 题目背景 快noip了,yyy很紧张! 题目描述 现在各大oj上有n个比赛,每个比赛的开始.结束的时间点是知道的. yyy认为,参加越多的比赛,noip就能考的越好(假的) ...

  4. LeetCode: 29. Divide Two Integers (Medium)

    1. 原题链接 https://leetcode.com/problems/divide-two-integers/description/ 2. 题目要求 给出被除数dividend和除数divis ...

  5. 「日常训练」Balancing Act(POJ-1655)

    题意与分析 树的重心板子题. 值得考虑的是,重心究竟有哪些优秀的性质? 这里是一些网上能看到的性质: (判定性质)找到一个点,其所有的子树中最大的子树节点数最少(子树可以"倒着看" ...

  6. JMeter常用元器件

    测试计划, 是整个工程的根节点, 可以取别名, 并添加注释, 里面的设置是全局变量: 线程组, 是一组线程的集合, 可以取别名, 并添加注释, 里面的设置只对本线程组有效: HTTP请求, 也就是取样 ...

  7. 域名、IP地址、URL关系

    域名是个文字形式记录的IP地址 IP地址是计算机在网络中的门牌号! URL是网页地址 例如1: http://zhidao.baidu.com/question/14674128.html 是URL ...

  8. [SHELL]shell中变量的使用

    1.输出变量 : #! /bin/bash my_var=BOB echo $my_var echo "hi,$my_var" echo "the price is \$ ...

  9. Appium ——Android KEYCODE键值:

    Python下语法: driver.keyevent(键值) 电话按键: 键名 描述 键值 KEYCODE_CALL 拨号键 5 KEYCODE_ENDCALL 挂机键 6 KEYCODE_HOME ...

  10. Windows环境下使用kafka单机模式

    测试运行环境 Win10 kafka_2.11-1.0.0 zookeeper-3.4.10 1.安装Zookeeper Kafka的运行依赖于Zookeeper,所以在运行Kafka之前我们需要安装 ...