MyBatis数据库类型与实体类型对应关系
# MyBatis数据库类型与实体类型对应关系
# 1. 概述
MyBatis作为一个优秀的ORM框架,提供了灵活的数据库类型与Java类型之间的映射机制。本文将详细介绍MyBatis中数据库类型与实体类型的对应关系,帮助开发者更好地理解和使用类型映射功能。
# 2. MyBatis类型处理系统
# 2.1 类型处理机制
MyBatis通过TypeHandler(类型处理器)实现数据库类型与Java类型之间的双向转换:
- JDBC类型到Java类型的转换:从数据库读取数据时,将数据库类型转换为Java类型
- Java类型到JDBC类型的转换:向数据库写入数据时,将Java类型转换为数据库类型
# 2.2 内置类型处理器
MyBatis内置了多种类型处理器,以支持常见的类型转换需求:
/**
* MyBatis内置的部分类型处理器
*/
public class InbuiltTypeHandlers {
// IntegerTypeHandler:处理Integer和int类型
// LongTypeHandler:处理Long和long类型
// StringTypeHandler:处理String类型
// DateTypeHandler:处理java.util.Date类型
// BooleanTypeHandler:处理Boolean和boolean类型
// BigDecimalTypeHandler:处理java.math.BigDecimal类型
// BlobTypeHandler:处理byte[]和Blob类型
// ClobTypeHandler:处理String和Clob类型
// LocalDateTimeTypeHandler:处理java.time.LocalDateTime类型
// EnumTypeHandler:处理枚举类型
// ...更多内置处理器
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 3. 基本类型映射
# 3.1 数值类型映射
数据库中的数值类型可以映射到Java中的多种类型:
/**
* 用户实体类 - 数值类型映射示例
*/
public class User {
private Long id; // 映射数据库BIGINT类型
private Integer age; // 映射数据库INT类型
// 也可以使用int,Java基本类型
// private int age;
private Long count; // 可以映射数据库INT或BIGINT类型
private Short level; // 映射数据库SMALLINT类型
private Byte status; // 映射数据库TINYINT类型
private BigDecimal salary; // 映射数据库DECIMAL类型
// 也可以使用Double类型,但可能存在精度问题
// private Double salary;
private Float score; // 映射数据库FLOAT类型
// getter和setter方法
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
对应的数据库表结构:
CREATE TABLE user (
id BIGINT PRIMARY KEY,
age INT,
count BIGINT,
level SMALLINT,
status TINYINT,
salary DECIMAL(10,2),
score FLOAT
);
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 3.2 字符串类型映射
数据库中的字符串类型通常映射到Java的String类型:
/**
* 用户详情实体类 - 字符串类型映射示例
*/
public class UserProfile {
private Long userId; // 用户ID
private String username; // 映射数据库VARCHAR类型
private String address; // 映射数据库VARCHAR或TEXT类型
private String remark; // 映射数据库TEXT类型
private String code; // 映射数据库CHAR类型
// getter和setter方法
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
对应的数据库表结构:
CREATE TABLE user_profile (
user_id BIGINT PRIMARY KEY,
username VARCHAR(50),
address VARCHAR(200),
remark TEXT,
code CHAR(10)
);
1
2
3
4
5
6
7
2
3
4
5
6
7
# 3.3 日期时间类型映射
数据库中的日期时间类型可以映射到多种Java类型:
/**
* 订单实体类 - 日期时间类型映射示例
*/
public class Order {
private Long orderId; // 订单ID
private Date createTime; // 映射数据库DATETIME类型,使用java.util.Date
private LocalDateTime updateTime; // 映射数据库DATETIME类型,使用Java 8的LocalDateTime
private LocalDate orderDate; // 映射数据库DATE类型
private Timestamp payTime; // 映射数据库TIMESTAMP类型
private LocalTime deliveryTime; // 映射数据库TIME类型
// getter和setter方法
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
对应的数据库表结构:
CREATE TABLE `order` (
order_id BIGINT PRIMARY KEY,
create_time DATETIME,
update_time DATETIME,
order_date DATE,
pay_time TIMESTAMP,
delivery_time TIME
);
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 3.4 布尔类型映射
数据库中的布尔类型(通常是BIT、TINYINT或BOOLEAN)可以映射到Java的Boolean类型:
/**
* 用户设置实体类 - 布尔类型映射示例
*/
public class UserSettings {
private Long userId; // 用户ID
private Boolean notification; // 映射数据库BIT(1)或TINYINT(1)类型
// 也可以使用boolean基本类型
// private boolean notification;
private Boolean isActive; // 映射数据库BIT(1)或BOOLEAN类型
// getter和setter方法
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
对应的数据库表结构:
CREATE TABLE user_settings (
user_id BIGINT PRIMARY KEY,
notification BIT(1), -- 或TINYINT(1)
is_active BOOLEAN -- MySQL中实际是TINYINT(1)
);
1
2
3
4
5
2
3
4
5
# 3.5 二进制类型映射
数据库中的二进制类型可以映射到Java的字节数组或Blob类型:
/**
* 用户头像实体类 - 二进制类型映射示例
*/
public class UserAvatar {
private Long userId; // 用户ID
private byte[] avatar; // 映射数据库BLOB类型,使用字节数组
private Blob document; // 映射数据库BLOB类型,使用Blob对象
// getter和setter方法
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
对应的数据库表结构:
CREATE TABLE user_avatar (
user_id BIGINT PRIMARY KEY,
avatar BLOB,
document LONGBLOB
);
1
2
3
4
5
2
3
4
5
# 4. 映射配置与注解
MyBatis提供了多种方式来配置类型映射关系:
# 4.1 XML映射文件配置
<!-- UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
<!-- resultMap定义实体映射关系 -->
<resultMap id="userResultMap" type="com.example.entity.User">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<result column="age" property="age" jdbcType="INTEGER"/>
<!-- 指定日期类型映射 -->
<result column="birth_date" property="birthDate" jdbcType="DATE"/>
<!-- 指定大文本类型映射 -->
<result column="description" property="description" jdbcType="CLOB"/>
<!-- 指定二进制类型映射 -->
<result column="avatar" property="avatar" jdbcType="BLOB"/>
</resultMap>
<!-- 使用resultMap进行查询 -->
<select id="getUserById" parameterType="long" resultMap="userResultMap">
SELECT id, name, age, birth_date, description, avatar
FROM user
WHERE id = #{id}
</select>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 4.2 注解配置
/**
* 用户Mapper接口 - 使用注解配置类型映射
*/
public interface UserMapper {
/**
* 根据ID查询用户信息
* @param id 用户ID
* @return 用户实体
*/
@Select("SELECT id, name, age, birth_date, description, avatar FROM user WHERE id = #{id}")
@Results({
@Result(column = "id", property = "id", jdbcType = JdbcType.BIGINT, id = true),
@Result(column = "name", property = "name", jdbcType = JdbcType.VARCHAR),
@Result(column = "age", property = "age", jdbcType = JdbcType.INTEGER),
@Result(column = "birth_date", property = "birthDate", jdbcType = JdbcType.DATE),
@Result(column = "description", property = "description", jdbcType = JdbcType.CLOB),
@Result(column = "avatar", property = "avatar", jdbcType = JdbcType.BLOB)
})
User getUserById(Long id);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 5. 自定义类型处理器
有时内置的类型转换无法满足需求,我们可以创建自定义的TypeHandler:
# 5.1 创建自定义类型处理器
以处理枚举类型为例:
/**
* 用户状态枚举
*/
public enum UserStatus {
ACTIVE(1, "活跃"),
INACTIVE(0, "非活跃"),
LOCKED(-1, "锁定");
private final int code;
private final String desc;
UserStatus(int code, String desc) {
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
/**
* 根据code获取枚举值
*/
public static UserStatus getByCode(int code) {
for (UserStatus status : values()) {
if (status.getCode() == code) {
return status;
}
}
return null;
}
}
/**
* 用户状态枚举类型处理器
* 自定义类型处理器需要实现TypeHandler接口或继承BaseTypeHandler类
*/
public class UserStatusTypeHandler extends BaseTypeHandler<UserStatus> {
/**
* 设置非空参数
* Java类型 -> 数据库类型
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i, UserStatus parameter, JdbcType jdbcType) throws SQLException {
// 存储枚举的code值到数据库
ps.setInt(i, parameter.getCode());
}
/**
* 根据列名获取可为空的结果
* 数据库类型 -> Java类型
*/
@Override
public UserStatus getNullableResult(ResultSet rs, String columnName) throws SQLException {
int code = rs.getInt(columnName);
return rs.wasNull() ? null : UserStatus.getByCode(code);
}
/**
* 根据列索引获取可为空的结果
* 数据库类型 -> Java类型
*/
@Override
public UserStatus getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int code = rs.getInt(columnIndex);
return rs.wasNull() ? null : UserStatus.getByCode(code);
}
/**
* 获取可为空的结果
* 数据库类型 -> Java类型
*/
@Override
public UserStatus getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int code = cs.getInt(columnIndex);
return cs.wasNull() ? null : UserStatus.getByCode(code);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# 5.2 注册自定义类型处理器
在MyBatis配置文件中注册自定义类型处理器:
<!-- mybatis-config.xml -->
<configuration>
<!-- 类型处理器注册 -->
<typeHandlers>
<!-- 注册自定义类型处理器 -->
<typeHandler handler="com.example.typehandler.UserStatusTypeHandler"
javaType="com.example.enums.UserStatus"
jdbcType="INTEGER"/>
</typeHandlers>
</configuration>
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 5.3 使用自定义类型处理器
在实体类中使用自定义类型,然后在映射文件中引用:
/**
* 用户实体类 - 使用自定义类型处理器
*/
public class User {
private Long id;
private String name;
private Integer age;
// 使用自定义枚举类型
private UserStatus status;
// getter和setter方法
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
在XML映射文件中:
<resultMap id="userResultMap" type="com.example.entity.User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<!-- 使用自定义类型处理器处理枚举映射 -->
<result column="status" property="status"
typeHandler="com.example.typehandler.UserStatusTypeHandler"/>
</resultMap>
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
或者在注解中:
@Select("SELECT id, name, age, status FROM user WHERE id = #{id}")
@Results({
@Result(column = "id", property = "id"),
@Result(column = "name", property = "name"),
@Result(column = "age", property = "age"),
@Result(column = "status", property = "status",
typeHandler = UserStatusTypeHandler.class)
})
User getUserById(Long id);
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 6. 常见类型映射问题及解决方案
# 6.1 日期类型处理
/**
* 日期类型处理示例
*/
public class DateTypeDemo {
/**
* 使用Date类型处理日期
*/
public void handleWithDate() {
// 查询条件 - 今天的日期
Date today = new Date();
// 使用日期参数
List<Order> orders = orderMapper.getOrdersByCreateDate(today);
}
/**
* 使用LocalDate处理日期
*/
public void handleWithLocalDate() {
// 查询条件 - 今天的日期
LocalDate today = LocalDate.now();
// 使用Java 8日期参数
List<Order> orders = orderMapper.getOrdersByOrderDate(today);
}
/**
* 使用LocalDateTime处理日期时间
*/
public void handleWithLocalDateTime() {
// 指定时间范围
LocalDateTime startTime = LocalDateTime.of(2023, 1, 1, 0, 0, 0);
LocalDateTime endTime = LocalDateTime.now();
// 使用日期时间范围查询
List<Order> orders = orderMapper.getOrdersByTimeRange(startTime, endTime);
}
}
/**
* 订单Mapper
*/
interface OrderMapper {
/**
* 根据创建日期查询订单
*/
@Select("SELECT * FROM `order` WHERE DATE(create_time) = #{createDate}")
List<Order> getOrdersByCreateDate(Date createDate);
/**
* 使用Java 8的LocalDate查询订单
*/
@Select("SELECT * FROM `order` WHERE order_date = #{orderDate}")
List<Order> getOrdersByOrderDate(LocalDate orderDate);
/**
* 使用日期时间范围查询
*/
@Select("SELECT * FROM `order` WHERE create_time BETWEEN #{startTime} AND #{endTime}")
List<Order> getOrdersByTimeRange(LocalDateTime startTime, LocalDateTime endTime);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 6.2 枚举类型处理
除了使用自定义TypeHandler外,MyBatis还提供了内置的枚举处理器:
/**
* 使用MyBatis内置枚举处理器
*/
public enum PaymentMethod {
ALIPAY, WECHAT, CREDITCARD, CASH;
}
/**
* 支付实体类
*/
public class Payment {
private Long id;
private BigDecimal amount;
// 使用内置枚举处理器
// EnumTypeHandler - 存储枚举的名称(默认)
// EnumOrdinalTypeHandler - 存储枚举的序号
private PaymentMethod method;
// getter和setter方法
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在MyBatis配置文件中设置默认枚举处理方式:
<configuration>
<!-- 设置默认枚举处理器 -->
<settings>
<!-- 默认使用EnumTypeHandler,存储枚举名称 -->
<!-- 如设置为true,则使用EnumOrdinalTypeHandler,存储枚举序号 -->
<setting name="useEnumOrdinalTypeHandler" value="false"/>
</settings>
</configuration>
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 6.3 空值处理
/**
* 空值处理示例
*/
public class NullValueDemo {
/**
* 实体类中的属性处理
*/
public class Product {
private Long id;
// 使用包装类型,可以接收null值
private Integer stock; // 库存可能为null
// 使用基本类型,null值会被转换为0
private int sales; // 销量默认为0
// getter和setter方法
}
/**
* 执行更新操作,处理null值
*/
public void updateProduct() {
Product product = new Product();
product.setId(1L);
// 设置库存为null
product.setStock(null);
// 不设置销量,默认为0
// 更新操作 - 需要特殊处理stock字段的null值
productMapper.updateProduct(product);
}
}
/**
* 产品Mapper
*/
interface ProductMapper {
/**
* 更新产品信息
* 使用if标签处理null值
*/
@Update("<script>" +
"UPDATE product SET " +
"<if test='stock != null'>stock = #{stock}, </if>" +
"sales = #{sales} " +
"WHERE id = #{id}" +
"</script>")
int updateProduct(Product product);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 7. 类型映射最佳实践
# 7.1 选择合适的类型映射
/**
* 类型映射最佳实践示例
*/
public class TypeMappingBestPractice {
/**
* 推荐的类型映射
*/
public class RecommendedEntity {
// 1. 使用包装类型处理可能为null的字段
private Long id;
private Integer age;
// 2. 数值精度敏感数据使用BigDecimal
private BigDecimal amount;
// 3. 日期时间字段优先使用Java 8的日期类型
private LocalDateTime createTime;
private LocalDate birthDate;
// 4. 布尔值使用Boolean
private Boolean isActive;
// 5. 枚举类型使用自定义处理器
private UserStatus status;
}
/**
* 不推荐的类型映射
*/
public class NotRecommendedEntity {
// 使用String存储数字会带来类型转换成本
private String id; // 不推荐,应该使用Long
// 使用float/double存储精确小数会有精度问题
private double amount; // 不推荐,应该使用BigDecimal
// 使用老的日期API会有时区和线程安全问题
private Date createTime; // 不推荐,应该使用LocalDateTime
// 使用int存储布尔值不够直观
private int active; // 不推荐,应该使用Boolean
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 7.2 配置全局类型处理
在MyBatis配置文件中进行全局类型处理器配置:
<!-- mybatis-config.xml -->
<configuration>
<!-- 设置 -->
<settings>
<!-- 全局启用或禁用延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 开启驼峰命名自动映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 设置默认枚举类型处理器 -->
<setting name="defaultEnumTypeHandler" value="org.apache.ibatis.type.EnumTypeHandler"/>
</settings>
<!-- 类型处理器注册 -->
<typeHandlers>
<!-- 注册日期处理器 -->
<typeHandler handler="org.apache.ibatis.type.LocalDateTypeHandler"
javaType="java.time.LocalDate" jdbcType="DATE"/>
<typeHandler handler="org.apache.ibatis.type.LocalDateTimeTypeHandler"
javaType="java.time.LocalDateTime" jdbcType="TIMESTAMP"/>
<!-- 注册自定义处理器 -->
<typeHandler handler="com.example.typehandler.UserStatusTypeHandler"
javaType="com.example.enums.UserStatus" jdbcType="INTEGER"/>
</typeHandlers>
</configuration>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
总结
MyBatis提供了强大而灵活的类型映射系统,允许Java类型与数据库类型之间的双向转换。通过合理使用内置类型处理器和自定义类型处理器,可以实现各种复杂的类型映射需求。
- 实体类型可以与数据库类型不完全一致:MyBatis的类型处理器会自动完成合理的类型转换
- 优先选择合适的类型:如BigDecimal存储精确小数,LocalDateTime存储日期时间等
- 处理特殊类型:通过自定义TypeHandler实现复杂类型的处理
- 全局配置:通过MyBatis配置文件统一配置类型处理策略
编辑此页 (opens new window)
上次更新: 2025/03/16, 20:49:53