1 订单商品数据模型
1.1 数据模型分析思路
- 每张表所记录的数据内容
- 每张表数据库字段设置:非空字段、外键字段
- 数据库级别表与表之间的关系:外键关系
- 表与表之间的业务关系:一定要建立在某个业务意义基础之上
1.2 数据模型分析
1.2.1 每张表的内容
- 用户表t_user:
- 记录了购买商品的用户信息
- 订单表t_orders:
- 记录了用户所创建的订单(购买商品的订单)
- 订单明细表t_orderdetail:
- 记录了订单的详细信息(购买商品的信息)
- 商品表t_items:
- 记录了商品信息
1.2.2 表与表之间的业务关系
- 建立在某个业务意义基础上分析
- 先分析数据级别之间有关系的表之间的关系
- t_user和t_orders:
- t_user—>t_orders:一个用户可以创建多个订单,一对多
- t_orders—>t_user:一个订单只由一个用户创建,一对一
- t_orders和t_orderdetail
- t_orders—>t_orderdetail:一个订单可以包括多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail记录,一对多
- t_orderdetail—>t_orders:一个订单明细只能包括在一个订单中,一对一
- t_orderdetail和t_items:
- t_orderdetail—>t_items:一个订单明细只包含一个商品信息,一对一
- t_items—>t_orderdetail:一个商品可以包括在多个订单明细中,一对多
- 再分析数据级别之间没有关系的表之间的是否有业务关系:
- t_orders和t_items:t_orders和t_items之间可以通过订单明细表之间建立关系
- t_orders–>t_items:一对多
- t_items–>t_orders:一对多
- 总之:t_items和t_orders是多对多关系
2 一对一查询
2.1 需求
- 查询订单信息,关联查询创建订单的用户信息
2.2 resultType
2.2.1 sql语句
-
确定查询的主表:订单表
-
确定查询的关联表:用户表
-
关联查询使用内连接还是外链接?由于t_orders表中有一个外键,通过外键关联查询用户表只能查询一条记录,可以使用内连接
SELECT t_orders.* , t_user.username, t_user.sex, t_user.address FROM t_orders ,t_user WHERE t_orders.user_id=t_user.id
2.2.2 创建pojo
-
将上边sql查询的结果映射到pojo,pojo中必须包括所有查询列名
-
原始的order.java不能映射全部字段,需要新创建的pojo
-
创建一个pojo继承包括查询字段较多的po类
//通过此类映射订单和用户查询的结果,让此继承查询字段较多的pojo类 public class OrdersCustomer extends Orders{ //添加用户属性 private String username; private String sex; private String address;
2.2.3 mapper.xml
<mapper namespace="cn.itcast.mybatis.mapper.OrdersMapperCustom">
<!-- 查询订单关联用户 -->
<select id="findUsersOrder" parameterType="" resultType="cn.itcast.mybatis.po.OrdersCustom">
SELECT t_orders.* ,
t_user.username,
t_user.sex,
t_user.address
FROM t_orders ,t_user
WHERE t_orders.user_id=t_user.id
</select>
</mapper>
2.2.4 mapper.java
//查询订单关联查询用户信息
public List<OrdersCustom> findOrdersUser() throws Exception;
2.3 resultMap
2.3.1 sql语句
- 同上
2.3.2 使用resultMap映射的思路
- 使用resultMap将查询结果中的订单映射到Orders对象中,在orders类中添加User属性,将关联查询出来的用户信息映射到orders类中
2.3.3 需要Orders类中添加user属性
2.3.4 mapper.xml
2.3.4.1 定义resultMap
<!-- 订单查询关联用户的resultMap -->
<resultMap type="cn.itcast.mybatis.po.Orders" id="OrdersUserResultMap">
<!-- 配置映射的订单信息 -->
<!-- 指定查询列中的唯一标识,订单信息中的唯一标识,
如果有多格列组成唯一标识,需要配置多个id
column:订单信息唯一标识的列
property:订单信息的唯一标识的列所映射到的orders类中的属性
-->
<id column="id" property="id"/>
<result column="user_id" property="user_id"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 配置映射的关联的用户信息 -->
<!-- 用于映射关联查询单个对象的信息
property:要将关联查询用户信息映射到orders类中的哪个属性
-->
<association property="user" javaType="cn.itcast.mybatis.po.User">
<!-- 关联查询用户的唯一标识
column:用于唯一标识用户信息的列
property:映射到javaType指定类的哪个属性
-->
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</association>
</resultMap>
2.3.4.2 statement
<!-- 使用resultMap查询订单关联用户信息 -->
<select id="findOrdersUserResultMap" resultMap="OrdersUserResultMap">
SELECT t_orders.* ,
t_user.username,
t_user.sex,
t_user.address
FROM t_orders ,t_user
WHERE t_orders.user_id=t_user.id
</select>
2.3.5 mapper.java
//查询订单关联查询用户信息,使用resultMap
public List<Orders> findOrdersUserResultMap() throws Exception;
2.4 resultType和resultMap一对一查询小结
- 实现一对一查询:
- 使用resultType实现较为简单,如果pojo中没有包括查询出的列明,只需增加对应属性即可完成映射。
- 如果没有查询结果的特殊要求,建议使用resultType。
- resultMap:需要单独定义resultMap,如果对查询结果有特殊要求,使用resultMap可以完成将关联信息映射到pojo属性中。
- resultMap可以实现延迟加载,而resultType无法实现
一对多查询
3.1 需求
- 查询订单及订单明细信息
3.2 sql语句
- 确定主查询表:订单表
- 确定关联查询表:点单明细表
- 在一对一查询基础上添加订单明细表关联查询即可
3.3 分析
- 使用resultType将上边的查询结果映射到pojo中,订单信息的pojo就会重复
- 要求:对orders映射不能出现重复记录
- 在orders.java类中添加List orderDetails属性
- 最终会将订单信息映射到orders中,订单所对应的订单明细映射到orders中的orderDetails属性中
- 映射成的orders信息不重复,每个orders中的orderDetails属性存储了该订单所对应的订单明细
3.4 在orders中添加list订单明细属性
3.5 mapper.xml
<select id="findOrdersAndOrderDetailResultMap" resultMap="findOrdersAndOrderDetailResultMap">
SELECT t_orders.* ,
t_user.username,
t_user.sex,
t_user.address,
t_orderdetail.id orderdetail_id,
t_orderdetail.items_id,
t_orderdetail.items_num,
t_orderdetail.orders_id
FROM t_orders ,t_user ,t_orderdetail
WHERE t_orders.user_id=t_user.id
AND t_orderdetail.orders_id=t_orders.id
</select>
3.6 resultMap定义
<!-- 查询订单及订单明细的resultMap
使用继承extends,不用在此配置订单信息和用户信息的映射
-->
<resultMap type="cn.itcast.mybatis.po.Orders" id="findOrdersAndOrderDetailResultMap" extends="OrdersUserResultMap">
<!-- 订单信息 -->
<!-- 用户信息 -->
<!-- 订单明细信息
一个订单对应多条明细,使用collection进行映射
collection:实现对关联查询到的多条记录映射到集合对象中
property:将关联查询到的多条记录映射到的orders哪个属性
ofType:指定映射到list集合属性中pojo的类型
-->
<collection property="orderDetails" ofType="cn.itcast.mybatis.po.Orderdetail">
<!-- id 订单明细的唯一标识
property:要将订单明细的唯一标识映射到orderdetail的哪个属性
-->
<id column="orderdetail_id" property="id"/>
<result column="items_id" property="items_id"/>
<result column="items_num" property="items_num"/>
<result column="orders_id" property="orders_id"/>
</collection>
</resultMap>
3.7 mapper.java
//查询订单(关联用户)及订单明细
public List<Orders> findOrdersAndOrderDetailResultMap() throws Exception;
3.8 小结
- mybatis使用resultMap的collection对关联查询的多条记录映射到一个list集合属性中
- 使用resultType实现;
- 将订单明细映射到orders中的orderdetails中,需要自行处理,使用双重循环遍历,去掉重复记录,将订单明细放在orderdetails中
4 多对多查询
4.1 需求
- 查询用户及用户所购买的商品信息
4.2 分析
-
需要查询的主表:用户表
-
通过订单和订单明细进行关联,所以关联表:order,orderdetail,items
SELECT t_orders.* , t_user.username, t_user.sex, t_user.address, t_orderdetail.id orderdetail_id, t_orderdetail.items_id, t_orderdetail.items_num, t_orderdetail.orders_id, t_itmes.name items_name, t_itmes.price items_price FROM t_orders ,t_user ,t_orderdetail,t_itmes WHERE t_orders.user_id=t_user.id AND t_orderdetail.orders_id=t_orders.id AND t_orderdetail.items_id=t_itmes.id
4.3 映射思路
- 将用户信息映射到user中
- 在user类中添加订单列表属性List orderslist,将用户所创建的订单映射到orderslist中
- 在orders中添加订单明细列表属性List orderdetails,将订单明细映射到orderdetails中
- 在orderdetail中添加items属性,将订单明细所对应的商品映射到items中
4.4 mapper.xml
<!-- 查询用户及购买的商品 -->
<resultMap type="cn.itcast.mybatis.po.User" id="findUserAndItemsResultMap">
<!-- 用户信息 -->
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!-- 订单信息
一个用户对应多个订单
-->
<collection property="ordersList" ofType="cn.itcast.mybatis.po.Orders">
<id column="id" property="id"/>
<result column="user_id" property="user_id"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 订单明细
一个订单包括多个订单明细
-->
<collection property="orderDetails" ofType="cn.itcast.mybatis.po.Orderdetail">
<id column="orderdetail_id" property="id"/>
<result column="items_id" property="items_id"/>
<result column="items_num" property="items_num"/>
<result column="orders_id" property="orders_id"/>
<!-- 商品信息
一个订单明细对应一个商品
-->
<association property="items" javaType="cn.itcast.mybatis.po.Items">
<id column="items_id" property="id"/>
<result column="items_name" property="name"/>
<result column="items_price" property="price"/>
</association>
</collection>
</collection>
</resultMap>
<!-- 查询用户及购买的商品信息,使用resultMap -->
<select id="findUserAndItemsResultMap" resultMap="findUserAndItemsResultMap">
SELECT t_orders.* ,
t_user.username,
t_user.sex,
t_user.address,
t_orderdetail.id orderdetail_id,
t_orderdetail.items_id,
t_orderdetail.items_num,
t_orderdetail.orders_id,
t_itmes.name items_name,
t_itmes.price items_price
FROM t_orders ,t_user ,t_orderdetail,t_itmes
WHERE t_orders.user_id=t_user.id
AND t_orderdetail.orders_id=t_orders.id
AND t_orderdetail.items_id=t_itmes.id
</select>
4.5 mapper.java
//查询用户购买信息
public List<User> findUserAndItemsResultMap() throws Exception;
4.6 多对多查询总结
-
将查询用户所购买的商品信息明细清单,(用户名,用户地址,购买商品名称,购买商品时间,购买数量)
-
针对上边需求,使用resultType将查询到的记录映射到一个扩展的pojo中,很简单的实现明细清单功能
-
查询用户账号,用户名称,购买商品数量,商品明细(鼠标移上显示明细)
-
针对上边需求,使用resultMap将用户购买的商品明细列表映射带user对象中
-
总结:使用resultMap是针对那些对查询结果有特殊要求的功能。
5 总结
5.1 resultType
- 作用:将查询结果按照sql列名pojo属性名一致映射到pojo中
- 场合:常见一些明细记录的展示,比如用户购买商品明细,将关联查询信息全部显示在页面时,此时可直接使用resultType将没意义奥记录映射到pojo中,在前端页面遍历list(list中是pojo)即可
5.2 resultMap
- 使用association和collection完成一对一和一对多高级映射(对结果有特殊映射要求)
5.2.1 association
- 作用:将关联查询信息映射到一个pojo对象中
- 场合:为了方便查询关联信息可以使用association将关联订单信息映射为用户对象的pojo属性中,比如:查询订单及关联用户信息。使用resultType无法将查询结果映射到pojo对象的pojo属性中,根据对结果集查询遍历的需要选择使用resultType还是resultMap。
5.2.2 collection
- 作用:将关联查询信息映射到一个list集合中
- 场合:为了方便查询遍历关联信息可以使用collection将关联信息映射到list集合中,比如:查询用户权限范围模块及模块下的菜单,可使用collection将模块映射到模块list中,将菜单列表映射到模块对象的菜单list属性中,这样的目的也是方便对查询结果集进行遍历查询。如果使用resultType无法将查询结果映射到list集合中。
6 延迟加载
6.1 什么是延迟加载
- resultMap可以实现高级映射(使用association,collection实现一对一及一对多映射),association,collection具备延迟加载功能
- 需求:查询订单并且查询用户关联信息。如果先查询订单信息即可满足要求,但我们需要查询用户信息时再查询用户信息。把对用户信息的按需查询就是延迟加载。
- 延迟加载:先从单表查,需要时再从关联表去查询,大大提高数据库性能。
6.2 使用association实现延迟加载
6.2.1 需求
- 查询订单并且查询用户关联信息
6.2.2 mapper.xml
-
需要定义两个mapper的方法对应的statement
-
只查询订单信息
-
SELECT * FROM t_orders
-
在查询订单的statement中使用association去延迟加载(执行)下边的statement(关联查询用户信息)
<!-- 查询订单,关联查询用户 --> <select id="fingOrdersUserLazyLoading" resultMap="fingOrdersUserLazyLoading"> SELECT * FROM t_orders </select>
-
-
关联查询用户信息
-
通过上边查询到的订单信息中的user_id字段关联查询用户信息
-
使用UserMapper.xml中的findUserById
<select id="findUserById" parameterType="int" resultType="user"> SELECT * FROM t_user WHERE id = #{id} </select>
-
-
-
总结:上边先去执行findOrdersUserLazyLoading,当需要去查询用户时再去执行findUserById,通过resultMap的定义将延迟加载配置起来
6.2.3 延迟加载resultMap
<!-- 延迟加载的resultMap -->
<resultMap type="cn.itcast.mybatis.po.Orders" id="findOrdersUserLazyLoading">
<!-- 订单信息 -->
<id column="id" property="id"/>
<result column="user_id" property="user_id"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 对用户信息进行延迟加载
select:制定延迟加载所要执行的statement的id(是根据user_id查询用户信息的statement)
要是用userMapper.xml中findUserById完成用户信息的查询,根据用户id(user_id),
如果不在本mapper中,要加namespace
column:订单信息中关联用户信息查询的列,是user_id
关联查询的sql语句可以理解为:
SELECT t_orders.*,
(SELECT username FROM t_user WHERE t_orders.user_id=t_user.id) username,
(SELECT sex FROM t_user WHERE t_orders.user_id=t_user.id) sex
FROM t_orders
-->
<association property="user" javaType="cn.itcast.mybatis.po.User"
select="cn.itcast.mybatis.mapper.UserMapper.findUserById" column="user_id">
</association>
</resultMap>
6.2.4 mapper.java
//查询订单关联查询用户,用户信息是延迟加载
public List<Orders> findOrdersUserLazyLoading() throws Exception;
6.2.5 测试
6.2.5.1 测试思路
- 执行上边mapper方法(findOrdersUserLazyLoading),内部去掉用xml中的findOrdersUserLazyLoading只查询orders信息(单表)
- 在程序中去遍历上一步查询出的List,当我们调用Orders的getUser方法时,开始进行延迟加载。
- 延迟加载,去掉用UserMapper.xml中findUserById这个方法获取用户信息。
6.2.5.2 延迟加载配置
-
mybatis中默认没有配置延迟加载,在mybatis核心文件中配置:
<!-- 全局配置参数,需要时再配置 --> <settings> <!-- 打开延迟加载的开关 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 将积极加载改为消极加载即按需加载 --> <setting name="aggressiveLazyLoading" value="false"/> </settings>
6.2.5.3 测试代码
//调用mapper方法
List<Orders> list = ordersMapperCustom.findOrdersUserLazyLoading();
//遍历链表
for (Orders orders : list) {
//执行getUser()去查询用户信息,这里实现按需加载
User user = orders.getUser();
System.out.println(user);
}
6.2.6 不用mybatis中的association,如何实现延迟加载
- 实现方法,定义两个mapper方法:
- 查询订单列表
- 根据用户id查询用户信息
- 实现思路:先去查询第一个mapper方法,获取订单信息列表,在程序中个(service),按需去掉用第二个mapper方法去查询用户信息
- 总之:使用延迟加载方法,先去查询简单的sql(最好单表,也可以关联查询),再去按需加载关联查询的其他信息
7 查询缓存
7.1 什么是查询缓存
- mybatis提供查询缓存,用于减轻数据压力,提高数据库性能
- mybatis提供一级缓存,和二级缓存
- 一级缓存是SqlSession级别的缓存。在操作数据库是需要构造sqlSession对象,在对象中各有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的环迅数据区域(HashMap)是互相不影响的。
- 二级缓存是mapper级别的缓存。多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession共用二级缓存,二级缓存是跨SqlSession的。
- 为什么用缓存?
- 如果缓存中有数据,就不用从数据库获取,大大提高系统性能。
7.2 一级缓存
7.2.1 一级缓存工作原理
- 第一次:发起查询用户id为1的用户信息,先去找缓存中是否有id为1的和用户信息,如果没有,去数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
- 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,为了让缓存中存储的是最新的信息,避免脏读。
- 第二次:发起查询用户id为1的用户信息,先去找缓存中是否有id为1的和用户信息,缓存中有,直接从缓存获取用户信息。
7.2.2 一级缓存测试
- mybatis默认支持一级缓存,不需要在配置文件中配置。
- 按照上边的以及缓存原理步骤测试。
7.2.3 一级缓存应用
- 正式开发,试将mybatis和spring进行整合开发,事务控制在service中,一个service方法中包括很多mapper方法调用。
- 如果执行service方法查询相同用户信息,不走一级缓存,因为session方法结束,sqlSession就关闭,一级缓存就清空。
7.3 二级缓存
7.3.1 原理
- 首先开启mybatis的二级缓存
- sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中
- 如果sqlSession3去执行相同mapper下sql,执行commit,清空failmapper下的二级缓存区域
- sqlSession2去查询用户id为1的用户信息,区缓存中找是否存在数据,如果存在直接从缓存中取出数据
- 二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。UserMapper有一个二级缓存区域(按照namespace分),其他Mapper也有自己的二级缓存区域(按照namespace分)。
- 每一个namespace的mapper有一个二级缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到的数据将存储在同一个二级缓存中。
7.3.2 开启二级缓存
-
mybatis的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。
-
在核心配置文件sqlMapConfig.xml中加入
<setting name="cacheEnabled" value="true">
默认值为true -
在UerMapper中开启二级缓存,UserMapper.xml下的sql执行完成会存储到它的缓存区域(HashMap)。
<!-- 开启本mapper的namespace下的二级缓存 --> <cache/>
7.3.3 调用pojo类实现序列化接口
public class User implements Serializable{
- 为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定存储在硬盘中。
7.3.4 userCache配置
- 在statement中设置userCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql语句去查询,默认情况是true,即该sql使用二级缓存。
- 总结:针对每次查询都需要最新数据的sql,要设置成userCache=false,禁用二级缓存。
7.3.5 刷新缓存(清空缓存)
- 在mapper的同一个namespace中,如果有其他insert、update、delete操作数据后要刷新缓存,如果不执行刷新缓存会出现脏读
- 设置statement配置中的flushCache="true"属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存是如果手动修改数据库表中的查询数据会出现脏读。
- 总结:一般情况下执行完commit操作都需要刷新缓存,flushCache=“true”表示刷新缓存,这样可以避免脏读。
7.3.6 Mybatis Cache参数
- flushInterval(刷新间隔)
7.4 mybatis整合ehcache
- ehcache是一个分布式缓存
7.4.1 分布缓存
- 为了提高系统并发、性能,一般对系统进行分布式部署(集群部署方式)
- 不使用分布式缓存,缓存的数据在各个服务器单独存储。
- mybatis无法实现分布缓存,需要和其他的分布式缓存框架进行整合。
7.4.2 整合方法
- mybatis提供了一个cache接口,吐过要是新自己的缓存逻辑,实现cache接口开发即可
- mybatis和ehcache整合,mybatis和ehcache整合包中提供了一个接口的实现类
7.4.3 加入ehcache整合包
7.4.4 整合ehcache,配置mapper中cache中的type为ehcache接口实现类型
<!-- 开启本mapper的namespace下的二级缓存
type:指定cache接口的实现类的类型,mybatis默认使用PerpetualCache
要和ehcache整合,需要配置type为ehcache实现cache接口的类型
-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
7.4.5 加入ehcache配置文件
-
在classpath下边创建配置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>
7.5 二级缓存应用场景
- 对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。
- 实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。
7.6 二级缓存局限性
- mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。
8 Mybatis和Spring整合
8.1 整合思路
- 需要spring通过单例方式管理SqlSession
- spring和mybatis整合生成代理对象,使用SqlSessionFactory创建sqlSession(spring和mybatis整合自动完成)
- 持久层的mapper、dao都需要由spring进行管理
8.2 整合环境
- 创建一个新的java工程(接近实际开发工程结构)
- jar包:mybatis3.2.7的jar包
- spring3.2.0jar包
- mybatis和spring的整合包:早期ibatis和spring整合室友pring整合,mybatis和spring整合由mybatis提供
- 下面加入MyBatis与Spring整合的全部jar包
8.3 SQLSessionFactory
-
在applicationContext.xml配置SQLSessionFactory
-
SQLSessionFactory在mybatis和spring的整合包下
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "> <!-- 加载配置文件 --> <property-placeholder location="classpath:db.properties" /> <!-- 数据源,使用dbcp --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="maxActive" value="10" /> <property name="maxIdle" value="5" /> </bean> <!-- SQLSessionFactory --> <bean id="SqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 加载mybatis配置文件 --> <property name="configLocation" value="mybatis/SqlMapConfig"> </property> <!-- 数据源 --> <property name="dataSource" ref="dataSource"> </property> </bean> </beans>
8.4 原始dao开发(和spring整合后)
8.4.1 User.xml
<mapper namespace="test">
<!-- 在映射文件中配置很多sql语句 -->
<!-- 通过select执行数据库查询
id:用于表示映射文件中的sql
将sql语句封装到mapperstatement对象中,所以将id成为statement的id
parameterType:指定输入参数类型,这里指定int型
#{}表示一个占位符
#{id}:其中id表示接收输入的参数,参数名称为id,如果输入参数是简单类型,#{}中的参数名可以任意,可以是value或其他名称
resultType:输出结果所映射的java对象类型,select执行resultType表示将单条记录所映射成的java对象
-->
<select id="findUserById" parameterType="int" resultType="cn.itcast.ssm.po.User">
SELECT * FROM t_user WHERE id = #{value}
</select>
</mapper>
-
在SqlMapConfig.xml中加载User.xml
<!-- 加载映射文件 --> <mappers> <mapper resource="sqlmap/User.xml"/>
8.4.2 dao(实现类继承SqlSessionDaoSupport)
public interface UserDao {
//根据id查询用户信息
public User findUserById(int id) throws Exception;
}
-
dao接口实现类中需要注入SQLSessionFactory,通过spring进行注入。
-
这里使用spring的声明配置方式配置dao的bean
-
让UserDaoImpl实现类继承SqlSessionDaoSupport类
public User findUserById(int id) throws Exception { //继承SqlSessionDaoSupport,通过this.getSqlSession()得到sqlSession SqlSession sqlSession = this.getSqlSession(); User user = sqlSession.selectOne("test.findUserById",id); return user; }
8.4.3 配置dao
-
在applicationContext.xml中配置dao
<!-- 原始dao接口 --> <bean id="userDao" class="cn.itcast.ssm.dao.UserDaoImpl"> <property name="sqlSessionFactory" ref="sqlSessionFactory"></property> </bean>
8.4.4 测试程序
public class UserDaoImplTest {
private ApplicationContext applicationContext;
//在setUp这个方法得到spring容器
@Before
public void setUp() throws Exception {
applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml");
}
@Test
public void testFindUserById() throws Exception {
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
//调用userDao的方法
User user = userDao.findUserById(1);
System.out.println(user);
}
}
8.5 Mapper代理开发
8.5.1 mapper.xml和mapper.java
<mapper namespace="cn.itcast.ssm.mapper.UserMapper">
<select id="findUserById" parameterType="int" resultType="user">
SELECT * FROM t_user WHERE id = #{id}
</select>
</mapper>
//
public interface UserMapper {
//根据id查询用户信息
public User findUserById(int id) throws Exception;
}
8.5.2 通过mapperFactoryBean创建动态代理对象
<!-- mapper的配置
MapperFactoryBean:根据mapper接口生成代理对象
-->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!-- 指定mapper接口 -->
<property name="mapperInterface" value="cn.itcast.ssm.mapper.UserMapper"></property>
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
- 此方法问题:针对每个mapper都要去配置
8.5.3 通过MapperScannerConfigurer进行mapper的扫描(建议使用)
<!-- mapper批量扫描,从mapper保重扫描出mapper接口,自动创建道理对象并且在spring容器中注册
遵循一些规范:需要将mapper接口的类名和mapper.xml映射文件名称保持一致,且在一个目录中
自动扫描出来的mapper的bean的id为mapper类名(首字母小写)
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 指定扫描的包名
如果扫描多个包,每个包之间使用半角逗号分隔
-->
<property name="basePackage" value="cn.itcast.ssm.mapper"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
8.5.4 测试代码
public class UserMapperTest {
private ApplicationContext applicationContext;
@Before
public void setUp() throws Exception {
applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml");
}
@Test
public void testFindUserById() throws Exception {
UserMapper userMapper = (UserMapper) applicationContext.getBean("userMapper");
User user = userMapper.findUserById(1);
System.out.println(user);
}
}