摘要:MyBatis 实现一对一关系主要有两种方式,这两种方式都依赖于 标签在 resultMap 中的配置。 核心区别在于如何获取关联对象的数据:
MyBatis 实现一对一关系主要有两种方式,这两种方式都依赖于 标签在 resultMap 中的配置。 核心区别在于如何获取关联对象的数据:
1. 嵌套查询 (Nested Select/Lazy Loading):
-- 用户表 (主表)CREATETABLE users ( id INTPRIMARY KEY AUTO_INCREMENT, username VARCHAR(50), -- ... 其他用户字段 address_id INTUNIQUE-- 外键关联地址表 ); -- 地址表 (关联表)CREATETABLE addresses ( id INTPRIMARY KEY AUTO_INCREMENT, street VARCHAR(100), city VARCHAR(50), -- ... 其他地址字段 user_id INTUNIQUE-- 反向外键 (可选,根据关系方向决定是否需要) ); -- 假设 users.address_id 关联 addresses.idb) java 实体类:publicclassUser { private Integer id; private String username; // ... 其他用户字段private Address address; // 一对一关联 Address 对象// Getters and Setters// ... } publicclassAddress { private Integer id; private String street; private String city; // ... 其他地址字段// 可以选择是否需要 User 属性,取决于关系方向和需求// private User user;// ... }c) Mapper XML 配置: select="com.example.mapper.AddressMapper.getAddressById"/> SELECT u.id as user_id, u.username, u.address_id FROM users u WHERE u.id = #{id} SELECT a.id as address_id, a.street, a.city FROM addresses a WHERE a.id = #{id} d) 关键配置解释:: 定义 User 实体类的 address 属性是一对一关联。javaType="com.example.model.Address": 指定关联属性的 Java 类型。column="address_id": 指定从 users 表查询结果中,哪个列的值 (这里是 address_id) 将作为参数传递给 select 属性指定的查询。select="com.example.mapper.AddressMapper.getAddressById": 指定用于查询关联 Address 对象的 Mapper 接口和方法。MyBatis 会根据 column 传递的 address_id 值,调用 AddressMapper.getAddressById 方法执行查询。2. 连接查询 (Join Query/Eager Loading):
SELECT u.id as user_id, u.username, a.id as address_id, a.street as address_street, a.city as address_city FROM users u LEFT JOIN addresses a ON u.address_id = a.id WHERE u.id = #{id} : 定义 User 实体类的 address 属性是一对一关联,并直接引用 AddressResultMap。resultMap="AddressResultMap": 指定使用 AddressResultMap 来映射查询结果中的地址相关列到 Address 对象。columnPrefix="address_" (在 AddressResultMap 中,可选但推荐): 如果查询结果中,地址相关的列名都带有相同的前缀 (例如 address_id, address_street 等),可以使用 columnPrefix 属性来简化 AddressResultMap 的配置。 例如,如果设置 columnPrefix="address_", AddressResultMap 中的 会自动映射到查询结果中的 address_id 列, 会自动映射到 address_street 列,以此类推。 但更推荐在 SQL 中使用别名,更清晰易懂。SQL JOIN 语句: 在 getUserWithAddressById 查询中,使用了 LEFT JOIN (或其他合适的 JOIN 类型) 将 users 表和 addresses 表连接起来,一次性查询出所有需要的数据。列别名 (重要): 由于连接查询会返回多个表的列,为了避免列名冲突,并且方便 resultMap 映射,强烈建议在 SQL 查询中使用列别名,例如 a.id as address_id, a.street as address_street 等。选择哪种方式?
性能考虑:嵌套查询 (Lazy Loading): 初始查询速度快,只查询主表数据。但如果后续需要访问关联对象,会触发额外的数据库查询,可能导致 “N+1 查询问题” (如果批量查询用户,每个用户都需要单独查询地址)。连接查询 (Eager Loading): 一次性查询所有数据,减少数据库查询次数,性能通常更好,尤其是在经常需要访问关联对象的情况下。但初始查询可能较慢,返回的数据量也可能更大。数据访问模式:如果应用程序中,大部分情况下只需要用户信息,而很少需要地址信息,那么嵌套查询可能更合适。如果应用程序中,用户和地址信息经常一起使用,那么连接查询可能更合适。复杂性:嵌套查询: 配置相对简单,SQL 语句也比较简单。连接查询: SQL 语句可能更复杂 (需要 JOIN 和别名),resultMap 配置也可能稍微复杂一些 (需要处理列别名或 columnPrefix)。特性嵌套查询 (Nested Select)连接查询 (Join Query)加载方式延迟加载 (Lazy Loading)立即加载 (Eager Loading)查询次数1 + N (可能)1初始查询速度快慢整体性能可能较差 (N+1 问题)通常更好配置复杂度简单稍复杂SQL 复杂度简单稍复杂适用场景关联对象非必需, 初始性能敏感关联对象常用, 整体性能敏感在实际项目中,你需要根据具体的业务需求、数据访问模式和性能要求来选择合适的一对一关联实现方式。 通常来说,连接查询 (Join Query) 在大多数情况下是更推荐的选择,因为它能避免 N+1 查询问题,提供更好的整体性能。 但如果你的场景确实非常注重初始查询速度,并且关联对象很少被访问,那么嵌套查询也是一个可行的选择。
来源:做个明媚的女子