user[购买商品用户信息表]
购买商品的用户信息
id:自增主键,无特殊业务意义[用户所有的信息查询的最重要的条件]
order[订单表]
记录用户创建的订单,购买商品的订单[一个用户可以下多个订单]
id:自增主键,无特殊业务意义[主键]
user_id:下达订单的用户的id[外键]
number:用户创建的每一个订单的订单唯一编号[非空]
createtime:订单创建时间[非空]
orderdetail[订单明细表]
记录订单的详细信息,即订单购买商品的信息[订单明细表]
id:自增主键,无特殊业务意义
orders_id:订单的id
items_id;商品的id
items_num:商品的数量
一个订单号可以在这个表中重复出现,就能够实现一个用户购买多个商品
items[商品表]
用户可以购买的商品的详细信息
id:自增主键,无特殊业务意义
name:商品名称,非空
数据库级别存在关系的
user和order
user à order
一个用户可以下多个订单,一对多
user ß order
一个订单只由一个用户创建,一对一
order和orderdetail
orde à 和orderdetail
一个订单可以包括多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail中记录,一对多的关系
orde ß 和orderdetail
一个订单的明细只能包括在一个订单中,一对一的关系
orderdetail和items
orderdetail和items
一个订单的明细到一个商品,一对一
orderdetail和items
一个商品可以对应多个订单明细.一对多
分析数据库级别无关系,但是实际业务有关系的
order和items,通过orderdetail建立关系
order和items是多对多的关系
user和items,通过orderdetail建立关系
user和items是多对多的关系
插入数据的方式
先插入用户信息和商品信息,创建是不需要依赖于外键的,是最先创建的数据项!!!
然后实际业务由用户创建
分析结构图
需求
查询订单信息,关联查询订单的用户信息
sql语句
确定查询的主表,再查询从表
主表:订单表
从表:用户表
使用内连接还是外链接
由于orders表中有一个外键[user_id],通过外键关联查询用户表,则使用内连接
SELECT * FROM user,orders WHERE orders.user_id=user.id
查询的是以以user为主表, orders为从表,会有问题,相当于查询所有用户的所有的订单信息
SELECT * FROM orders,user WHERE orders.user_id=user.id
查询的是以以orders为主表, user为从表,相当于查询所有订单的用户信息,是以订单为主,订单只由外键user_id
sql
SELECT
orders.*,
user.username,
user.birthday,
user.sex,
user.address
FROM
order,user
WHERE
orders.user_id=user.id
resultType实现
pojo
resultType必须将所有的查询字段全部映射成功,才能进行查询,然而原始的pojo不能完全映射所有连接查询的数据,需要创建新的pojo
这样相当于对原有的Orders进行了拓展,还不破坏原有的封装,这就是java继承的好处,相当于生成了一个新的pojo
mapper.xml
直接将所有查询的属性一次映射到一个pojo中,简单快速
mapper.java
测试
resultMap实现
pojo
resultMap具体的使用及其原理:将所有的查询结果映射到一个类中,通过resultMap实现查询结果的映射, resultMap也是映射一个pojo类,但是这个类中可以有其他类对象的引用,通过其它类对象的引用进行映射,但是在映射的时候,mybatis无法解析复杂的pojo,所以在引用的其他pojo对象的具体映射时,就需要配置resultMap进行具体的映射
将查询出的结果映射到orders中,在orders这个pojo中添加user属性,将sql查询出的user表中的数据映射到user属性中,具体如何映射user使用resultMap进行具体的配置
mapper.xml
查询
resultMap具体映射
重点难点:
mapper.java
测试
resultType与resultMap总结
实现一对一查询:
resultType:
使用resultType.实现较为简单,如果pgje.中没有包括查询出来的列名,需要增加列名对应的属性,即可完成映射。如果没有查询结果的特殊要求建议使用resultType。
resultMap:
需要单独定义resultMap,实现有点麻烦,如果有对查询结果有特殊的要求,使用resultMap.可以完成将关联查询映射pgj.的属性中。
resultMap可以实现延迟加载,resultType.无法实现延迟加载
需求
查询订单及订单明细的信息
注意:一个订单对应多个订单明细,因为每个订单有多个商品
sql语句
关联
主表:订单表
从表:订单明细表
sql
select
orders.*,
User.username,user.sex,User.address,
orderdetial.id orderdetial_id,orderdetial.items_id,
orderdetial.items_num,orderdetial.orders_id
from
orders,user,orderdetial
where
orders.user_id = user.id and orderdetial.orders_id=orders.id
pojo
如果订单表对应的一条订单号有两条两条以上对应的订单明细记录,就会出现订单号多次出现的情况,但是要将一个订单号的订单全部封装到一个orders中,这样同一个订单号会创建多个pojo,所以在orders中添加一个List<.orderdetail> orderdetail属性,将一个订单号对应的所有的orderdetail全部添加到list集合中进行映射
mapper.xml
mapper.java
使用resultType可以操作,但是查询出的数据需要进行去重处理
测试
需求
查询用户的购买信息
sql
主表:
从表:
select
orders.*,
User.username,user.sex,User.address,
orderdetial.id orderdetial_id,orderdetial.items_id,
orderdetial.items_num,orderdetial.orders_id,
items.detail items_detail,items.name items_name,items.price items_price
from
orders,user,orderdetial,items
where
orders.user_id = user.id and
orderdetial.orders_id=orders.id and
items.id = orderdetial.items_id
pojo
mapper.xml
mapper.java
测试
使用resultMap的collection可以方便的使用树形结构进行数据导航
延迟加载使用条件
resultMap可以实现高级映射,[使用association, collection实现一对一一对多映射]
而assision,collection具备延迟加载,可以先从单表查询,再去关联查询
好处
单查询较快,关联查询较慢,使用延迟加载可以提高数据库的查询效率
查询订单并关联查询用户信息
当需要查询用户信息时才查询用户信息,不需要查询用户信息时不查询用户信息
mapper.xml
定义方式
需要定义两个mapper的方法对应的statement
在查询订单的statement中使用association延迟加载[执行]关联查询用户信息
通过订单orders查询的user_id这个外键去查询用户user的信息
具体配置
sqlMapConfing.xml配置
mapper.java
测试
结果分析
当执行到getUser时才进行用户信息查询,并且每次查询一条SQL,重复的SQL进行不查询
一级缓存原理
mybatis提供查询缓存,用于减轻数据的压力,提高数据库的性能
mybatis提供一级缓存和二级缓存
在操作数据库时,需要构造sqlSession对象,在对象中存在一个数据结构(hashMap)用于存储缓存数据,不同的sqlSession之间的缓存数据的区域(hasHmap)是互不影响的
一级缓存是sqlSession级别的缓存
为什么使用缓存
查询从内存中获取,提高效率
第一次:查询id为1的信息,找缓存没有,从数据库进行查询,得到的信息将存储到一级缓存中
执行commit操作,会清空一级缓存,这样是为了让查询数据正确,防止脏数据,让缓存中永远是最新的数据库信息
第二次: 查询id为1的信息,找缓存中有,直接从缓存中取出
一级缓存测试
mybatis默认支持一级缓存,不需要开启
测试
测试思路:同一个用户不commit,会只查询一次,同一个用户查询,commit会查询两次,进行前后对比,一个查询一次,一个查询两次
一级缓存的应用
正式开发,是将mybatis和spring进行整合开发,事物控制是在service中.
一个service方法中包括很多的mapper方法调用
service1(){
//开始执行时,开启事务,创建sqlSession对象
//第一次调用mapper的方法findUserById(1);
//第二次调用mapper的方法findUserById(1);
//方法结束,sqlSession关闭
}
注意:如果是执两次service调查相同用户的用户信息,不走一级缓存,因为service方法结束,sqlSession就关闭,一级缓存就清空
二级缓存原理
原理
二级缓存mapper级别的缓存(nameSpace),多个sqlSession操作同一个同一个Mapper中的SQL语句,得到的数据会缓存在二级缓存区域,二级缓存是跨sqlSession的,多个sqlSession可以共用二级缓存
每一个mapper的namespace就是一个二级缓存区域,不同的mapper使用同一个namespace就会使用同一个二级缓存空间
二级缓存测试
测试思路
开启二级缓存
进行测试
使用同一个namespace创建SQLSession,通过不同的SQLSession进行同一个SQL语句操作,会发现第一次进行了SQL查询,第二次没有进行SQL查询,而是直接从内存中进行读取上一次查询的数据,若在第二次进行相同查询的时候,进行commit操作,就会进行sql语句查询
禁用二级缓存
useCache配置
在statement中设置useCache,可以禁用当前select的二级缓存,即每次发出请求都会发出SQL去查询,默认为true,即使用二级缓存
<select id="findOrdersUserresultMap" resultMap="OrdersUserresultMap" useCache="true">
使用情景
查询时commit频繁的,每次查询都需要最新的,就使用禁用二级缓存
刷新缓存(就是清空缓存)
在mapper的同一个namespace中,如果有其他的insert,update,delete操作数据后需要刷新缓存,如果不刷新会出现脏读数据
设置statement配置中的flushCache=”true”属性,默认情况下为true即刷新缓存,如果改为false则不会刷新缓存
一般执行完commit都要刷新缓存
Mybatis的Cache参数
flushinterval (刷新间隔) 可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。。
size (引用数目) 可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。.
readOnly(只读) 属性可以被设置为true或false.只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false..
如下例子:
这更高级的配置创建了一个FIFO缓存并每隔60秒刷新,存数结果对象或列表的512个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。可用的收回策略有,默认的是LRU:4
1. LRU- 最近最少使用的移除最长时间不被使用的对象。
2. FIFO- 先进先出:按对象进入缓存的顺序来移除它们。
mybatis和ehcache整合
ehcache分布式缓存框架
mybatis无法实现分布缓存,无法进行集群管理,必须进行整合
二级缓存使用场景
对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis 二级缓存技术降低数据库访问量,提高访问速度,业务场景比如: 耗时较高的统计分析sql.电话账单查询sql等
实现方法如下: 通过设置刷新间隔时间,由mybatis 每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔fushinterval,比如设置为30 分钟、60 分钟、24 小时等,根据需求而定
局限性
mybatis 二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求: 对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis 的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits 的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存,即指定改变位置的缓存改变方式
分布式缓存框架
为了提高系统并发性能,一般对系统进行分布式部署(集群部署),因为缓存数据可能十分的庞大,不对缓存数据进行分布式处理,就不会负载均衡,缓存数据就会在各自的服务器单独存储
对缓存数据进行集中管理,可以使用分布式缓存框架,如Redis,memcached, ehcache
与ehcache进行整合
整合方法
mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可
mybatis和ehcache整合,mybatis和ehcache整合包中提供了cache接口的实现类
在CLASSPATH下配置ehcache.xml进行缓存处理具体配置