MySQL的数据类型是构建表结构的基础,不同类型直接影响存储效率、查询性能和数据处理方式。以下是详细分类和使用建议,覆盖高频场景和避坑指南:
一、数值类型(存储数字)
1. 整数类型
| 类型 | 存储空间 | 范围(有符号/无符号) | 适用场景 |
|---|---|---|---|
TINYINT | 1字节 | -128 ~ 127 / 0 ~ 255 | 布尔值(0/1)、状态码(如1-5) |
SMALLINT | 2字节 | -32,768 ~ 32,767 / 0 ~ 65,535 | 小范围数值(如年龄) |
MEDIUMINT | 3字节 | -8,388,608 ~ 8,388,607 / 0 ~ 16M+ | 中等范围数值 |
INT | 4字节 | ±21亿 / 0 ~ 42亿+ | 常规ID、数量统计 |
BIGINT | 8字节 | 极大范围(约±9×10¹⁸) | 分布式ID(雪花算法生成) |
注意点:
- 优先用
INT(性价比高),除非明确需要更大范围(如BIGINT存时间戳)。 - 无符号(
UNSIGNED)可使正数范围翻倍,但字段不能存负数(如年龄用TINYINT UNSIGNED)。
2. 浮点类型
| 类型 | 存储空间 | 精度 | 适用场景 |
|---|---|---|---|
FLOAT | 4字节 | 单精度(约7位小数) | 非精确计算(如经纬度) |
DOUBLE | 8字节 | 双精度(约15位小数) | 科学计算 |
DECIMAL(M,D) | 可变 | 精确小数(如DECIMAL(10,2)存金额) | 财务计算(避免精度丢失) |
避坑指南:
- 不要用
FLOAT/DOUBLE存金额(如FLOAT存0.1+0.2可能得到0.30000000000000004),必须用DECIMAL。 DECIMAL的M是总位数,D是小数位数(如DECIMAL(5,2)最大存999.99)。
二、字符串类型(存储文本)
1. 定长与变长字符串
| 类型 | 存储空间 | 最大长度 | 适用场景 |
|---|---|---|---|
CHAR(N) | 固定N个字符 | N ≤ 255 | 长度固定的字符串(如手机号、MD5) |
VARCHAR(N) | 实际长度+1/2字节 | N ≤ 65,535 | 长度可变的字符串(如姓名、标题) |
性能对比:
CHAR适合短且长度固定的数据(如性别'男'/'女'),读写更快(无需计算长度)。VARCHAR节省空间(如VARCHAR(100)存5个字符只占7字节),但频繁更新可能产生碎片。
2. 大文本类型
| 类型 | 最大长度 | 适用场景 |
|---|---|---|
TEXT | 65,535字节 | 短文章、配置信息 |
MEDIUMTEXT | 16MB | 长文章、博客内容 |
LONGTEXT | 4GB | 超大文本(如富文本编辑器内容) |
注意点:
- 避免在
TEXT字段上建索引(性能差),如需检索可考虑FULLTEXT索引。 TEXT不能有默认值(但VARCHAR可以)。
3. 特殊字符串
-
ENUM(枚举):
存储固定值列表(如性别ENUM('男','女','未知')),用数字(1,2,3)内部存储,节省空间。
缺点:修改枚举值需ALTER TABLE,不灵活。 -
SET(集合):
存储多选值(如爱好SET('读书','运动','音乐')),每个选项占1位(最多64个选项)。
示例:INSERT INTO t VALUES ('读书,音乐');
三、日期与时间类型
| 类型 | 存储空间 | 范围 | 精度 | 适用场景 |
|---|---|---|---|---|
DATE | 3字节 | ‘1000-01-01’ ~ ‘9999-12-31’ | 年月日 | 生日、日期(无时间) |
TIME | 3字节 | ‘-838:59:59’ ~ ‘838:59:59’ | 时分秒 | 时间段(如电影时长) |
DATETIME | 8字节 | ‘1000-01-01 00:00:00’ ~ ‘9999-12-31 23:59:59’ | 年月日时分秒 | 通用时间(如创建时间) |
TIMESTAMP | 4字节 | ‘1970-01-01 00:00:01’ UTC ~ ‘2038-01-19 03:14:07’ UTC | 同上 | 自动更新时间戳(如最后修改时间) |
YEAR | 1字节 | 1901 ~ 2155 | 年份 | 单独存储年份(如产品年份) |
核心区别:
DATETIMEvsTIMESTAMP:DATETIME存绝对值(如存2023-01-01 12:00:00,全球看到的都是这个时间)。TIMESTAMP存相对值(实际存的是UTC时间戳,查询时自动转换为当前时区时间)。- 建议:存用户无关的时间(如订单创建时间)用
DATETIME,存与服务器时区相关的时间(如登录时间)用TIMESTAMP。
自动更新特性:
-- 创建表时设置自动更新时间戳
CREATE TABLE t (
id INT,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP, -- 插入时自动填充当前时间
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -- 更新时自动刷新
);
四、二进制类型(存储非文本数据)
| 类型 | 最大长度 | 适用场景 |
|---|---|---|
BINARY(N) | 固定N字节 | 存储二进制数据(如加密密钥) |
VARBINARY(N) | 可变长度+1/2字节 | 存储变长二进制(如图片缩略图) |
BLOB | 最大65,535字节 | 存储大二进制(如小文件) |
MEDIUMBLOB | 最大16MB | 存储中等文件(如音频) |
LONGBLOB | 最大4GB | 存储大文件(如视频) |
避坑指南:
- 不建议直接存大文件(如图片、视频)到数据库(影响性能),建议存文件路径,文件放服务器磁盘或对象存储(如阿里云OSS)。
- 二进制类型与字符串类型的区别:
- 二进制类型按字节存储(如
BINARY(3)存'abc'占3字节),字符串类型按字符存储(如CHAR(3)在UTF-8下存'中国'占6字节)。
- 二进制类型按字节存储(如
五、JSON类型(MySQL 5.7+新增)
-
特点:
- 存储JSON格式数据(如
{"name":"张三","age":20})。 - 支持索引(通过
JSON_EXTRACT()函数)。 - 查询效率高于
TEXT(无需解析整个JSON)。
- 存储JSON格式数据(如
-
示例:
CREATE TABLE user ( id INT, info JSON ); -- 插入JSON数据 INSERT INTO user VALUES (1, '{"name":"张三","age":20,"hobbies":["读书","运动"]}'); -- 查询JSON字段(用->或->>操作符) SELECT info->'$.name' AS name FROM user; -- 返回带引号的字符串 SELECT info->>'$.name' AS name FROM user; -- 返回不带引号的字符串 -- 对JSON字段建索引 ALTER TABLE user ADD INDEX idx_info_age ((info->>'$.age')); -
适用场景:
- 半结构化数据(如配置项、用户偏好)。
- 数据结构不固定(避免频繁改表结构)。
六、选择数据类型的核心原则
-
够用就好,越小越好:
- 能用
TINYINT就不用INT(如状态码),节省存储空间。 - 示例:用户积分用
INT(最大21亿),但论坛帖子数可能需要BIGINT。
- 能用
-
避免NULL:
- 字段尽量加
NOT NULL(否则可能导致索引失效、查询效率降低)。 - 示例:
age INT DEFAULT 0比age INT NULL更优。
- 字段尽量加
-
优先用内置类型:
- 日期用
DATE/DATETIME,而非字符串(字符串无法用日期函数,如YEAR())。 - 示例:存IP地址用
INT UNSIGNED(通过INET_ATON()/INET_NTOA()转换),而非VARCHAR(15)。
- 日期用
-
权衡存储与性能:
- 频繁更新的字段避免用
VARCHAR(可能产生碎片),但也别过度用CHAR(浪费空间)。 - 示例:用户昵称用
VARCHAR(50),而非CHAR(50)。
- 频繁更新的字段避免用
七、常见场景的数据类型选择
| 场景 | 推荐类型 | 示例 |
|---|---|---|
| 用户ID | BIGINT UNSIGNED | id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT |
| 手机号 | CHAR(11) | phone CHAR(11) NOT NULL |
| 邮箱 | VARCHAR(255) | email VARCHAR(255) UNIQUE |
| 密码(哈希值) | CHAR(60) | password CHAR(60) NOT NULL |
| 金额 | DECIMAL(10,2) | price DECIMAL(10,2) DEFAULT 0.00 |
| 经纬度 | DECIMAL(10,6) | longitude DECIMAL(10,6), latitude DECIMAL(10,6) |
| 状态码(如订单状态) | TINYINT UNSIGNED | status TINYINT UNSIGNED DEFAULT 0 |
| 文章内容 | TEXT或MEDIUMTEXT | content MEDIUMTEXT |
| 图片路径 | VARCHAR(255) | image_url VARCHAR(255) |
| 时间戳(精确到毫秒) | DATETIME(3) | create_time DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) |
掌握这些类型的特性和适用场景,你设计的表结构会更合理,查询性能也会更好。实际开发中,建议结合业务需求(如数据量、查询模式)选择最优类型。
在Java开发中,与MySQL数据类型的对应关系需要从JDBC映射、ORM框架适配、数据处理逻辑等多个维度综合考虑。以下是系统性的对应方案和避坑指南:
一、基础数据类型映射(JDBC标准)
Java与MySQL的基础数据类型映射遵循JDBC规范,这是最底层的映射关系,所有ORM框架都基于此:
| MySQL 类型 | Java 类型(推荐) | 备注 |
|---|---|---|
| 整数类型 | ||
TINYINT | byte 或 Boolean | TINYINT(1) 通常映射为 Boolean |
SMALLINT | short | |
INT | int 或 Integer | |
BIGINT | long 或 Long | 主键ID建议用 Long(避免溢出) |
BIGINT UNSIGNED | String 或 BigInteger | 无符号大整数需特殊处理 |
| 浮点类型 | ||
FLOAT | float 或 Float | 不推荐用于财务计算 |
DOUBLE | double 或 Double | 不推荐用于财务计算 |
DECIMAL | BigDecimal | 必须用 BigDecimal(避免精度丢失) |
| 字符串类型 | ||
CHAR/VARCHAR | String | |
TEXT/MEDIUMTEXT | String | 大文本建议限制长度(避免OOM) |
ENUM | String 或 Enum | 推荐映射为 Enum(需自定义转换器) |
| 日期时间类型 | ||
DATE | LocalDate | Java 8+ 推荐 |
TIME | LocalTime | Java 8+ 推荐 |
DATETIME/TIMESTAMP | LocalDateTime | Java 8+ 推荐 |
YEAR | Short 或 Integer | |
| 二进制类型 | ||
BLOB/VARBINARY | byte[] 或 InputStream | 大文件建议存路径而非直接存二进制 |
JSON | String 或 JSONObject | 需手动解析(或借助ORM框架) |
二、ORM框架的优化映射(以MyBatis和Hibernate为例)
1. MyBatis映射
MyBatis通过typeHandler处理特殊类型,以下是常用配置:
<!-- mybatis-config.xml 配置 -->
<typeHandlers>
<!-- 注册Java 8日期时间类型处理器 -->
<typeHandler handler="org.apache.ibatis.type.LocalDateTimeTypeHandler"/>
<typeHandler handler="org.apache.ibatis.type.LocalDateTypeHandler"/>
<!-- 自定义JSON类型处理器 -->
<typeHandler handler="com.example.JsonTypeHandler"/>
</typeHandlers>
实体类示例:
public class User {
private Long id;
private String username;
private BigDecimal balance; // 对应DECIMAL
private LocalDateTime createTime; // 对应DATETIME
private Map<String, Object> config; // 对应JSON字段(需自定义TypeHandler)
// getters/setters
}
2. Hibernate映射
Hibernate通过JPA注解和自定义转换器处理类型映射:
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // 对应BIGINT
@Column(name = "username")
private String username;
@Column(name = "balance", precision = 10, scale = 2)
private BigDecimal balance; // 对应DECIMAL(10,2)
@Column(name = "create_time")
private LocalDateTime createTime; // 对应DATETIME
@Convert(converter = JsonConverter.class) // 自定义JSON转换器
@Column(name = "config", columnDefinition = "JSON")
private Map<String, Object> config;
// getters/setters
}
自定义JSON转换器示例:
@Converter(autoApply = true)
public class JsonConverter implements AttributeConverter<Map<String, Object>, String> {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public String convertToDatabaseColumn(Map<String, Object> attribute) {
try {
return objectMapper.writeValueAsString(attribute);
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON序列化失败", e);
}
}
@Override
public Map<String, Object> convertToEntityAttribute(String dbData) {
try {
return objectMapper.readValue(dbData, new TypeReference<Map<String, Object>>() {});
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON反序列化失败", e);
}
}
}
三、特殊场景处理方案
1. 无符号整数(如BIGINT UNSIGNED)
MySQL的BIGINT UNSIGNED范围超过Java的long,需特殊处理:
// 方案1:映射为String(最简单,但无法直接做数学运算)
ResultSet rs = statement.executeQuery("SELECT unsigned_col FROM table");
if (rs.next()) {
String unsignedValue = rs.getString("unsigned_col"); // 直接取字符串
}
// 方案2:映射为BigInteger(可做数学运算)
ResultSet rs = statement.executeQuery("SELECT unsigned_col FROM table");
if (rs.next()) {
BigInteger unsignedValue = rs.getBigDecimal("unsigned_col").toBigInteger();
}
2. JSON字段处理
从MySQL 5.7+开始支持原生JSON类型,Java中可通过以下方式处理:
// 方案1:手动解析(适用于简单场景)
ResultSet rs = statement.executeQuery("SELECT json_col FROM table");
if (rs.next()) {
String jsonStr = rs.getString("json_col");
JSONObject jsonObj = new JSONObject(jsonStr); // 使用JSON库解析
String value = jsonObj.getString("key");
}
// 方案2:ORM框架自动映射(如Hibernate+Jackson)
@Entity
public class Product {
@Convert(converter = JsonConverter.class)
private Map<String, Object> attributes; // 自动映射JSON字段
}
3. 日期时间时区问题
MySQL的TIMESTAMP会自动转换为服务器时区,而DATETIME不会。建议:
// 方案1:统一使用UTC时区(推荐)
// 数据库连接URL添加时区参数
jdbc:mysql://localhost:3306/dbname?serverTimezone=UTC
// 方案2:手动处理时区转换
LocalDateTime localDateTime = resultSet.getObject("create_time", LocalDateTime.class);
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("UTC"));
四、性能与安全注意事项
-
避免
NULL值- MySQL字段设置
NOT NULL DEFAULT ...,Java实体类用Optional<T>或合理的默认值。
- MySQL字段设置
-
大文本/二进制字段
- 避免直接用
TEXT/BLOB存储大文件,建议存文件路径到VARCHAR,文件存OSS等存储服务。
- 避免直接用
-
DECIMAL精度问题- 初始化
BigDecimal必须用new BigDecimal("10.00"),而非new BigDecimal(10.00)(后者会有精度误差)。
- 初始化
-
索引字段类型匹配
- 若MySQL索引字段为
VARCHAR,Java查询时应保证参数类型为String(避免类型转换导致索引失效)。
- 若MySQL索引字段为
五、总结:最佳实践清单
-
优先使用Java 8+日期时间API
LocalDate→DATELocalDateTime→DATETIME/TIMESTAMP- 避免使用
java.util.Date和java.sql.Timestamp(设计缺陷)。
-
财务计算用
BigDecimal- 精度和舍入模式明确设置:
BigDecimal value = new BigDecimal("10.25").setScale(2, RoundingMode.HALF_UP);
- 精度和舍入模式明确设置:
-
自定义类型转换器
- 对特殊类型(如
ENUM、JSON),通过ORM框架的转换器机制统一处理。
- 对特殊类型(如
-
数据库连接配置
# 关键连接参数(MySQL 8+) spring.datasource.url=jdbc:mysql://localhost:3306/dbname?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.maximum-pool-size=10
通过以上方案,你可以在Java开发中与MySQL数据类型实现高效、安全的交互,同时避免常见的坑点。


被折叠的 条评论
为什么被折叠?



