Java 中处理数据库数据几乎总是使用 java.sql 包下的类型,而不是它们在 java.lang 包下的原始对应物(如 int, String)。
- 你在 Java 代码中定义的实体类(POJO)可以使用基本类型或包装类型。
- 但在与 JDBC 交互时(
PreparedStatement,ResultSet),你使用的setXxx()和getXxx()方法的参数和返回值类型,必须是java.sql包中的类型。
详细类型对照表
这个表格分为三列:
- MySQL 类型: 你在创建表时使用的 SQL 类型。
- JDBC 类型 (
java.sql): 你在 JDBC 代码中使用的类型,用于设置和获取数据。 - Java 常用类型: 你在 Java 实体类(POJO)中通常使用的类型,这是业务逻辑层最常用的类型。
| MySQL 类型 | JDBC 类型 (java.sql.*) |
Java 常用类型 (POJO) | 说明 |
|---|---|---|---|
| 数值类型 | |||
TINYINT |
TINYINT |
Byte, byte |
1字节整数,常用于布尔值(0/1)。 |
SMALLINT |
SMALLINT |
Short, short |
2字节整数。 |
INT, INTEGER |
INTEGER |
Integer, int |
4字节整数,最常用的整数类型。 |
BIGINT |
BIGINT |
Long, long |
8字节整数,用于大整数,如自增主键、时间戳。 |
FLOAT |
FLOAT |
Float, float |
单精度浮点数。 |
DOUBLE |
DOUBLE |
Double, double |
双精度浮点数。 |
DECIMAL, NUMERIC |
DECIMAL |
java.math.BigDecimal |
精确的小数计算,推荐用于货币、财务等场景。避免使用 float/double。 |
| 字符串类型 | |||
CHAR |
CHAR |
String |
定长字符串。 |
VARCHAR |
VARCHAR |
String |
变长字符串,最常用的文本类型。 |
TEXT (long) |
LONGVARCHAR |
String |
长文本。 |
TINYTEXT (short) |
VARCHAR |
String |
短文本。 |
MEDIUMTEXT (medium) |
LONGVARCHAR |
String |
中等长度文本。 |
| 日期和时间类型 | |||
DATE |
DATE |
java.sql.Date |
仅存储日期(年-月-日)。 |
TIME |
TIME |
java.sql.Time |
仅存储时间(时:分:秒)。 |
DATETIME |
TIMESTAMP |
java.sql.Timestamp |
存储日期和时间(年-月-日 时:分:秒),范围到 9999-12-31 23:59:59。 |
TIMESTAMP |
TIMESTAMP |
java.sql.Timestamp |
存储日期和时间,但范围较小(1970-01-01 到 2038-01-19),会根据时区变化。 |
| 二进制类型 | |||
BINARY |
BINARY |
byte[] |
定长二进制数据。 |
VARBINARY |
VARBINARY |
byte[] |
变长二进制数据。 |
BLOB (large) |
BLOB |
byte[], java.sql.Blob |
二进制大对象,用于存储图片、文件等。 |
TINYBLOB (short) |
VARBINARY |
byte[] |
小二进制对象。 |
| 其他类型 | |||
BIT |
BIT |
Boolean, boolean |
位字段,可以存储多个位。 |
JSON |
VARCHAR, LONGVARCHAR 或特定类型 |
String, JSONObject (e.g., from org.json or com.fasterxml.jackson.databind) |
MySQL 5.7+ 支持,JDBC 驱动通常将其作为字符串处理,也可以使用特定库解析为 JSON 对象。 |
关键点与最佳实践
java.sql.Date, Time, Timestamp
这是最容易混淆的地方,它们都继承自 java.util.Date,但用途不同。
-
java.sql.Date: 只包含日期信息(没有时间部分)。- 从数据库获取
DATE字段时,使用resultSet.getDate()。 - 向数据库设置
DATE字段时,使用preparedStatement.setDate()。 - 注意:
java.sql.Date的构造函数需要一个毫秒值,这个毫秒值会被截断,只保留日期部分。
- 从数据库获取
-
java.sql.Time: 只包含时间信息(没有日期部分)。- 用于
TIME字段。
- 用于
-
java.sql.Timestamp: 同时包含日期和时间信息,精度到纳秒。- 这是 MySQL 的
DATETIME和TIMESTAMP字段在 Java 中的最佳映射。 Timestamp是java.util.Date的子类,但增加了一些额外的方法。
- 这是 MySQL 的
如何转换?
// 从 java.util.Date 转换 java.util.Date utilDate = new java.util.Date(); // 转换为 java.sql.Date (只保留日期) java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime()); // 转换为 java.sql.Timestamp (保留日期和时间) java.sql.Timestamp timestamp = new java.sql.Timestamp(utilDate.getTime()); // 反向转换 java.util.Date dateFromTimestamp = timestamp; // Timestamp 可以直接赋值给 util.Date
BigDecimal 与浮点数
对于涉及金钱、财务、科学计算等需要高精度的场景,必须使用 BigDecimal。
- 为什么?
float和double是基于二进制的浮点数,无法精确表示所有十进制小数。1 + 0.2在double中结果不等于3。 - 如何使用?
- 从数据库读取
DECIMAL字段时,使用resultSet.getBigDecimal()。 - 向数据库写入时,使用
preparedStatement.setBigDecimal()。
- 从数据库读取
// 正确的做法
BigDecimal price = resultSet.getBigDecimal("price");
preparedStatement.setBigDecimal("price", new BigDecimal("123.45"));
// 错误的做法(可能导致精度丢失)
double price = resultSet.getDouble("price");
preparedStatement.setDouble("price", 123.45);
NULL 值
- 当数据库字段为
NULL时,调用getXxx()方法(如getString(),getInt())会返回null(对于对象类型)或抛出NullPointerException(对于基本类型int,double等)。 - 在设置参数时,可以使用
preparedStatement.setNull(columnIndex, Types.VARCHAR)来显式地设置一个NULL值。
使用 ResultSetMetaData 获取类型
如果你在编写通用的数据库操作工具(如 ORM 框架),你可能不知道查询结果的具体列类型,这时可以使用 ResultSetMetaData 来动态获取信息。
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnName(i);
int columnType = metaData.getColumnType(i); // 返回 java.sql.Types 中的常量
String columnTypeName = metaData.getColumnTypeName(i); // 返回数据库类型名,如 "VARCHAR", "INT"
// 根据columnType决定调用哪个getXxx()方法
switch (columnType) {
case Types.VARCHAR:
String value = resultSet.getString(i);
break;
case Types.INTEGER:
int intValue = resultSet.getInt(i);
break;
// ... 其他类型
}
}
| 场景 | 推荐做法 |
|---|---|
| 定义数据库表 | 根据业务需求选择合适的 MySQL 类型。 |
| JDBC 交互 | 严格使用 java.sql 包下的类型(java.sql.Date, java.sql.Timestamp, java.sql.Blob 等)。 |
| Java 实体类 (POJO) | 使用 Java 基本类型及其包装类、String、BigDecimal 等。 |
| 日期时间 | MySQL DATETIME -> Java java.sql.Timestamp,MySQL DATE -> Java java.sql.Date。 |
| 精确小数 | MySQL DECIMAL -> Java java.math.BigDecimal。 |
| 处理 NULL | 始终检查 resultSet.getObject() 的返回值是否为 null。 |
遵循这些原则,可以避免绝大多数因类型不匹配而导致的错误,并写出更健壮、更可靠的数据库应用程序。
