1. 引言
在本教程中,我們將探討常見 <em>PSQLException</em> 錯誤:“列的數據類型為 json,但表達式的數據類型為 character varying” 當使用 JPA 與 PostgreSQL 交互時。 我們將探討該錯誤發生的原因,識別觸發該錯誤的一些常見場景,並演示如何解決它。
2. 常見原因
在 PostgreSQL 中,<em >JSON</em> 或 <em >JSONB</em> 數據類型用於存儲 JSON 數據。然而,如果嘗試將字符串(character varying)插入到期望存儲 JSON 的列中,PostgreSQL 會拋出“<em >列的數據類型為 json,但表達式的數據類型為 character varying</em>” 錯誤。這在與 JPA 和 PostgreSQL 一起工作時尤其常見,因為 JPA 可能會嘗試將字符串保存到 JSON 列中,從而導致此錯誤。
3. 演示錯誤
我們將創建一個包含必要的依賴項和測試數據,用於演示錯誤的簡單 Spring Boot 項目。首先,我們需要將 PostgreSQL 依賴項添加到我們的 Maven pom.xml 文件中:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.1</version>
<scope>runtime</scope>
</dependency>接下來,我們創建一個 JPA 實體類,將其映射到 student 表:
@Entity
@Table(name = "student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String admitYear;
@Column(columnDefinition = "json")
private String address;
// getters and setters
}在實體類中,address 字段映射到 student 表中的 address 列。 值得注意的是,我們指定了 columnDefinition 屬性為 JSON,以指示該列的類型為 JSON。
現在,讓我們嘗試將一個 Student 對象保存到數據庫中:
Student student = new Student();
student.setAdmitYear("2024");
student.setAddress("{\"postCode\": \"TW9 2SF\", \"city\": \"London\"}");
Throwable throwable = assertThrows(Exception.class, () -> studentRepository.save(student));
assertTrue(ExceptionUtils.getRootCause(throwable) instanceof PSQLException);在這段代碼中,我們創建了一個 Student 對象並將 address 字段設置為 JSON 字符串。 然後,我們使用 studentRepository 對象中的 save() 方法將此對象保存到數據庫。
然而,這導致 PSQLException。
Caused by: org.postgresql.util.PSQLException: ERROR: column "address" is of type json but expression is of type character varying此錯誤發生的原因是,JPA 嘗試將字符串保存到 JSON 列中,這被禁止。
4. 使用 @Type 註解
為了解決此問題,我們需要正確處理 JSON 類型。 我們可以使用 @Type 註解,該註解由 hibernate-types 庫提供。 首先,讓我們將 hibernate-types 依賴添加到我們的 pom.xml 中:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.18.0</version>
</dependency>接下來,我們更新實體,使其包含 @TypeDef 和 @Type 註解:
@Entity
@Table(name = "student_json")
@TypeDefs({
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
public class StudentWithTypeAnnotation {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String admitYear;
@Type(type = "jsonb")
@Column(columnDefinition = "json")
private String address;
// Getters and Setters
}這裏,,它使用了來自 hibernate-types-52 庫的 處理 PostgreSQL 的 的形式高效地存儲和檢索。
, 我們告訴 Hibernate 使用通過 。 這個自定義類型處理 Java 對象與 PostgreSQL 中 數據類型高效地存儲和檢索:
StudentWithTypeAnnotation student = new StudentWithJson();
student.setAdmitYear("2024");
student.setAddress("{\"postCode\": \"TW9 2SF\", \"city\": \"London\"}");
studentWithTypeAnnotationRepository.save(student);
StudentWithTypeAnnotation retrievedStudent = studentWithTypeAnnotationRepository.findById(student.getId()).orElse(null);
assertThat(retrievedStudent).isNotNull();
assertThat(retrievedStudent.getAddress()).isEqualTo("{\"postCode\":\"TW9 2SF\",\"city\":\"London\"}");5. 本地查詢
此外,當我們使用帶有本地 SQL 查詢的 @Query 註解將 JSON 數據插入到 PostgreSQL 表中時,也會遇到相同的錯誤。 讓我們通過創建一個本地查詢來演示這個錯誤:
@Query(value = "INSERT INTO student (admit_year, address) VALUES (:admitYear, :address) RETURNING *", nativeQuery = true)
Student insertJsonData(@Param("admitYear") String admitYear, @Param("address") String address);當我們用 JSON 字符串調用此方法時,我們期望會收到異常。
Throwable throwable = assertThrows(Exception.class, () ->
studentRepository.insertJsonData("2024","{\"postCode\": \"TW9 2SF\", \"city\": \"London\"}"));
assertTrue(ExceptionUtils.getRootCause(throwable) instanceof PSQLException);要解決這個問題,我們需要在插入之前將 JSON 字符串轉換為 JSONB 類型,以避免出現此錯誤。
以下是一個示例,説明如何執行此操作:
public interface StudentWithTypeAnnotationRepository extends JpaRepository<StudentWithTypeAnnotation, Long> {
@Query(value = "INSERT INTO student (admit_year, address) VALUES (:admitYear, CAST(:address AS JSONB)) RETURNING *", nativeQuery = true)
StudentWithTypeAnnotation insertJsonData(@Param("admitYear") String admitYear, @Param("address") String address);
}在上述代碼中,我們使用 <em>CAST(:address AS JSONB)</em> 語法將 <em>:address</em> 參數轉換為 <em>JSONB</em> 類型。現在,讓我們測試此方法:
StudentWithTypeAnnotation student = studentWithJsonRepository.insertJsonData("2024","{\"postCode\": \"TW9 2SF\", \"city\": \"London\"}");
StudentWithTypeAnnotation retrievedStudent = studentWithJsonRepository.findById(student.getId()).orElse(null);
assertThat(retrievedStudent).isNotNull();
assertThat(retrievedStudent.getAddress()).isEqualTo("{\"city\": \"London\", \"postCode\": \"TW9 2SF\"}");6. 結論
在本文中,我們探討了如何解決 PSQLException 錯誤“列的數據類型為 json,但表達式為 character varying” 的問題,該問題在通過 JPA 將 Java 對象映射到 PostgreSQL JSON 列時出現。
通過使用 @Type 註解並使用原生 SQL 查詢時將 JSON 字符串轉換為 JSONB 類型,我們可以高效地在 PostgreSQL 中存儲和檢索 JSON 數據,利用 JSONB 數據類型。