mybatis基础知识回顾
1. mybatis是什么?
- mybatis是一个持久层框架,是Apache下的开源项目,前身是ibatis,是一个不完全的ORM框架,mybatis提供输入和输出的映射,需要程序员自己手动写SQL语句,mybatis重点对SQL语句进行灵活操作。
- 适用场合:需求变化频繁,数据模型不固定的项目,例如:互联网项目。
2.mybatis架构:
- SqlMapConfig.xml(名称不固定),配置内容:数据源、事务、properties、typeAliases、settings、mappers配置。
- SqlSessionFactory:会话工厂,作用是创建SqlSession,实际开发中以单例模式管理SqlSessionFactory。
- SqlSession:会话,是一个面向用户(程序员)的接口,使用mapper代理方法开发是不需要程序员直接调用Sqlsession的方法。它是线程不安全的,最佳使用场合是方法体内。
3.mybatis开发DAO的方法
3.1 原始DAO开发方法,需要程序员编写Dao接口和实现类,此方法在当前企业中还有使用因为ibatis用的就是这种原始的Dao开发方式。
3.2 mapper代理方法,程序员只需要编写mapper接口(相当于DAO接口),mybatis自动根据mapper接口和mapper接口对应的statement自动生成代理对象(接口实现类对象),注意使用mapper代理方法开发需要遵循以下规则:
- mapper.xml中namespace是mapper接口的全限定名。
- mapper.xml中statement的id为mapper接口的方法名。
- mapper.xml中statement输入类型(parameterType)和mapper接口方法输入参数类型一致。
- mapper.xml中statement输出类型(resultType)和mapper接口方法返回结果类型一致。
resultType和resultMap都可以完成输出映射:
resultType映射要求SQL查询的列名和输出映射pojo类型的属性名一致。
resultMap映射时对SQL查询的列名和输出映射pojo类型的属性名做一个对应关系。
4.动态SQL:
#{}和${}完成输入参数的属性值获取,通过OGNL获取parameterType指定的pojo的属性名。
#{}:占位符号,好处是防止SQL注入。
${}: SQL拼接符号,无法防止SQL注入。
if、where以及foreach的使用。
mybatis重点高级知识清单
1.使用resultMap完成高级映射(重点)
—|学习商品订单数据模型(一对一、一对多、多对多)
—|resultMap实现一对一、一对多、多对多
—|延迟加载
2.查询缓存(重点)
—|一级缓存
—|二级缓存
3.mybatis和Spring的整合(重点)
4.mybatis逆向工程(常用)
商品订单数据模型
技巧总结:学会在企业中如何去分析陌生表的数据模型
1.学习单表记录了什么东西(去学习理解需求)
2.学习单表重要字段的意义(优先学习不能为空的字段)
3.学习表与表之间的关系(一对一、一对多、多对多)
通过表的外键分析表之间的关系。
注意:分析表与表之间的关系是建立在业务意义基础之上的。
用户表user:记录了购买商品的用户
订单表orders:记录了用户所创建的订单信息
订单明细表orderdetail:记录了用户所创建订单的详细信息
商品信息表items:记录了商家提供的商品信息。
分析表与表之间的订单关系:
(1) 用户表user和订单orders:
user—>orders: 一个用户可以创建多个订单 多对多
orders—>user: 一个订单只能由一个用户创建 一对一
(2) 订单表orders和订单明细表orderdetail:
orders—>orderdetail: 一个订单可以包括多个订单明细 一对多
orderdetail—>orders: 一个订单明细只能属于一个订单 一对一
(3)订单明细orderdetail和商品信息items
orderdetail—>items: 一个订单明细只能对应一个商品信息 一对一
items—>orderdetail: 一个商品对应多个订单明细 一对多
一对一查询
需求
查询订单信息关联查询用户信息
sql语句
查询语句:
先确定主查询表:订单信息表
再确定关联查询表:用户信息
通过orders关联穿用户使用user_id一个外键,根据一条订单数据只能查询出一条用户记录就可以使用内连接
- SELECT
- orders.*, user.username, user.sex
- FROM
- orders,
- user
- WHERE
- orders.user_id = user.id;
使用resultType实现
创建PO类
创建基础表单的PO类
一对一查询映射的pojo
创建pojo包括 订单信息和用户信息,resultType才可以完成映射
创建OrderCustom作为自定义pojo,继承 SQL查询中列最多的POJO类
- public class OrderCustom extends Orders {
-
- private String username;
- private String sex;
- private String address;
-
- ......
- }
mapper.xml
-
- <resultMap type="test.lx.mybatis.po.Orders" id="ordersUserResultMap">
-
-
- <id column="id" property="id"/>
- <result column="user_id" property="userId"/>
- <result column="number" property="number"/>
- <result column="createtime" property="createtime"/>
- <result column="note" property="note"/>
-
-
-
- <id column="user_id" property="id"/>
-
- <result column="username" property="username"/>
- <result column="sex" property="sex"/>
- association>
- resultMap>
注意的是:两个id的配置以及column的配置 外层的id配置一般指主查询表的主键,内层一般指的是关联查询的外键,共同点是其column的值都为执行其SQL语句查询出来的名称。起property是对应查询表的PO类中的属性名。
mapper.java
-
- public List findOrderUserListResultMap() throws Exception;
小结
resultType:要自定义pojo 保证SQL查询列名要和pojo的属性对应,这种方法相对较简单,所以应用广泛。
resultMap:使用association完成一对一映射需要配置resultMap,过程有点复杂,如果要实现延迟加载就只能使用resultMap来进行实现,如果为了方便对关联信息的解析,也可使用association将关联信息映射到pojo中方便解析。
一对多查询
需求
查询所有订单信息及订单下的订单明细信息
SQL语句
主查询表:订单表
关联查询表:订单明细
- SELECT
- orders.*,
- user.username,
- user.sex ,
- orderdetail.id orderdetail_id,
- orderdetail.items_num,
- orderdetail.items_id
- FROM
- orders,
- USER,
- orderdetail
- WHERE orders.user_id = user.id AND orders.id = orderdetail.orders_id
查询结果
需要将订单明细内容设置到对应的订单中
resultMap进行一对多映射思路
resultMap提供collection完成关联信息映射到集合对象中。
在orders类中创建集合属性orderdetails:
- public class Orders {
- private Integer id;
- private Integer userId;
- private String number;
-
- private Date createtime;
-
- private String note;
-
-
- private User user;
-
-
- private List orderDetails;
-
- ......
mapper.xml
-
- <resultMap type="orders" id="orderAndOrderDetails" extends="ordersUserResultMap">
-
-
-
- <resultMap type="user" id="userOrderDetailResultMap">
-
- <id column="user_id" property="id"/>
- <result column="username" property="username"/>
- <result column="sex" property="sex"/>
-
- <collection property="orderlist" ofType="test.lx.mybatis.po.Orders">
- <id column="id" property="id"/>
- <result column="user_id" property="userId"/>
- <result column="number" property="number"/>
- <result column="createtime" property="createtime"/>
- <result column="note" property="note"/>
-
- <collection property="orderDetails" ofType="test.lx.mybatis.po.OrderDetail">
-
- <association property="items" javaType="test.lx.mybatis.po.Items">
- <id column="item_id" property="id"/>
- <result column="items_name" property="name"/>
- <result column="items_detail" property="detail"/>
- association>
- collection>
- collection>
- resultMap>
mapper.java
-
- public List findUserOrderDetail() throws Exception;
多对多查询
一对多是多对多的特例。
需求1:
查询显示字段:用户账号、用户名称、用户性别、商品名称、商品价格(最常见)
企业开发中常见明细列表,用户购买商品明细列表,
使用resultType将上边查询列映射到pojo输出。
需求2:
查询显示字段:用户账号、用户名称、购买商品数量、商品明细(鼠标移上显示明细)
使用resultMap将用户购买的商品明细列表映射到user对象中。
实现方法和一对多一样。
延迟加载
使用延迟加载的意义
在进行数据查询时,为了提高数据库查询性能,尽量使用单表查询,因为单表查询比多表关联查询速度快。
如果查询单表就可以满足需求,一开始先查询单表,当需要关联信息时,再关联查询。当需要关联信息时才进行查询就叫做延迟加载。mybatis中resultMap提供延迟加载功能,通过resultMap配置延迟加载。
在SqlMapConfig.xml中配置全局参数
-
- <settings>
-
- <setting name="lazyLoadingEnabled" value="true"/>
-
- <setting name="aggressiveLazyLoading" value="false"/>
- settings>
延迟加载实现
实现思路
需求:查询订单及用户信息,一对一查询
刚开始只查询订单信息,当需要用户信息时调用Orders类中的getUser()方法执行延迟加载,向数据库发出SQL。
mapper.xml
-
- <resultMap type="orders" id="orderCustomLazyLoading">
-
-
- <id column="id" property="id"/>
- <result column="user_id" property="userId"/>
- <result column="number" property="number"/>
- <result column="createtime" property="createtime"/>
- <result column="note" property="note"/>
-
- <diskStore path="/Users/liuxun/Desktop/ehcache" />
- <defaultCache
- maxElementsInMemory="1000"
- maxElementsOnDisk="10000000"
- eternal="false"
- overflowToDisk="false"
- diskPersistent="true"
- timeToIdleSeconds="120"
- timeToLiveSeconds="120"
- diskExpiryThreadIntervalSeconds="120"
- memoryStoreEvictionPolicy="LRU">
- defaultCache>
- ehcache>
整合ehcache
在mapper.xml下添加ehcache配置
-
-
- <cache type="org.mybatis.caches.ehcache.EhcacheCache">
- <property name="timeToIdleSeconds" value="12000"/>
- <property name="timeToLiveSeconds" value="3600"/>
-
- <property name="maxEntriesLocalHeap" value="1000"/>
-
- <property name="maxEntriesLocalDisk" value="10000000"/>
- <property name="memoryStoreEvictionPolicy" value="LRU"/>
- cache>
二级缓存的应用场景
对于查询频率高,变化频率低的数据建议使用二级缓存。
对于访问多的查询请求且用户对查询结果的实时性要求不高,此时可采用mybatis二级缓存技术降低数据库的访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。
实现方法如下:通过设置刷新时间间隔,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新时间间隔flushInterval,比如设置30分钟、60分钟、24小时等,根据需求而定。
mybatis局限性
mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。
项目代码
完整代码已经上传GitHub(https://github.com/LX1993728/mybatisDemo_2)
OrdersMapperCustom.java
- package test.lx.mybatis.mapper;
-
- import java.util.List;
-
- import test.lx.mybatis.po.OrderCustom;
- import test.lx.mybatis.po.Orders;
- import test.lx.mybatis.po.User;
-
-
-
-
-
-
-
- public interface OrdersMapperCustom {
-
- public List findOrderUserList() throws Exception;
-
-
- public List findOrderUserListResultMap() throws Exception;
-
-
- public List findOrderUserListLazyLoading() throws Exception;
-
-
- public List findOrderAndOrderdetails() throws Exception;
-
-
- public List findUserOrderDetail() throws Exception;
- }
OrdersMapperCustom.xml
- xml version="1.0" encoding="UTF-8"?>
- PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="test.lx.mybatis.mapper.OrdersMapperCustom">
-
-
- <resultMap type="orders" id="ordersUserResultMap">
-
-
- <id column="id" property="id"/>
- <result column="user_id" property="userId"/>
- <result column="number" property="number"/>
- <result column="createtime" property="createtime"/>
- <result column="note" property="note"/>
-
-
-
- <id column="user_id" property="id"/>
-
- <result column="username" property="username"/>
- <result column="sex" property="sex"/>
- association>
- resultMap>
-
-
- <resultMap type="orders" id="orderCustomLazyLoading">
-
-
- <id column="id" property="id"/>
- <result column="user_id" property="userId"/>
- <result column="number" property="number"/>
- <result column="createtime" property="createtime"/>
- <result column="note" property="note"/>
-
- <resultMap type="orders" id="orderAndOrderDetails" extends="ordersUserResultMap">
-
-
-
- <resultMap type="user" id="userOrderDetailResultMap">
-
- <id column="user_id" property="id"/>
- <result column="username" property="username"/>
- <result column="sex" property="sex"/>
-
- <collection property="orderlist" ofType="test.lx.mybatis.po.Orders">
- <id column="id" property="id"/>
- <result column="user_id" property="userId"/>
- <result column="number" property="number"/>
- <result column="createtime" property="createtime"/>
- <result column="note" property="note"/>
-
- <collection property="orderDetails" ofType="test.lx.mybatis.po.OrderDetail">
-
- <association property="items" javaType="test.lx.mybatis.po.Items">
- <id column="item_id" property="id"/>
- <result column="items_name" property="name"/>
- <result column="items_detail" property="detail"/>
- association>
- collection>
- collection>
- resultMap>
-
-
- <diskStore path="/Users/liuxun/Desktop/ehcache" />
- <defaultCache
- maxElementsInMemory="1000"
- maxElementsOnDisk="10000000"
- eternal="false"
- overflowToDisk="false"
- diskPersistent="true"
- timeToIdleSeconds="120"
- timeToLiveSeconds="120"
- diskExpiryThreadIntervalSeconds="120"
- memoryStoreEvictionPolicy="LRU">
- defaultCache>
- ehcache>
UserMapper.xml
逆向工程运行所需要的jar包
数据库驱动包。