- Java 类型 → SQL 类型 (JDBC 设置参数时)
- SQL 类型 → Java 类型 (JDBC 读取结果时)
- 最佳实践与工具推荐
Java 类型 → SQL 类型 (设置参数 PreparedStatement.setXXX())
当你使用 PreparedStatement 来执行 SQL 语句(如 INSERT, UPDATE)时,你需要将 Java 变量的值绑定到 SQL 语句的占位符()上,这时就需要进行 Java 到 SQL 类型的转换。

JDBC 提供了一系列 setXXX() 方法,XXX 通常对应标准的 SQL 类型。
| Java 类型 | JDBC setXXX() 方法 |
SQL 类型 (JDBC Type) | 示例 |
|---|---|---|---|
String |
setString() |
VARCHAR, CHAR, LONGVARCHAR |
ps.setString(1, "John Doe"); |
int |
setInt() |
INTEGER, TINYINT, SMALLINT |
ps.setInt(2, 30); |
long |
setLong() |
BIGINT |
ps.setLong(3, 123456789L); |
double |
setDouble() |
DOUBLE, FLOAT |
ps.setDouble(4, 99.99); |
float |
setFloat() |
REAL, FLOAT |
ps.setFloat(5, 89.5f); |
boolean |
setBoolean() |
BIT, BOOLEAN |
ps.setBoolean(6, true); |
java.sql.Date |
setDate() |
DATE |
ps.setDate(7, java.sql.Date.valueOf("2025-10-27")); |
java.sql.Time |
setTime() |
TIME |
ps.setTime(8, java.sql.Time.valueOf("14:30:00")); |
java.sql.Timestamp |
setTimestamp() |
TIMESTAMP, DATETIME |
ps.setTimestamp(9, new java.sql.Timestamp(System.currentTimeMillis())); |
java.util.Date |
setTimestamp() |
TIMESTAMP, DATETIME |
ps.setTimestamp(10, new java.sql.Timestamp(new java.util.Date().getTime())); |
byte[] |
setBytes() |
BINARY, VARBINARY, LONGVARBINARY |
ps.setBytes(11, "some data".getBytes()); |
java.sql.Blob |
setBlob() |
BLOB |
ps.setBlob(12, blobObject); |
java.sql.Clob |
setClob() |
CLOB |
ps.setClob(13, clobObject); |
Object |
setObject() |
任意类型 | ps.setObject(14, anyJavaObject); |
关键点:
- 基本类型 vs 包装类型: 对于
int,long,double等基本类型,JDBC 驱动程序通常能自动处理它们的包装类型(Integer,Long,Double)。ps.setInt(1, new Integer(30))也是可以的。 java.util.Date的处理:java.util.Date是一个旧的时间类,在 JDBC 中,如果你想存储日期和时间,推荐使用java.sql.Timestamp。java.sql.Date只存储日期部分(没有时间),java.sql.Time只存储时间部分(没有日期)。setObject(): 这是一个万能方法,可以接受任何 Java 对象,JDBC 驱动会尝试自动转换,但为了代码的清晰性和类型安全,最好使用明确的setXXX()方法。
SQL 类型 → Java 类型 (获取结果 ResultSet.getXXX())
这是更常见也更容易出错的环节,当你从 ResultSet 中读取数据时,需要将数据库中的 SQL 类型转换为 Java 类型。
| SQL 类型 (MySQL) | JDBC Type | Java getXXX() 方法 |
示例 |
|---|---|---|---|
TINYINT(1) |
BIT |
getBoolean() |
boolean active = rs.getBoolean("is_active"); |
VARCHAR, CHAR |
VARCHAR |
getString() |
String name = rs.getString("name"); |
TEXT, MEDIUMTEXT |
LONGVARCHAR |
getString() 或 getCharacterStream() |
String bio = rs.getString("biography"); |
INT, INTEGER |
INTEGER |
getInt() 或 getLong() |
int id = rs.getInt("id"); |
BIGINT |
BIGINT |
getLong() |
long userId = rs.getLong("user_id"); |
DOUBLE, FLOAT |
DOUBLE |
getDouble() |
double price = rs.getDouble("price"); |
DECIMAL, NUMERIC |
DECIMAL, NUMERIC |
getBigDecimal() |
BigDecimal amount = rs.getBigDecimal("amount"); |
DATE |
DATE |
getDate() |
java.sql.Date birthDate = rs.getDate("birth_date"); |
TIME |
TIME |
getTime() |
java.sql.Time startTime = rs.getTime("start_time"); |
DATETIME, TIMESTAMP |
TIMESTAMP |
getTimestamp() |
java.sql.Timestamp createdTime = rs.getTimestamp("created_at"); |
BLOB |
BLOB |
getBytes() 或 getBlob() |
byte[] imageData = rs.getBytes("image_data"); |
JSON (MySQL 5.7+) |
VARCHAR 或 LONGVARCHAR |
getString() |
String jsonStr = rs.getString("json_data"); |
类型不匹配与转换问题:

