修正 JPA 中的「無法確定類別的推薦 JdbcType」錯誤
一、簡介
在本教程中,我們將探討 Hibernate 中「 could not determine recommended JdbcType for class
」錯誤的原因,並討論如何解決它。
2. 常見原因
當我們遇到此錯誤訊息時,通常是因為 JPA(Java Persistence API)拋出了異常。當 JPA 無法確定類別的建議 JDBC 類型時,就會發生這種情況。通常,這種情況發生在 Hibernate 應用程式啟動期間,此時 Hibernate 嘗試建立資料庫模式或驗證對應。
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;
private Map<String, String> studentDetails;
// getters and setters
}
在上面的範例中, 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 資料類型將地圖儲存為 JSON 物件。
首先,我們需要將Jackson依賴項新增到pom.xml
檔案中:
<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;
// getters and setters
}
透過新增@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關係映射
JPA 依靠註解來理解實體及其欄位類型之間的關係。省略這些註解可能會導致 Hibernate 無法確定所涉及欄位的建議 JDBC 類型,從而導致錯誤。
讓我們探討兩種常見的關係映射: @OneToOne
和@OneToMany
,並了解缺少註釋會如何導致問題。
4.1.一對一映射
我們將示範一個場景,其中Employee
實體與Address
實體具有一對一關係。如果我們忘記註解這種關係,Hibernate 可能會拋出PersistenceException
因為它無法確定Address
欄位的適當 JDBC 類型:
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Address address;
// setter and getter
}
在這種情況下,當我們嘗試初始化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;
// setters and getters
}
透過新增@OneToOne
註釋,我們指示 Hibernate 處理Employee
和Address
之間的關係。現在,Hibernate 知道Employee
實體中的address
欄位已對應到Address
實體,並且它使用Employee
表中的address_id
欄位來建立這種關係。
4.2.一對多映射
與一對一映射類似,在 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<>();
// setters and getters
}
此設定會在初始化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<>();
// setters and getters
}
我們常在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;
// setters and getters
}
此設定可確保 Hibernate 可以正確地對應Department
實體中的employees
欄位和Employee
實體中的department
字段,避免PersistenceException
並啟用正確的資料庫互動。
5. 結論
在本文中,我們探討了 Hibernate 中「 could not determine recommended JdbcType for class
」錯誤的原因,並討論如何解決它。我們已經了解了複雜的資料類型和關係映射如何導致此錯誤,並研究了防止該錯誤的解決方案。
與往常一樣,範例的原始程式碼可在 GitHub 上取得。