@Results
注解详解
在 MyBatis 中,@Results
注解用于将数据库的字段和 Java 实体类的属性进行映射,特别是在 字段名与属性名不一致 的情况下。
MyBatis 默认会使用 自动映射,但如果数据库字段使用 snake_case
(下划线命名法),而 Java 类使用 camelCase
(驼峰命名法),就可能需要手动指定映射关系。这时可以使用 @Results
注解。(或者也可以在配置文件中设置<setting name="mapUnderscoreToCamelCase" value="true"/>
)
1. @Results
的基本用法
示例:基本映射
假设 t_user
表的字段如下:
id | user_name | user_email |
---|---|---|
1 | Alice | alice@example.com |
2 | Bob | bob@example.com |
对应的 User
实体类:
public class User {
private Integer id;
private String userName; // 和数据库的 user_name 字段不匹配
private String email; // 和数据库的 user_email 不匹配
// getter & setter 略
}
UserMapper
接口:
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface UserMapper {
@Select("SELECT id, user_name, user_email FROM t_user")
@Results({
@Result(property = "userName", column = "user_name"),
@Result(property = "email", column = "user_email")
})
List<User> selectUsers();
}
解释
@Results({ ... })
:定义字段与属性的映射@Result(property = "userName", column = "user_name")
:将数据库的user_name
字段映射到userName
属性@Result(property = "email", column = "user_email")
:将数据库的user_email
字段映射到email
属性
这样,查询结果中的 user_name
和 user_email
就能正确映射到 User
类的 userName
和 email
变量。
2. @Result
的常用参数
@Result(
property = "实体类属性名", // Java 实体类中的属性
column = "数据库字段名", // 数据库表的字段名
id = true/false, // 是否为主键
javaType = Java类型.class, // 指定 Java 类型
jdbcType = JdbcType.VARCHAR, // 指定 JDBC 类型
typeHandler = 自定义TypeHandler.class, // 自定义类型转换器
one = @One(select = "方法路径"), // 一对一映射,和 <association> 类似
many = @Many(select = "方法路径") // 一对多映射,和 <collection> 类似
)
3. id = true
(标识主键)
如果某个字段是 主键,需要使用 id = true
来标识:
@Results({
@Result(property = "id", column = "id", id = true),
@Result(property = "userName", column = "user_name"),
@Result(property = "email", column = "user_email")
})
4. @Result
指定 javaType
和 jdbcType
如果数据库的字段类型和 Java 类型不匹配,可以在@Result
注解中显式指定 javaType
和 jdbcType
:
例子:
假设我们有一个数据库表 tab_user
,其字段类型与 Java 类型不完全匹配,且需要明确指定转换规则。比如,tab_user
表中的 birth_date
字段是 VARCHAR
类(存储日期字符串),但想将其映射为 Date
类。
数据库表 tab_user
:
id | username | age | birth_date |
---|---|---|---|
1 | Alice | 25 | 1996-05-15 |
2 | Bob | 30 | 1991-08-22 |
注意:MyBatis 会使用内置的类型处理器,自动将符合yyyy-MM-dd格式的
VARCHAR
日期字符串转换为Date
类型,所以这里我们不用自己写,只需指定 javaType 和 jdbcType。
对应的 Java 实体类 User
:
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private Integer age;
private Date birthDate; // 对应 VARCHAR 类型(日期字符串)
}
UserMapper
接口:
package org.example.mb_study.mapper;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.type.JdbcType;
import org.example.mb_study.entity.User;
import java.util.Date;
import java.util.List;
public interface UserMapper {
@Select("SELECT id, username, age, birth_date FROM tab_user")
@Results({
@Result(property = "birthDate", column = "birth_date", javaType = Date.class, jdbcType = JdbcType.VARCHAR)
})
List<User> selectUsers();
}
解释:
@Result(property = "birthDate", column = "birth_date", javaType = Date.class, jdbcType = JdbcType.VARCHAR)
:- 数据库中的
birth_date
字段类型是VARCHAR
(字符串),但是我们希望将它映射为 Java 中的Date
类型。为了确保正确转换,我们需要指定javaType
为Date.class
和jdbcType
为JdbcType.VARCHAR
。
- 数据库中的
为什么需要显式指定 javaType
和 jdbcType
?
jdbcType
:当数据库中字段的类型和 Java 类型不一致时,比如,数据库中的日期字段可能是VARCHAR
类型存储日期字符串,而不是DATE
或TIMESTAMP
,此时需要显式指定jdbcType = JdbcType.VARCHAR
,以避免自动映射时产生错误。javaType
:在一些情况下,MyBatis 会尝试自动推断 Java 类型,但有时这种推断并不准确,尤其是当数据库列的类型无法直接映射到 Java 类型时。显式指定javaType
可以确保正确转换数据类型。
5. @Result
+ @One
(一对一映射)
如果一个 User
关联了 Address
,可以用 @One
进行查询:
表结构
id | username | address_id |
---|---|---|
1 | Alice | 101 |
2 | Bob | 102 |
id | street |
---|---|
101 | 123 Main St |
102 | 456 Oak Ave |
实体类
public class Address {
private Integer id;
private String street;
// getter & setter
}
public class User {
private Integer id;
private String userName;
private Address address; // 一对一映射
// getter & setter
}
查询 User
时,自动查询 Address
@Select("SELECT id, username, address_id FROM t_user")
@Results({
@Result(property = "address", column = "address_id",
one = @One(select = "org.example.mapper.AddressMapper.selectById"))
})
List<User> selectUsers();
@One(select = "org.example.mapper.AddressMapper.selectById")
:address_id
通过AddressMapper.selectById
方法查找Address
信息- 相当于
select * from t_address where id = #{address_id}
6. @Result
+ @Many
(一对多映射)
如果 User
关联多个 Order
,可以用 @Many
进行查询:
表结构
user_id | order_id | order_name |
---|---|---|
1 | 1001 | Phone |
1 | 1002 | Laptop |
2 | 1003 | Keyboard |
实体类
public class Order {
private Integer id;
private String orderName;
}
public class User {
private Integer id;
private String userName;
private List<Order> orders; // 一对多映射:一个用户对应多个订单
}
查询 User
时,自动查询 Order
@Select("SELECT id, username FROM t_user")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "userName", column = "username"),
@Result(property = "orders", column = "id",
many = @Many(select = "org.example.mapper.OrderMapper.selectByUserId"))
})
List<User> selectUsers();
@Many(select = "org.example.mapper.OrderMapper.selectByUserId")
:orders
通过OrderMapper.selectByUserId
查找订单列表- 相当于
select * from t_order where user_id = #{id}
总结
功能 | 关键用法 |
---|---|
字段映射 | @Result(property, column) |
标识主键 | id = true |
指定 Java 类型 | javaType = Integer.class |
指定 JDBC 类型 | jdbcType = JdbcType.INTEGER |
一对一查询 | one = @One(select = "...") |
一对多查询 | many = @Many(select = "...") |
@Results
在 字段名与属性名不一致 或 查询需要复杂映射 时非常有用。如果查询结构复杂,建议使用 resultMap
进行映射。