这是类型转换中最需要注意的地方,当数据库中的类型与你期望的 Java 类型不匹配时,JDBC 驱动会尝试进行转换,但这可能导致意外结果或错误。
常见问题与解决方案:
-
从
VARCHAR读取数字- 问题: 数据库中
status字段是VARCHAR('active', 'inactive'),但你误以为是INT,调用了rs.getInt("status")。 - 结果: 会抛出
SQLException。 - 解决方案: 总是使用正确的
getXXX()方法,对于枚举或状态码,getString()是最安全的选择。
- 问题: 数据库中
-
从
TINYINT(1)读取布尔值
(图片来源网络,侵删)- 场景: MySQL 中
is_deleted字段定义为TINYINT(1),0 表示 false,1 表示 true。 - 正确做法:
boolean isDeleted = rs.getBoolean("is_deleted");JDBC 驱动会自动将 0 转换为false,非 0 转换为true。 - 注意: 如果你用
getInt(),你会得到 0 或 1。
- 场景: MySQL 中
-
从
DATETIME读取java.util.Date- 问题: 你有一个
java.util.Date类型的字段lastLoginTime。 - 最佳实践:
java.sql.Timestamp是java.util.Date的子类,并且包含了纳秒级的精度,非常适合用于数据库的DATETIME或TIMESTAMP。 - 代码:
// 从数据库读取 java.sql.Timestamp ts = rs.getTimestamp("last_login_time"); // 转换为 java.util.Date (直接赋值即可) java.util.Date lastLoginTime = ts;
- 问题: 你有一个
-
处理
NULL值- 问题: 如果数据库字段是
NULL,直接调用rs.getInt("age")会返回0,这可能是错误的(0是一个有效的年龄值)。 - 解决方案: 在读取任何值之前,必须先检查
NULL。int age = 0; // 默认值 if (rs.wasNull()) { // 处理 NULL 值,例如设置为 -1 或一个特殊对象 age = -1; } else { age = rs.getInt("age"); }或者更简洁地:
Integer age = rs.getInt("age"); if (rs.wasNull()) { age = null; // 使用包装类来表示 NULL }
- 问题: 如果数据库字段是
最佳实践与工具推荐
手动处理类型转换繁琐且容易出错,以下是提升开发效率和代码质量的建议。
最佳实践
-
始终使用
PreparedStatement: 它不仅能防止 SQL 注入,还能让 JDBC 驱动更好地处理类型转换。 -
优先使用包装类型: 在 Java 实体类中,对于可为空的数据库字段,使用
Integer,Long,Double,Boolean,String等包装类型,而不是基本类型int,long等,这样可以明确地表示NULL值。 -
明确的
getXXX(): 除非你有特殊需求,否则不要使用通用的getObject(),明确的类型方法更安全、更易读。 -
NULL检查: 养成在从ResultSet读取数据后检查wasNull()的好习惯。 -
使用
java.time包 (Java 8+): 对于日期和时间,java.sql.Date,Time,Timestamp是旧 API,现代 Java 应用应优先使用java.time包中的LocalDate,LocalTime,LocalDateTime,ZonedDateTime等。-
转换示例:
-
LocalDate(Java) ↔java.sql.Date(JDBC)// Java -> JDBC java.sql.Date sqlDate = java.sql.Date.valueOf(localDate); // JDBC -> Java LocalDate localDate = rs.getDate("hire_date").toLocalDate(); -
LocalDateTime(Java) ↔java.sql.Timestamp(JDBC)// Java -> JDBC java.sql.Timestamp ts = java.sql.Timestamp.valueOf(localDateTime); // JDBC -> Java LocalDateTime localDateTime = rs.getTimestamp("event_time").toLocalDateTime();
-
-
工具推荐
-
ORM 框架 (强烈推荐) 对象关系映射框架是解决类型转换问题的终极方案,它们会自动处理几乎所有的 JDBC 类型映射,让你可以用面向对象的方式操作数据库。
-
MyBatis: 半自动化 ORM,你需要在 XML 文件或注解中定义 SQL 和结果映射,可以精确控制结果集到 Java 对象的转换过程,非常灵活,性能高。
<!-- MyBatis Result Map 示例 --> <resultMap id="userResultMap" type="com.example.User"> <id property="id" column="id" /> <result property="username" column="username" /> <result property="createTime" column="create_time" javaType="java.time.LocalDateTime" jdbcType="TIMESTAMP" /> </resultMap> -
JPA / Hibernate: 全自动化 ORM,你只需要在 Java 实体类上使用注解(如
@Entity,@Column,@Temporal),Hibernate 就能自动完成表结构创建和对象持久化,代码非常简洁。@Entity public class User { @Id private Long id; private String username; @Column(name = "create_time") private LocalDateTime createTime; // 自动处理转换 }
-
-
Bean 映射工具 如果你不想使用完整的 ORM 框架,可以使用轻量级的 Bean 映射工具来手动处理
ResultSet到 Java 对象的转换。-
Apache Commons BeanUtils: 提供了将
ResultSet映射到 JavaBean 的功能。 -
Spring JDBC: Spring 框架提供的
JdbcTemplate和BeanPropertyRowMapper非常好用,可以一行代码完成映射。// 使用 Spring JdbcTemplate JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); String sql = "SELECT id, username, create_time FROM users WHERE id = ?"; // RowMapper 会自动根据列名和属性名进行映射 User user = jdbcTemplate.queryForObject(sql, new Object[]{1L}, new BeanPropertyRowMapper<>(User.class));
-
| 场景 | 手动处理 (不推荐) | 推荐做法 |
|---|---|---|
| Java → SQL | ps.setString(1, name); ps.setInt(2, age); |
使用 PreparedStatement,并选择合适的 setXXX() 方法。 |
| SQL → Java | int id = rs.getInt("id"); String name = rs.getString("name"); 必须检查 rs.wasNull() |
使用 ORM 框架 (MyBatis/JPA) 或 Spring 的 BeanPropertyRowMapper,让工具自动处理。 |
| 日期时间 | java.sql.Date, Timestamp |
使用 Java 8+ 的 java.time 包,并结合 ORM 或工具进行转换。 |
虽然理解 JDBC 底层的类型转换机制很重要,但在实际项目中,强烈建议使用 ORM 框架或成熟的 JDBC 工具库,将你从繁琐且易错的类型转换工作中解放出来,让你能更专注于业务逻辑。
