一、一对一关联查询
一对一关联关系是指两个表之间的一条记录与另一张表的记录有且仅有一条对应关系。通常通过外键来实现,例如用户表和地址表,每个用户对应一个唯一的地址。
1. 使用 association
标签处理一对一关系
MyBatis 中的 association
标签用于处理一对一关系。association
标签可以指定外键关联的子对象,并将查询结果映射到子对象的属性上。
示例:
假设有两个表 user
和 address
,结构如下:
用户表(user):
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(50),
address_id INT
);
地址表(address):
CREATE TABLE address (
id INT PRIMARY KEY,
street VARCHAR(50),
city VARCHAR(50)
);
Java 实体类:
public class User {
private int id;
private String name;
private Address address; // 一对一关系,用户对应一个地址
// Getters and Setters
}
public class Address {
private int id;
private String street;
private String city;
// Getters and Setters
}
MyBatis 映射文件:
<resultMap id="userResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<association property="address" column="address_id" javaType="Address" select="selectAddressById"/>
</resultMap>
<select id="selectUserById" resultMap="userResultMap">
SELECT id, name, address_id FROM user WHERE id = #{id}
</select>
<select id="selectAddressById" resultType="Address">
SELECT id, street, city FROM address WHERE id = #{id}
</select>
在这个示例中:
userResultMap
定义了User
对象的映射关系,其中association
标签表示与Address
对象的一对一关联。association
标签通过select
属性引用了另一个 SQL 查询(selectAddressById
),该查询会根据address_id
去address
表中查找对应的地址信息。
2. 使用注解方式处理一对一关系
MyBatis 也支持通过注解方式处理一对一关系,但这种方式比较复杂且不如 XML 灵活,因此在实际项目中较少使用。
示例:
public interface UserMapper {
@Select("SELECT id, name, address_id FROM user WHERE id = #{id}")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "name", column = "name"),
@Result(property = "address", column = "address_id",
one = @One(select = "selectAddressById"))
})
User selectUserById(int id);
@Select("SELECT id, street, city FROM address WHERE id = #{id}")
Address selectAddressById(int id);
}
二、一对多关联查询
一对多关系是指一个记录在另一个表中可以有多条对应的记录。例如,一个用户可以有多个订单。
1. 使用 collection
标签处理一对多关系
collection
标签用于处理一对多关系。它将查询结果中的多个记录映射为一个集合属性。
示例:
假设有两个表 user
和 order
,结构如下:
用户表(user):
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(50)
);
订单表(order):
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT,
item VARCHAR(50),
price DECIMAL(10, 2)
);
Java 实体类:
public class User {
private int id;
private String name;
private List<Order> orders; // 一对多关系,用户对应多个订单
// Getters and Setters
}
public class Order {
private int id;
private String item;
private BigDecimal price;
// Getters and Setters
}
MyBatis 映射文件:
<resultMap id="userResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<collection property="orders" ofType="Order" column="id" select="selectOrdersByUserId"/>
</resultMap>
<select id="selectUserById" resultMap="userResultMap">
SELECT id, name FROM user WHERE id = #{id}
</select>
<select id="selectOrdersByUserId" resultType="Order">
SELECT id, user_id, item, price FROM orders WHERE user_id = #{id}
</select>
在这个示例中:
collection
标签用于定义User
对象与Order
对象的一对多关系。ofType
属性指定了集合中对象的类型。collection
标签中的select
属性指定了一个子查询(selectOrdersByUserId
),用于根据user_id
从orders
表中获取所有相关订单。
2. 使用注解方式处理一对多关系
同样地,MyBatis 也支持使用注解方式处理一对多关系。
示例:
public interface UserMapper {
@Select("SELECT id, name FROM user WHERE id = #{id}")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "name", column = "name"),
@Result(property = "orders", column = "id",
many = @Many(select = "selectOrdersByUserId"))
})
User selectUserById(int id);
@Select("SELECT id, user_id, item, price FROM orders WHERE user_id = #{userId}")
List<Order> selectOrdersByUserId(int userId);
}
在这个示例中:
@Many
注解指定了一个方法,该方法用于加载与当前对象相关联的多个子对象。
三、延迟加载与立即加载
MyBatis 支持对一对一、一对多关系的延迟加载(Lazy Loading)和立即加载(Eager Loading)。
- 延迟加载:关联对象的数据仅在真正访问时才会加载,适用于关联数据量大或不常访问的场景。
- 立即加载:在加载主对象时同时加载所有关联对象的数据,适用于关联数据量小或需要同时使用的场景。
在 XML 中配置延迟加载:
<association property="address" column="address_id" javaType="Address" select="selectAddressById" fetchType="lazy"/>
<collection property="orders" ofType="Order" column="id" select="selectOrdersByUserId" fetchType="lazy"/>
fetchType="lazy"
表示延迟加载。fetchType="eager"
表示立即加载。
四、注意事项
-
N+1 查询问题:在使用延迟加载时,如果不小心会导致 N+1 查询问题(即第一次查询出主表数据,然后为每条记录再发起 N 次子查询)。解决方法可以通过调整查询策略或者使用
join
来减少查询次数。 -
映射配置的一致性:在使用
association
和collection
时,确保映射关系配置正确,特别是select
子查询的方法名称和参数类型应与实际情况一致。 -
性能考虑:在处理一对多关系时,如果数据量较大,建议根据业务需求使用分页查询或分批加载数据,避免一次加载过多数据导致性能问题。
五、总结
MyBatis 通过 association
和 collection
标签以及注解方式提供了灵活的机制来处理一对一和一对多的关系查询。无论是通过 XML 还是注解方式,开发者都可以根据实际情况自由选择使用合适的方式来实现复杂的关联查询。
使用 MyBatis 处理关联关系时,开发者需要注意映射配置的正确性,考虑到查询的性能问题,并选择合适的加载策略(延迟加载或立即加载)。通过合理的配置和设计,MyBatis 可以高效地处理复杂的数据库关系查询,满足各种业务需求。