1、简介 {#1简介}
本文将带你了解使用 Hibernate 时出现 "could not determine recommended JdbcType for class" 异常的原因,以及解决办法。
2、常见原因 {#2常见原因}
出现该异常,通常是因为 JPA(Java Persistence API)抛出了异常。
这种情况发生在 JPA 无法确定类的推荐 JDBC 类型时。
通常,这会 Hibernate 应用启动期间发生,当 Hibernate 尝试创建数据库 Schema 或验证映射时。
3、复合数据类型 {#3复合数据类型}
JPA 中出现 "could not determine recommended JdbcType for class " 错误的一个常见原因是,我们在实体类中使用了复合数据类型或其他非原始类型。一个典型的例子就是使用 java.util.Map
来存储动态键值对。
来看看下面这个实体类:
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 使用了 Map
private Map<String, String> studentDetails;
// Getter/Setter 省略
}
在上例中,studentDetails
字段是 Map<String, String>
。JPA 可能会抛出错误,因为它无法自动为这种复杂的数据类型确定推荐的 JDBC 类型。
测试,尝试用这个实体类建立一个 EntityManagerFactory
:
PersistenceException exception = assertThrows(PersistenceException.class,
() -> createEntityManagerFactory("jpa-undeterminejdbctype-complextype"));
assertThat(exception)
.hasMessage("Could not determine recommended JdbcType for Java type 'java.util.Map<java.lang.String, java.lang.String>'");
要这个问题,可以使用 @JdbcTypeCode
注解来指定 Hibernate 应为 studentDetails
字段使用的 JDBC 类型。在本例中,我们可以使用 JSON 数据类型将 Map
存储为 JSON 对象。
首先,需要在 pom.xml 文件中添加 Jackson 依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
Jackson 是 Java 中处理 JSON 的常用库,它提供了映射 JSON 数据类型的必要工具。接下来,使用 @JdbcTypeCode
注解告诉 Hibernate 在 studentDetails
字段中使用 JSON 数据类型。下面是更新后的实体类:
@Entity
public class StudentWithJson {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@JdbcTypeCode(SqlTypes.JSON)
private Map<String, String> studentDetails;
// Getter / Setter 省略
}
通过添加 @JdbcTypeCode
注解,明确指示 Hibernate 该如何处理复杂数据类型。SqlTypes.JSON
常量用于指示 JDBC 类型为 JSON 。现在,Hibernate 将 studentDetails
字段作为 JSON 对象存储在数据库中,从而可以将 Java Map 正确映射到数据库中的 JSON 列:
EntityManagerFactory factory = createEntityManagerFactory("jpa-undeterminejdbctype-complextypewithJSON");
EntityManager entityManager = factory.createEntityManager();
entityManager.getTransaction().begin();
StudentWithJson student = new StudentWithJson();
Map<String, String> details = new HashMap<>();
details.put("course", "Computer Science");
details.put("year", "2024");
student.setStudentDetails(details);
entityManager.persist(student);
entityManager.getTransaction().commit();
StudentWithJson retrievedStudent = entityManager.find(StudentWithJson.class, student.getId());
assertEquals(student.getStudentDetails(), retrievedStudent.getStudentDetails());
4、JPA 关系映射 {#4jpa-关系映射}
JPA 依靠注解来理解实体与其字段类型之间的关系。省略这些注解会导致 Hibernate 无法为相关字段确定推荐的 JDBC 类型,从而导致错误。
来看看 @OneToOne
和 @OneToMany
这两种常见的关系映射,缺少注解会导致什么问题。
4.1、一对一映射 {#41一对一映射}
考虑一个场景:Employee
(雇员)实体与 Address
(地址)实体之间是一对一的关系。如果我们忘记注解这种关系,Hibernate 可能会抛出 PersistenceException
(持久化异常),因为它无法为 Address
字段确定适当的 JDBC 类型:
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Address address;
// Getter / Setter 方法省略
}
在这种情况下,当我们尝试初始化 EntityManagerFactory
时,会遇到以下异常:
PersistenceException exception = assertThrows(PersistenceException.class,
() -> createEntityManagerFactory("jpa-undeterminejdbctype-relationship"));
assertThat(exception)
.hasMessage("Could not determine recommended JdbcType for Java type 'com.baeldung.jpa.undeterminejdbctype.Address'");
出现异常的原因是 Hibernate 不知道如何将 address
字段映射到数据库。要解决这个问题,我们需要正确注解一对一关系。
为 Employee
实体添加必要的注解:
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne
@JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
// Getter / Setter 方法省略
}
通过添加 @OneToOne
注解,我们指示 Hibernate 处理 Employee
和 Address
之间的关系。现在,Hibernate 知道 Employee
实体中的 address
字段映射到了 Address
实体,并使用 Employee
表中的 address_id
列来建立这种关系。
4.2、一对多映射 {#42一对多映射}
与一对一映射类似,在处理 JPA 中的一对多关系时,正确的注解是至关重要的,以避免异常。以 Department
(部门)和 Employee
(雇员)实体之间的一对多映射为例。
在这种情况下,一个 Department
可以有多个 Employee
。如果我们忘记正确注解这种关系,Hibernate 可能会抛出 PersistenceException
,因为它无法为 employees
字段确定适当的 JDBC 类型。
下面初始设置,没有适当的注解:
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private List<Employee> employees = new ArrayList<>();
// Getter / Setter 方法省略
}
这会在初始化 EntityManagerFactory
时抛出异常:
PersistenceException exception = assertThrows(PersistenceException.class,
() -> createEntityManagerFactory("jpa-undeterminejdbctype-relationship"));
assertThat(exception)
.hasMessage("Could not determine recommended JdbcType for Java type 'java.util.List<com.baeldung.jpa.undeterminejdbctype.Employee>'");
出现异常的原因是 Hibernate 不知道如何将 employees
字段映射到数据库。要解决这个问题,需要正确注解 Department
实体中的一对多关系。
为 Department
实体添加必要的注解:
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
private List<Employee> employees = new ArrayList<>();
// Getter / Setter 方法省略
}
我们经常在 Department
类中添加 @OneToMany
关系,但忘记在 Employee
类中用 @ManyToOne
关系进行注解,导致此类错误。以下是如何正确注解关系的双方:
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
// Getter / Setter 方法省略
}
这种设置可确保 Hibernate 能正确映射 Department
实体中的 employees
字段和 Employee
实体中的 department
字段,从而避免发生 PersistenceException
并实现正确的数据库交互。
5、总结 {#5总结}
本文介绍了 Hibernate 中出现 "could not determine recommended JdbcType for class" 异常的原因,以及如何解决该异常。
Ref:https://www.baeldung.com/jpa-could-not-determine-recommended-jdbctype-for-class