[英]How to map JAVA enum to Postgresql Enum using Hibernate/JPA?
[英]How to map PostgreSQL enum with JPA and Hibernate
我正在嘗試將名為傳輸結果的 postgres 自定義類型映射到 Hibernate/JPA POJO。 postgres 自定義類型或多或少是字符串值的枚舉類型。
我創建了一個名為 PGEnumUserType 的自定義 EnumUserType 以及一個表示 postgres 枚舉值的枚舉類。 當我針對真實數據庫運行此程序時,收到以下錯誤:“錯誤:列“狀態”的類型為傳輸結果,但表達式的類型為字符變化 提示:您需要重寫或轉換表達式。 位置:135'
看到這一點,我想我需要將我的 SqlTypes 更改為 Types.OTHER。 但是這樣做會破壞我的集成測試(在內存數據庫中使用 HyperSQL)並顯示以下消息:'Caused by: java.sql.SQLException: Table not found in statement [selectenrollment0_."id" as id1_47_0_,enrollment0_."tpa_approval_id" as tpa2_47_0_ ,enrollment0_."tpa_status_code" as tpa3_47_0_,enrollment0_."status_message" 作為 status4_47_0_,enrollment0_."approval_id" 作為批准5_47_0_,enrollment0_."transmission_date" 作為 transmis6_47_0_,enrollment0_作為傳輸_47_0_,"status_40_8"transmission_4itter0"status_7"statter0"status_7"傳輸“注冊0_其中注冊0_.“id”=?]'
我不確定為什么更改 sqlType 會導致此錯誤。 任何幫助表示贊賞。
JPA/休眠實體:
@Entity
@Access(javax.persistence.AccessType.PROPERTY)
@Table(name="transmissions")
public class EnrollmentCycleTransmission {
// elements of enum status column
private static final String ACCEPTED_TRANSMISSION = "accepted";
private static final String REJECTED_TRANSMISSION = "rejected";
private static final String DUPLICATE_TRANSMISSION = "duplicate";
private static final String EXCEPTION_TRANSMISSION = "exception";
private static final String RETRY_TRANSMISSION = "retry";
private Long transmissionID;
private Long approvalID;
private Long transmitterID;
private TransmissionStatusType transmissionStatus;
private Date transmissionDate;
private String TPAApprovalID;
private String TPAStatusCode;
private String TPAStatusMessage;
@Column(name = "id")
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Long getTransmissionID() {
return transmissionID;
}
public void setTransmissionID(Long transmissionID) {
this.transmissionID = transmissionID;
}
@Column(name = "approval_id")
public Long getApprovalID() {
return approvalID;
}
public void setApprovalID(Long approvalID) {
this.approvalID = approvalID;
}
@Column(name = "transmitter")
public Long getTransmitterID() {
return transmitterID;
}
public void setTransmitterID(Long transmitterID) {
this.transmitterID = transmitterID;
}
@Column(name = "status")
@Type(type = "org.fuwt.model.PGEnumUserType" , parameters ={@org.hibernate.annotations.Parameter(name = "enumClassName",value = "org.fuwt.model.enrollment.TransmissionStatusType")} )
public TransmissionStatusType getTransmissionStatus() {
return this.transmissionStatus ;
}
public void setTransmissionStatus(TransmissionStatusType transmissionStatus) {
this.transmissionStatus = transmissionStatus;
}
@Column(name = "transmission_date")
public Date getTransmissionDate() {
return transmissionDate;
}
public void setTransmissionDate(Date transmissionDate) {
this.transmissionDate = transmissionDate;
}
@Column(name = "tpa_approval_id")
public String getTPAApprovalID() {
return TPAApprovalID;
}
public void setTPAApprovalID(String TPAApprovalID) {
this.TPAApprovalID = TPAApprovalID;
}
@Column(name = "tpa_status_code")
public String getTPAStatusCode() {
return TPAStatusCode;
}
public void setTPAStatusCode(String TPAStatusCode) {
this.TPAStatusCode = TPAStatusCode;
}
@Column(name = "status_message")
public String getTPAStatusMessage() {
return TPAStatusMessage;
}
public void setTPAStatusMessage(String TPAStatusMessage) {
this.TPAStatusMessage = TPAStatusMessage;
}
}
自定義枚舉用戶類型:
public class PGEnumUserType implements UserType, ParameterizedType {
private Class<Enum> enumClass;
public PGEnumUserType(){
super();
}
public void setParameterValues(Properties parameters) {
String enumClassName = parameters.getProperty("enumClassName");
try {
enumClass = (Class<Enum>) Class.forName(enumClassName);
} catch (ClassNotFoundException e) {
throw new HibernateException("Enum class not found ", e);
}
}
public int[] sqlTypes() {
return new int[] {Types.VARCHAR};
}
public Class returnedClass() {
return enumClass;
}
public boolean equals(Object x, Object y) throws HibernateException {
return x==y;
}
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
String name = rs.getString(names[0]);
return rs.wasNull() ? null: Enum.valueOf(enumClass,name);
}
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, Types.VARCHAR);
}
else {
st.setString(index,((Enum) value).name());
}
}
public Object deepCopy(Object value) throws HibernateException {
return value;
}
public boolean isMutable() {
return false; //To change body of implemented methods use File | Settings | File Templates.
}
public Serializable disassemble(Object value) throws HibernateException {
return (Enum) value;
}
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return cached;
}
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}
public Object fromXMLString(String xmlValue) {
return Enum.valueOf(enumClass, xmlValue);
}
public String objectToSQLString(Object value) {
return '\'' + ( (Enum) value ).name() + '\'';
}
public String toXMLString(Object value) {
return ( (Enum) value ).name();
}
}
枚舉類:
public enum TransmissionStatusType {
accepted,
rejected,
duplicate,
exception,
retry}
我想到了。 我需要在 nullSafeSet 函數中使用 setObject 而不是 setString 並傳入 Types.OTHER 作為 java.sql.type 讓 jdbc 知道它是一個 postgres 類型。
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, Types.VARCHAR);
}
else {
// previously used setString, but this causes postgresql to bark about incompatible types.
// now using setObject passing in the java type for the postgres enum object
// st.setString(index,((Enum) value).name());
st.setObject(index,((Enum) value), Types.OTHER);
}
}
如果您在 PostgreSQL 中有以下post_status_info
枚舉類型:
CREATE TYPE post_status_info AS ENUM (
'PENDING',
'APPROVED',
'SPAM'
)
您可以使用以下自定義 Hibernate Type 輕松將 Java Enum 映射到 PostgreSQL Enum 列類型:
public class PostgreSQLEnumType extends org.hibernate.type.EnumType {
public void nullSafeSet(
PreparedStatement st,
Object value,
int index,
SharedSessionContractImplementor session)
throws HibernateException, SQLException {
if(value == null) {
st.setNull( index, Types.OTHER );
}
else {
st.setObject(
index,
value.toString(),
Types.OTHER
);
}
}
}
要使用它,您需要使用 Hibernate @Type
注釋來注釋該字段,如下例所示:
@Entity(name = "Post")
@Table(name = "post")
@TypeDef(
name = "pgsql_enum",
typeClass = PostgreSQLEnumType.class
)
public static class Post {
@Id
private Long id;
private String title;
@Enumerated(EnumType.STRING)
@Column(columnDefinition = "post_status_info")
@Type( type = "pgsql_enum" )
private PostStatus status;
//Getters and setters omitted for brevity
}
以下內容也可能有助於讓 Postgres 以靜默方式將字符串轉換為您的 SQL 枚舉類型,以便您可以使用@Enumerated(STRING)
而不需要@Type
。
CREATE CAST (character varying as post_status_type) WITH INOUT AS IMPLICIT;
一個快速的解決方案是
jdbc:postgresql://localhost:5432/postgres?stringtype=unspecified
?stringtype=unspecified是答案
dependencies {
api("javax.persistence", "javax.persistence-api", "2.2")
api("org.hibernate", "hibernate-core", "5.4.21.Final")
}
在 Kotlin 中,使用EnumType<Enum<*>>()
進行通用擴展很重要
import org.hibernate.type.EnumType
import java.sql.Types
class PostgreSQLEnumType : EnumType<Enum<*>>() {
@Throws(HibernateException::class, SQLException::class)
override fun nullSafeSet(
st: PreparedStatement,
value: Any,
index: Int,
session: SharedSessionContractImplementor) {
st.setObject(
index,
value.toString(),
Types.OTHER
)
}
}
import org.hibernate.annotations.Type
import org.hibernate.annotations.TypeDef
import javax.persistence.*
@Entity
@Table(name = "custom")
@TypeDef(name = "pgsql_enum", typeClass = PostgreSQLEnumType::class)
data class Custom(
@Id @GeneratedValue @Column(name = "id")
val id: Int,
@Enumerated(EnumType.STRING) @Column(name = "status_custom") @Type(type = "pgsql_enum")
val statusCustom: StatusCustom
)
enum class StatusCustom {
FIRST, SECOND
}
我不推薦的一個更簡單的選項是Arthur's answer 中的第一個選項,它在連接 URL 中向數據庫添加一個參數,以便枚舉數據類型不會丟失。 我認為在后端服務器和數據庫之間映射數據類型的責任正是后端。
<property name="connection.url">jdbc:postgresql://localhost:5432/yourdatabase?stringtype=unspecified</property>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.