mybatis 一对多,多对多等关系xml文件中sql的写法以及缓存、延迟加载的配置写法

高级功能https://blog.youkuaiyun.com/dfsaggsd/article/details/50555583

映射关系

一对一映射

SQL语句:用户订单查询

SELECT

 orders.*,

  USER.username,

 USER.sex,

 USER.address

FROM

 orders,

 USER

WHERE orders.user_id = user.id

resultType方法

1.        定义pojo对象

由于原始的Orders.java不能映射全部字段,所以需要新创建的pojo。如下:

 

2.        在mapper.xml相应的sql语句配置中,输出类型为上边定义的pojo对象。其他照旧。

 

resultMap

1.        思路:

1)        使用resultMap将查询结果中的订单信息映射到Orders对象中。

2)        在orders类中添加User属性,将关联查询出来的用户信息映射到orders对象中的user属性中。

2.        方法:

1)        在主表对应类中添加关联表对应的对象

 

2)        在mapper.xml中定义resultMap

 

3)        Sql语句的配置

 

一对多映射

Sql语句:用户订单及订单明细

SELECT

 orders.*,

 USER.username,

 USER.sex,

 USER.address,

  orderdetail.idorderdetail_id,

 orderdetail.items_id,

 orderdetail.items_num,

 orderdetail.orders_id

FROM

 orders,

 USER,

 orderdetail

WHERE orders.user_id = user.id ANDorderdetail.orders_id=orders.id

resultMap方法

1.        主表对应实体类中添加属性:在orders.java类中添加List<orderDetail> orderDetails属性。最终会将订单信息映射到orders中,订单所对应的订单明细映射到orders中的orderDetails属性中

 

2.        定义resultMap

 

多对多映射

sql语句:由于用户和商品没有直接关联,通过订单和订单明细进行关联,所以关联表:

orders、orderdetail、items

SELECT

 orders.*,

 USER.username,

 USER.sex,

 USER.address,

 orderdetail.id orderdetail_id,

 orderdetail.items_id,

 orderdetail.items_num,

 orderdetail.orders_id,

 items.name items_name,

 items.detail items_detail,

 items.price items_price

FROM

 orders,

 USER,

 orderdetail,

 items

WHERE orders.user_id = user.id ANDorderdetail.orders_id=orders.id AND orderdetail.items_id = items.id

resultMap方法

1.        思路:将用户信息映射到user中。

1)        在user类中添加订单列表属性List<Orders> orderslist,将用户创建的订单映射到orderslist

2)        在Orders中添加订单明细列表属性List<OrderDetail>orderdetials,将订单的明细映射到orderdetials

3)        在OrderDetail中添加Items属性,将订单明细所对应的商品映射到Items

2.        定义resultMap

 

resultType与resultMap小结

 

 

resultType

resultMap(常用)

association

collection

作用

将查询结果按照sql列名与pojo中属性名,以一致性关系进行映射

将关联查询信息映射到一个pojo对象中

将关联查询信息映射到一个list集合中

场合

常见一些明细记录的展示,比如用户购买商品明细,将关联查询信息全部展示在页面时,此时可直接使用resultType将每一条记录映射到pojo中,在前端页面遍历list(list中是pojo)即可

为了方便查询关联信息可以使用association将关联订单信息映射为用户对象的pojo属性中,比如:查询订单及关联用户信息。

 

为了方便查询遍历关联信息可以使用collection将关联信息映射到list集合中

延迟加载

不可

可以

延迟加载

1.        延迟加载:先从单表查询、需要时再从关联表去关联查询,大大提高 数据库性能,因为查询单表要比关联查询多张表速度要快。

使用association实现延迟加载(同Collection)

1.        Sql语句:

Select order.*,

         (select username from user where orders.user_id = user.id)username

(select  sex from user whereorders.user_id = user.id)sex

from orders

2.        目的:

1)        首先加载orders表中的满足orders.user_id = user.id条件的user_id。

2)        然后在需要时加载User表中orders.user_id = user.id项中的username和sex。

3.        方法:

1)        在SqlMapConfig.xml开启延迟加载

 

2)        将上面的sql语句拆分成两个statement,即:

SELECT * FROMorders

 

3)        关联查询用户信息,通过上边查询到的订单信息中user_id去关联查询用户信息。

 

4)        将这两个statement通过resultMap关联起来:上边先去执行findOrdersUserLazyLoading,当需要去查询用户的时候再去执行findUserById。配置延迟加载的resultMap。

 

5)        小结:关系图如下

           

查询缓存

Mybatis缓存模型

 

1.        一级缓存是SqlSession级别的缓存。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的,默认自动开启。

2.        二级缓存是mapper的命名空间级别的缓存,一个命名空间对应一个二级缓存。多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的

一级缓存

原理

 

1.        第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。

2.        第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。

3.        如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

 

二级缓存

原理

 

使用方法

1.        在Mybatis的全局配置文件中(SqlMapconfig.xml文件)开启开启二级缓存。

 

2.        在mapper.xml映射文件中开启二级缓存

 

