杰瑞科技汇

MySQL与Java类型如何转换?

  1. Java 类型 → SQL 类型 (JDBC 设置参数时)
  2. SQL 类型 → Java 类型 (JDBC 读取结果时)
  3. 最佳实践与工具推荐

Java 类型 → SQL 类型 (设置参数 PreparedStatement.setXXX())

当你使用 PreparedStatement 来执行 SQL 语句(如 INSERT, UPDATE)时,你需要将 Java 变量的值绑定到 SQL 语句的占位符()上,这时就需要进行 Java 到 SQL 类型的转换。

MySQL与Java类型如何转换?-图1
(图片来源网络,侵删)

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.Timestampjava.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+) VARCHARLONGVARCHAR getString() String jsonStr = rs.getString("json_data");

类型不匹配与转换问题:

MySQL与Java类型如何转换?-图2
(图片来源网络,侵删)

这是类型转换中最需要注意的地方,当数据库中的类型与你期望的 Java 类型不匹配时,JDBC 驱动会尝试进行转换,但这可能导致意外结果或错误。

常见问题与解决方案:

  1. VARCHAR 读取数字

    • 问题: 数据库中 status 字段是 VARCHAR('active', 'inactive'),但你误以为是 INT,调用了 rs.getInt("status")
    • 结果: 会抛出 SQLException
    • 解决方案: 总是使用正确的 getXXX() 方法,对于枚举或状态码,getString() 是最安全的选择。
  2. TINYINT(1) 读取布尔值

    MySQL与Java类型如何转换?-图3
    (图片来源网络,侵删)
    • 场景: MySQL 中 is_deleted 字段定义为 TINYINT(1),0 表示 false,1 表示 true。
    • 正确做法: boolean isDeleted = rs.getBoolean("is_deleted"); JDBC 驱动会自动将 0 转换为 false,非 0 转换为 true
    • 注意: 如果你用 getInt(),你会得到 0 或 1。
  3. DATETIME 读取 java.util.Date

    • 问题: 你有一个 java.util.Date 类型的字段 lastLoginTime
    • 最佳实践: java.sql.Timestampjava.util.Date 的子类,并且包含了纳秒级的精度,非常适合用于数据库的 DATETIMETIMESTAMP
    • 代码:
      // 从数据库读取
      java.sql.Timestamp ts = rs.getTimestamp("last_login_time");
      // 转换为 java.util.Date (直接赋值即可)
      java.util.Date lastLoginTime = ts; 
  4. 处理 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
      }

最佳实践与工具推荐

手动处理类型转换繁琐且容易出错,以下是提升开发效率和代码质量的建议。

最佳实践

  1. 始终使用 PreparedStatement: 它不仅能防止 SQL 注入,还能让 JDBC 驱动更好地处理类型转换。

  2. 优先使用包装类型: 在 Java 实体类中,对于可为空的数据库字段,使用 Integer, Long, Double, Boolean, String 等包装类型,而不是基本类型 int, long 等,这样可以明确地表示 NULL 值。

  3. 明确的 getXXX(): 除非你有特殊需求,否则不要使用通用的 getObject(),明确的类型方法更安全、更易读。

  4. NULL 检查: 养成在从 ResultSet 读取数据后检查 wasNull() 的好习惯。

  5. 使用 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();

工具推荐

  1. 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; // 自动处理转换
      }
  2. Bean 映射工具 如果你不想使用完整的 ORM 框架,可以使用轻量级的 Bean 映射工具来手动处理 ResultSet 到 Java 对象的转换。

    • Apache Commons BeanUtils: 提供了将 ResultSet 映射到 JavaBean 的功能。

    • Spring JDBC: Spring 框架提供的 JdbcTemplateBeanPropertyRowMapper 非常好用,可以一行代码完成映射。

      // 使用 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 工具库,将你从繁琐且易错的类型转换工作中解放出来,让你能更专注于业务逻辑。

分享:
扫描分享到社交APP
上一篇
下一篇