MyBatis之手动映射
在一些简单的场景下,MyBatis 能够自动完成对象和数据库字段之间的映射,这时就不需要手动映射。
手动映射通常在以下情况下需要使用:
- 复杂查询或结果:当查询返回的结果结构与实体类不完全匹配,或者返回的结果需要进行复杂的处理时。
- 多表关联查询:当通过SQL进行多表联合查询时,返回的结果可能包含多个实体类的字段,这时需要手动映射结果到特定的对象。
- 非标准字段名:数据库的字段名与实体类属性名不一致,自动映射工具无法正确处理时。
- 部分字段映射:当查询的结果只需要映射到对象的部分字段时。
在MyBatis的XML文件中,我们可以通过<resultMap>
来进行手动映射。这种方式适合需要映射复杂对象的情况,比如下面的用户和地址信息的场景。下面是如何在MyBatis的XML文件中编写手动映射的示例。
例子:手动映射实体类和SQL查询
1. 表结构
假设有两个表:user
和 address
,它们的关系是一对一(每个用户对应一个地址)。
User表:
CREATE TABLE user (
id BIGINT PRIMARY KEY,
name VARCHAR(50),
email VARCHAR(100)
);
Address表:
CREATE TABLE address (
id BIGINT PRIMARY KEY,
user_id BIGINT,
city VARCHAR(100),
street VARCHAR(100),
FOREIGN KEY (user_id) REFERENCES user(id)
);
2. 实体类
User实体类:
public class User {
private Long id;
private String name;
private String email;
private Address address; // 对应用户的地址
// Getters and Setters
}
Address实体类:
public class Address {
private Long id;
private String city;
private String street;
// Getters and Setters
}
3. 在XML中手动映射
MyBatis XML文件通常与Mapper接口文件对应。假设Mapper接口是UserMapper.java
,XML文件命名为UserMapper.xml
。
XML文件内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<!-- 手动映射User和Address -->
<resultMap id="UserResultMap" type="com.example.model.User">
<!-- 映射User表的字段 -->
<id property="id" column="userId" />
<result property="name" column="name" />
<result property="email" column="email" />
<!-- 嵌套映射Address对象 -->
<association property="address" javaType="com.example.model.Address">
<id property="id" column="addressId" />
<result property="city" column="city" />
<result property="street" column="street" />
</association>
</resultMap>
<!-- 查询用户及其地址 -->
<select id="getUserWithAddress" parameterType="long" resultMap="UserResultMap">
SELECT u.id AS userId, u.name, u.email,
a.id AS addressId, a.city, a.street
FROM user u
LEFT JOIN address a ON u.id = a.user_id
WHERE u.id = #{userId}
</select>
</mapper>
-
<resultMap>
:用来定义如何将查询结果映射到Java对象。id
用于标识主键字段,result
用于映射普通字段。 -
<--column 数据库中的字段名--> <--property 实体类中对应的属性 该关键字可以省略... -->
-
<association>
:用于嵌套映射复杂对象。在本例中,我们将User
中的address
字段映射到Address
对象。 -
<select>
:这是查询语句,parameterType="long"
表示传入的参数类型是Long
,resultMap="UserResultMap"
表示结果映射使用上面定义的UserResultMap
。
4. 详解解析
4.1 XML声明和<mapper>
根元素
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<?xml version="1.0" encoding="UTF-8" ?>
: 声明了XML的版本和编码。<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
: 这是MyBatis的映射文件的DTD声明,用于校验映射文件的合法性。<mapper>
: 根元素,声明了Mapper接口的命名空间,namespace="com.example.mapper.UserMapper"
表示此XML文件对应于UserMapper
接口。MyBatis通过这个命名空间将SQL映射与接口方法进行关联。
4.2 手动映射用户和地址对象
<resultMap id="UserResultMap" type="com.example.model.User">
<!-- 映射User表的字段 -->
<id property="id" column="userId" />
<result property="name" column="name" />
<result property="email" column="email" />
<!-- 嵌套映射Address对象 -->
<association property="address" javaType="com.example.model.Address">
<id property="id" column="addressId" />
<result property="city" column="city" />
<result property="street" column="street" />
</association>
</resultMap>
resultMap
<resultMap>
: 定义如何将查询结果映射到Java对象。在这里,id="UserResultMap"
表示这个结果映射的名称,type="com.example.model.User"
表示这个映射对应的是User
类。<id>
: 映射主键。property="id"
表示User
对象的id
属性,column="userId"
表示数据库中的userId
列。这一步将数据库中的userId
映射到User
类的id
字段。<result>
: 映射普通字段。property="name"
和property="email"
分别映射到数据库中的name
和email
列。
嵌套映射Address
<association>
: 用于将复杂的关联对象进行映射。在本例中,property="address"
表示User
对象中关联的Address
对象,javaType="com.example.model.Address"
表示映射到的Java类型是Address
类。<id>
和<result>
: 同样映射Address
对象的属性。property="id"
表示Address
的id
属性,column="addressId"
表示查询结果中表示addressId
的列。city
和street
属性同理。
4.3 查询用户及其地址的SQL语句
<select id="getUserWithAddress" parameterType="long" resultMap="UserResultMap">
SELECT u.id AS userId, u.name, u.email,
a.id AS addressId, a.city, a.street
FROM user u
LEFT JOIN address a ON u.id = a.user_id
WHERE u.id = #{userId}
</select>
<select>
元素
<select>
: 定义SQL查询语句。id="getUserWithAddress"
表示这个查询的名称,MyBatis会根据这个ID将它与Mapper接口中的方法进行关联。parameterType="long"
表示传入的参数类型是Long
类型,通常是用于传递userId
。resultMap="UserResultMap"
: 指定查询结果的映射规则,使用前面定义的UserResultMap
来将结果集映射到User
对象和关联的Address
对象。
SQL语句
-
SELECT u.id AS userId, u.name, u.email, a.id AS addressId, a.city, a.street
: 这是具体的SQL查询语句,查询用户表user
和地址表address
中的字段,并为字段取别名以便与Java对象的字段匹配。u.id AS userId
: 将user
表的id
作为userId
,用于映射User
类的id
字段。a.id AS addressId
: 将address
表的id
作为addressId
,用于映射Address
类的id
字段。
-
FROM user u LEFT JOIN address a ON u.id = a.user_id
: 从user
表中查询,同时使用LEFT JOIN
与address
表进行关联,条件是u.id = a.user_id
,即用户的id
等于地址表中的user_id
。 -
WHERE u.id = #{userId}
: 查询条件是用户的id
等于传入的userId
。
4.4 整体流程
- 传入参数: 当调用Mapper接口的
getUserWithAddress(Long userId)
方法时,userId
会被传入SQL语句中的WHERE
子句中。 - 执行查询: MyBatis执行
SELECT
语句,查询用户和关联的地址信息。 - 结果映射: 查询结果根据
UserResultMap
中的定义,映射到User
和Address
对象的属性中。 - 返回结果: 映射后的
User
对象(包含嵌套的Address
对象)被返回。