3.        对需要缓存的pojo实体类对象实现序列化,这是因为二级缓存数据存储介质多种多样,为了将缓存数据取出执行反序列化操作。

 

二级缓存常用标签设置

1.        useCache配置:在statement中设置useCache=false可以禁用当前select语句的二级缓存。针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。

<selectid="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

2.        刷新缓存(清空缓存):设置statement配置中的flushCache="true" 属性,默认情况下为true即刷新缓存。一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。(一般无需特别设置)

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

Mybatis整合ehcache

分布式缓存

1.        优点:缓存的数据在各各服务单独存储,不方便系统开发,使用分布式缓存对缓存数据进行集中管理。

 

整合方法

mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合。

1.        实质:mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。

 

mybatis和ehcache整合,mybatis和ehcache整合包中提供了一个cache接口的实现类。

 

2.        与ehcache整合步骤

1)        导入ehcache包

 

2)        加入ehcache的配置文件:在classpath下配置ehcache.xml

 

3)        配置mapper中cache中的type为ehcache对cache接口的实现类型

 

Spring和Mybatis整合

整合的内容

1.        需要spring通过单例方式管理SqlSessionFactory。

2.        spring和mybatis整合生成代理对象,使用SqlSessionFactory创建SqlSession。(spring和mybatis整合自动完成)。

3.        持久层的mapper都需要由spring进行管理。

整合方法

1.        创建整合环境

 

2.        在spring中配置SQLSessionFactory:(applicationConfig.xml)

 

3.        spring管理mapper的代理对象,即将mapper的代理对象注入的spring容器中

 

1)        遵循规范:将mapper.java和mapper.xml映射文件名称保持一致,且在一个目录中。

2)        自动扫描出来的bean 的id为mapper类名,首字母小写。

3)        Mybatis扫描包与Spring扫描包的关系

