2011-09-29 10 views
6

Sto tentando di mappare un tipo personalizzato postgres, denominato transmission_result, in un POJO Hibernate/JPA. Il tipo personalizzato postgres è più o meno un tipo enum di valori stringa.Tentativo di mappare postgres enum in Hibernate/JPA pojo

Ho creato un oggetto personalizzato EnumUserType denominato PGEnumUserType nonché una classe enum che rappresenta i valori enumerati postgres. Quando eseguo questa operazione su un database reale, viene visualizzato il seguente errore: 'ERRORE: la colonna "stato" è di tipo transmission_result ma l'espressione è di tipo carattere variabile Suggerimento: è necessario riscrivere o trasmettere l'espressione. Posizione: 135 '

Nel vedere questo, ho pensato di aver bisogno di cambiare i miei SqlTypes in Tipi.ALTRO. Ma così facendo si rompono i miei test di integrazione (usando HyperSQL nel DB di memoria) con il messaggio: 'Causato da: java.sql.SQLException: tabella non trovata nell'istruzione [select enrollment0 _. "Id" come id1_47_0_, enrollment0 _. "Tpa_approval_id" as tpa2_47_0_, enrollment0 _. "tpa_status_code" come tpa3_47_0_, enrollment0 _. "status_message" come stato4_47_0_, enrollment0 _. "approval_id" come approvazione5_47_0_, enrollment0 _. "transmission_date" come transmis6_47_0_, enrollment0 _. "status" come status7_47_0_, enrollment0 _. "trasmettitore" come transmit8_47_0_ da "trasmissioni" iscrizione0_ dove iscrizione0 _. "id" =?] '

Non sono sicuro del motivo per cui il risultato sqlType viene modificato in questo errore. Qualsiasi aiuto è apprezzato.

JPA/Hibernate Entità:

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

personalizzato 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(); 
} 
} 

classe Enum:

public enum TransmissionStatusType { 
accepted, 
rejected, 
duplicate, 
exception, 
retry} 

risposta

9

ho capito. Avevo bisogno di usare setObject invece di setString nella funzione nullSafeSet e passare in Types.OTHER come java.sql.type per far sapere a jdbc che si trattava di un tipo 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

Come ho explained in this article, supponendo che siano presenti i seguenti post_status_info tipo enum in PostgreSQL:

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

Si può facilmente mappare Java Enum per un tipo di colonna PostgreSQL Enum utilizzando il seguente personalizzato Hibernate Tipo:

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

Per utilizzarlo, è necessario annotare il campo con l'annotazione Hibernate @Type come illustrato nell'esempio seguente:

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

Ecco, funziona come un fascino. Ecco uno test on GitHub that proves it.

Problemi correlati