0

I try to use postgres custom enums with Hibernate/JPA in Play Framework 2.5.0.

So basically I added a custom UserType and added a @Type annotation pointing to the specific java type / converter. Also, I tried to use JPA 2.1 @Convert and @Converter annotations. I did the same with @Enumerated and @Type (org.hibernate.type.EnumType) annotations. But, Any of it did not worked for me!

It seems like the annotations are not reconized because in the printstacktrace i can´t see the convert and the type classes.

SQL Code

CREATE TYPE auth_service_provider AS ENUM ('faceboook', 'google', 'linkedin', 'twitter');

CREATE TABLE "auth"(
   ...
   "auth_service" auth_service_provider NOT NULL,
   "auth_username" VARCHAR(128) NOT NULL,
   ...
);

Auth Entity

@Entity
@Table(name = "auth", schema = "public", uniqueConstraints = @UniqueConstraint(columnNames = { "auth_id","user_refid" }) )
public class Auth implements java.io.Serializable {

public static enum AuthServiceProvider {
    @JsonProperty("google")     google,
    @JsonProperty("twitter")    twitter,
    @JsonProperty("facebook")   facebook,
    @JsonProperty("linkedin")   linkedin;
};

@Convert(converter = AuthServiceConverter.class)
//@Type(type="AuthServiceType") 
//@Enumerated(EnumType.STRING)
//@Type(type = "org.hibernate.type.EnumType",
//parameters = {
//        @Parameter(name  = "enumClass", value = "AuthServiceType"),
//        @Parameter(name = "type", value = "1111"),
//        @Parameter(name = "useNamed", value = "true")
//})
private AuthServiceProvider authService;
private String authUsername;

...
//getters and setters
}

AuthServiceType

public class AuthServiceType implements UserType,Serializable { 

private static final long serialVersionUID = 4378790812145778372L;

private static final int[] SQL_TYPES = new int[]{Types.OTHER};

@Override
public int[] sqlTypes() {
    return SQL_TYPES;
}

@Override
public Object assemble(Serializable cached, Object owner) throws HibernateException {
    return deepCopy(cached);
}

@Override
public Object deepCopy(Object value) throws HibernateException {

    if (value == null) return value;
    try {
        return AuthServiceProvider.valueOf(((AuthServiceProvider)value).name());
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

@Override
public Serializable disassemble(Object value) throws HibernateException {
    return ((AuthServiceProvider)value).toString();
}

@Override
public boolean equals(Object x, Object y) throws HibernateException {
        if(x == y)
            return true;
        if(x == null || y == null)
            return false;
        return ((AuthServiceProvider) x).name() == ((AuthServiceProvider) y).name();
}

@Override
public int hashCode(Object value) throws HibernateException {
    return ((AuthServiceProvider)value).toString().hashCode();
}

@Override
public boolean isMutable() {
    return true;
}

@Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
        throws HibernateException, SQLException {
    Logger.debug("nullSafeGet: name {}",names[0]);

    if (rs.wasNull()) {
        return null;
    }

    Object identifier = rs.getObject(names[0]);

    if (identifier instanceof PGobject) {eturn getValueOfMethod().invoke(getMappedClass(), new Object[] { (PGobject) identifier).getValue() });
        return AuthServiceProvider.valueOf(((PGobject) identifier).getValue());
    } else {
        throw new IllegalArgumentException("AuthServiceType expected PGobject, received " + identifier.getClass().getName() + " with value of '" + identifier + "'");
    }

}

@Override
public void nullSafeSet(PreparedStatement stmt, Object value, int index, SessionImplementor session)
        throws HibernateException, SQLException {
    Logger.debug("nullSafeSet: value {}",value);
    if (value == null) {
        stmt.setNull(index, SQL_TYPES[0]);
    } else {
        PGobject pg = new PGobject();
        pg.setType("auth_service_provider");
        try {
            pg.setValue( value.toString() );
        } catch (SQLException e) {
            throw new IllegalArgumentException("Value not expected for PGobject: "+value.toString(),e);
        }
        stmt.setObject(index, pg,SQL_TYPES[0]);
    }
}


@Override
public Object replace(Object original, Object target, Object owner) throws HibernateException {
    return deepCopy(original);
}

@Override
@SuppressWarnings("unchecked")
public Class returnedClass() {
    return AuthServiceProvider.class;
}

}

AuthServiceConverter

@Converter
public class AuthServiceConverter implements AttributeConverter<AuthServiceProvider,PGobject> {

@Override
public PGobject convertToDatabaseColumn(AuthServiceProvider value) {
    Logger.debug("convertToDatabase");
    PGobject pg = new PGobject();
    pg.setType("auth_service_provider");
    try {
        pg.setValue( value.toString() );
    } catch (SQLException e) {
        throw new IllegalArgumentException("Value not expected for PGobject: "+value.toString(),e);
    }
    return pg;
}

@Override
public AuthServiceProvider convertToEntityAttribute(PGobject value) {
    Logger.debug("convertToEntityAttribute");
    return AuthServiceProvider.valueOf(value.getValue().toUpperCase());
}


}

Exception

Caused by: org.postgresql.util.PSQLException: ERROR: column "auth_service" is of type auth_service_provider but expression is of type integer
Hint: You will need to rewrite or cast the expression.
Position: 134

EDIT

I found the mistake. I had @Type annotation in the field declaration and @Column annotation in the getter method. The EnumType and Converts didn't work, but Hibernate @Type annotation worked, so the annotation should be written in my getter methods join with the @Column annotation.

EXAMPLE

@Column(name = "auth_service", nullable = false)
@Type(type="AuthServiceType")
public AuthServiceProvider getAuthService() {
    return this.authService;
}

1 Answer 1

1

Try

@Column(columnDefinition="auth_service_provider")
@ColumnTransformer(read="auth_service::varchar", write="?::auth_service_provider")
@Enumerated(EnumType.STRING)
private AuthServiceProvider authService;
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.