<think>我们正在讨论MyBatis中的对一关联映射(比如个订单属于同一个用户)。在MyBatis中,对一映射通常使用`<association>`标签来实现。根据引用内容,实现方式主要有两种:嵌套结果(联查询)和嵌套查询(分次查询)。此外,还可以使用注解方式。下面我将详细解释这两种XML配置方式,并给出代码示例。 ### 1. **嵌套结果(Nested Results)** 通过单次SQL查询获取所有数据,然后在resultMap中配置映射关系。 **示例代码:** ```xml <!-- 定义resultMap --> <resultMap id="orderResultMap" type="Order"> <!-- 订单自身字段 --> <id property="id" column="id"/> <result property="orderNo" column="order_no"/> <!-- 对一关联用户:使用association --> <association property="user" javaType="User"> <id property="id" column="user_id"/> <result property="name" column="user_name"/> </association> </resultMap> <!-- 联查询SQL --> <select id="selectOrderWithUser" resultMap="orderResultMap"> SELECT o.id, o.order_no, u.id AS user_id, <!-- 用户字段别名避免冲突 --> u.name AS user_name FROM orders o LEFT JOIN user u ON o.user_id = u.id WHERE o.id = #{id} </select> ``` **特点:** - 一次SQL查询获取所有数据 - 使用连接(JOIN)关联 - 需要为关联的字段设置别名以避免列名冲突 ### 2. **嵌套查询(Nested Select)** 分两次查询:先查主,再根据关联字段查关联。 **示例代码:** ```xml <!-- 定义两个resultMap --> <resultMap id="orderResultMap" type="Order"> <id property="id" column="id"/> <result property="orderNo" column="order_no"/> <!-- 通过select属性指定嵌套查询语句 --> <association property="user" column="user_id" javaType="User" select="selectUser"/> </resultMap> <!-- 查询订单的SQL --> <select id="selectOrder" resultMap="orderResultMap"> SELECT id, order_no, user_id FROM orders WHERE id = #{id} </select> <!-- 查询用户的SQL --> <select id="selectUser" resultType="User"> SELECT id, name FROM user WHERE id = #{userId} <!-- 参数来自主查询的user_id列 --> </select> ``` **特点:** - 需要执行SQL语句 - 支持延迟加载(按需查询关联数据)[^4] - 配置`<association>`时需指定`select`和`column`属性 ### 3. **注解方式实现** 使用MyBatis注解配置对一映射。 **示例代码:** ```java public interface OrderMapper { @Select("SELECT id, order_no, user_id FROM orders WHERE id = #{id}") @Results({ @Result(property = "id", column = "id"), @Result(property = "orderNo", column = "order_no"), @Result(property = "user", column = "user_id", one = @One(select = "com.example.mapper.UserMapper.selectById")) }) Order selectOrderWithUser(Long id); } // 在UserMapper中定义 public interface UserMapper { @Select("SELECT id, name FROM user WHERE id = #{userId}") User selectById(Long userId); } ``` **特点:** - 代码简洁,无需XML文件 - 使用`@One`注解指定嵌套查询 - 同样支持延迟加载(需全局配置`lazyLoadingEnabled=true`) ### 对比总结 | 实现方式 | 嵌套结果 | 嵌套查询 | 注解方式 | |----------------|--------------------------|--------------------------|---------------------| | **SQL次数** | 1次(联) | N+1次(主查询+关联查询) | N+1次 | | **性能** | 数据量大时可能降低性能 | 支持延迟加载优化 | 同嵌套查询 | | **配置复杂度** | 需处理字段别名 | 需定义个查询方法 | 接口注解配置 | | **适用场景** | 关联数据量小时 | 关联数据量大或需延迟加载 | 简单业务场景 | > **选择建议**: > - 需要减少数据库查询次数 → 选择**嵌套结果** > - 需要按需加载关联数据 → 选择**嵌套查询**并启用延迟加载[^4] > - 追求开发效率 → 选择**注解方式**(简单场景) ### 延迟加载配置 在嵌套查询/注解方式中启用延迟加载需在MyBatis全局配置中添加: ```xml <settings> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> </settings> ``` 这样配置后,只有当代码访问`order.getUser()`时才会触发查询用户信息的SQL[^4]。 §§相关问题§§ 1. MyBatis嵌套查询中如何避免N+1查询问题? 2. 对一映射使用嵌套结果时如何处理字段名冲突? 3. MyBatis延迟加载的原理是什么?如何配置不同层级的加载策略?</think>### MyBatis 对一映射实现方式详解 在 MyBatis 中实现对一关联(如个订单属于同一个用户)主要有三种方式,每种方式都有其适用场景: #### 1. **嵌套结果映射(联查询)** **原理**:通过单次 SQL JOIN 查询获取所有数据,在 resultMap 中配置映射关系 **适用场景**:关联数据量不大,需要一次性获取全部数据 **示例代码**: ```xml <!-- 定义resultMap --> <resultMap id="orderResultMap" type="Order"> <id property="id" column="order_id"/> <result property="orderNo" column="order_no"/> <!-- 对一关联用户 --> <association property="user" javaType="User"> <id property="id" column="user_id"/> <result property="name" column="user_name"/> </association> </resultMap> <!-- 联查询SQL --> <select id="selectOrderWithUser" resultMap="orderResultMap"> SELECT o.id AS order_id, o.order_no, u.id AS user_id, u.name AS user_name FROM orders o JOIN user u ON o.user_id = u.id WHERE o.id = #{id} </select> ``` #### 2. **嵌套查询(分次查询)** **原理**:先查询主对象,再根据关联字段查询关联对象 **适用场景**:需要延迟加载关联数据,大数据量时减少内存占用[^4] **示例代码**: ```xml <!-- 订单结果映射 --> <resultMap id="orderResultMap" type="Order"> <id property="id" column="id"/> <result property="orderNo" column="order_no"/> <!-- 嵌套查询关联用户 --> <association property="user" column="user_id" javaType="User" select="selectUserById"/> <!-- 指向查询用户的SQL --> </resultMap> <!-- 查询订单的SQL --> <select id="selectOrder" resultMap="orderResultMap"> SELECT id, order_no, user_id FROM orders WHERE id = #{id} </select> <!-- 查询用户的SQL --> <select id="selectUserById" resultType="User"> SELECT id, name FROM user WHERE id = #{userId} </select> ``` #### 3. **注解方式实现** **原理**:使用 MyBatis 注解直接在接口方法上配置映射 **适用场景**:简单关联关系,避免 XML 配置 **示例代码**: ```java public interface OrderMapper { @Select("SELECT id AS order_id, order_no, user_id FROM orders WHERE id = #{id}") @Results({ @Result(property = "id", column = "order_id", id = true), @Result(property = "orderNo", column = "order_no"), @Result( property = "user", column = "user_id", javaType = User.class, one = @One(select = "com.example.mapper.UserMapper.selectById") ) }) Order selectOrderWithUser(Long id); } // 在UserMapper中 public interface UserMapper { @Select("SELECT id, name FROM user WHERE id = #{userId}") User selectById(Long userId); } ``` ### 三种方式对比 | 特性 | 嵌套结果映射 | 嵌套查询 | 注解方式 | |---------------------|----------------------------|-----------------------------|-------------------------| | **SQL次数** | 1次(JOIN查询) | N+1次(主查询+关联查询) | N+1次 | | **性能** | 数据量大时可能慢 | 支持延迟加载[^4] | 同嵌套查询 | | **配置复杂度** | 需处理字段别名 | 需定义个查询 | 接口注解配置 | | **适用场景** | 关联数据量小 | 大数据量/需要延迟加载 | 简单关联关系 | | **缓存机制** | 一级缓存有效 | 嵌套查询可单独缓存 | 同嵌套查询 | ### 延迟加载配置 在嵌套查询/注解方式中启用延迟加载需在 MyBatis 配置中添加: ```xml <settings> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> </settings> ``` 这样配置后,只有当访问 `order.getUser()` 时才会触发查询用户信息的 SQL[^4]。 > **最佳实践建议**: > - 小数据量关联 → 选择**嵌套结果映射**(效率更高) > - 大数据量关联 → 选择**嵌套查询+延迟加载**(节省内存) > - 简单业务场景 → 选择**注解方式**(配置简洁)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值