2011-09-29 103 views
6

我想將postgres自定義類型,名爲transmission_result,映射到Hibernate/JPA POJO。 postgres自定義類型或多或少是一個枚舉類型的字符串值。試圖將Postgres枚舉映射到Hibernate/JPA pojo

我已經創建了一個名爲PGEnumUserType的自定義EnumUserType以及一個表示postgres枚舉值的枚舉類。當我針對真實數據庫運行此操作時,出現以下錯誤: '錯誤:列「狀態」屬於transmission_result類型,但表達式的類型是字符變化的 提示:您需要重寫或轉換表達式。 位置:135'

看到這個,我想我需要將我的SqlTypes更改爲Types.OTHER。但是這樣做會破壞我的集成測試(在內存數據庫中使用HyperSQL),並顯示以下消息: '由於:java.sql.SQLException:在語句中未找到表[select enrollment0 _。「id」as id1_47_0_,enrollment0 _。「tpa_approval_id」as tpa2_47_0_,enrollment0 _。「tpa_status_code」as tpa3_47_0_,enrollment0 _。「status_message」as status4_47_0_,enrollment0 _。「approval_id」as approval5_47_0_,enrollment0 _。「transmission_date」as transmis6_47_0_,enrollment0 _。「status」as status7_47_0_,enrollment0 _。「transmitter」as transmit8_47_0_ from 「transmissions」enrollment0_ where enrollment0 _。「id」=?]'

我不知道爲什麼在這個錯誤中更改sqlType結果。任何幫助表示讚賞。

JPA/Hibernate的實體:

@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; 
} 
} 

定製EnumUserType:

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} 

回答

9

我想通了。我需要在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); 
    } 
} 
0

正如我explained in this article,假設你有以下PostgreSQL中post_status_info枚舉類型:

CREATE TYPE post_status_info AS ENUM (
    'PENDING', 
    'APPROVED', 
    'SPAM' 
) 

您可以輕鬆地使用下面的自定義Hibernate類型的Java枚舉映射到PostgreSQL枚舉列類型

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 
} 

就是這樣,它就像一個魅力。這是一個test on GitHub that proves it