Scenario: A data object which persists in the DB table. There are some old entries in the table. Now I have to apply encryption to new further entries in the table. So I add a new column which has the field encrypted set to False by default to check if the values are encrypted.
Problem: I want to write an annotation to encrypt the fields in the data model(POJO) before persisting and decrypt on getter() calls only if it is encrypted.
Context:
The user model.
public class UserData {
#Id
#Column(name = "ID", length = 36)
private String id;
#Column(name = "IS_ENCRYPTED")
private boolean isEncrypted;
#Column(name = "NAME")
#Convert(converter = EncryptionConverter.class)
private String name;
// more fields ....
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
// more similar getter and setters
}
The encryption class that i have written.
#Converter
public class EncryptionConverter implements AttributeConverter<String, String>{
private final String secretKey= "someSecret";
UserData Data = new UserData();
#Override
public String convertToDatabaseColumn(String str) {
if(!isNullOrBlank(str))
return AesEncrypt.encrypt(str, secretKey);
return str;
}
#Override
public String convertToEntityAttribute(String encrypedStr) {
if(!isNullOrBlank(encrypedStr) && Data.isEncrypted)
return AesEncrypt.decrypt(encrypedStr, secretKey);
return encrypedStr;
}
}
This class is inside the model class. (can move outside, but how to pass isencrypted flag to annotation)
How can I do this, is my approach correct?
Edit: there are multiple fields which are to be encrypted/decrypted not just name.
You can create the encryption behaviour in another configuration class, say EncryptedPropertyConfig, in this you can create a bean, EncryptablePropertyResolver from jasypt-spring-boot
#EnableAutoConfiguration
public class EncryptedPropertyConfig {
public EncryptedPropertyConfig() {
}
#Bean
public EncryptablePropertyResolver encryptablePropertyResolver() {
EncryptablePropertyResolver r = new MyPropertyPlaceholderConfigurer();
return r;
}
}
public final class MyPropertyPlaceholderConfigurer implements EncryptablePropertyResolver {
private StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
private EnvironmentStringPBEConfig envConfig = new EnvironmentStringPBEConfig();
public MyPropertyPlaceholderConfigurer() {
// set the encryption key and config
}
public String resolvePropertyValue(String passedValue) {
if (!PropertyValueEncryptionUtils.isEncryptedValue(passedValue)) {
return passedValue;
} else {
String returnValue = "";
try {
returnValue = PropertyValueEncryptionUtils.decrypt(passedValue, this.encryptor);
return returnValue;
} catch (Exception var4) {
throw new RuntimeException("Error in decryption of property value:" + passedValue, var4);
}
}
}
}
I suggest alternative solution using Entity Listeners
import javax.persistence.PostLoad;
import javax.persistence.PreUpdate;
public class UserData {
private final String secretKey= "someSecret";
// ...
#PreUpdate
private void onUpdate() {
// triggered before saving entity to DB (both create & update)
if(!isNullOrBlank(name)) {
name = AesEncrypt.encrypt(name, secretKey);
}
}
#PostLoad
private void onLoad() {
// triggered after entity is fetched from Entity Provider
if (!isNullOrBlank(name) && isEncrypted) {
name = AesEncrypt.decrypt(name, secretKey);
}
}
}
Instead of using JPA AttributeConverter you can implement hibernate user type in this way:
import java.util.Objects;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.StringType;
import org.hibernate.usertype.UserType;
public class CustomNameType implements UserType
{
private String secretKey = "someSecret";
public CustomNameType()
{
}
#Override
public Object deepCopy(Object value) throws HibernateException
{
if (null == value) return null;
return ((CustomName) value).clone();
}
#Override
public Object assemble(Serializable cached, Object owner) throws HibernateException
{
return cached;
}
#Override
public Serializable disassemble(Object value) throws HibernateException
{
return (Serializable) value;
}
#Override
public Object replace(Object original, Object target, Object owner) throws HibernateException
{
return original;
}
#Override
public boolean equals(Object one, Object two) throws HibernateException
{
return Objects.equals(one, two);
}
#Override
public int hashCode(Object obj) throws HibernateException
{
return Objects.hashCode(obj);
}
#Override
public boolean isMutable()
{
return true;
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
throws HibernateException, SQLException
{
boolean isEncrypted = rs.getBoolean(0); // IS_ENCRYPTED
String name = rs.getString(1); // NAME
if (isEncrypted) {
name = AesEncrypt.decrypt(name, secretKey);
}
return new CustomName(isEncrypted, name);
}
#Override
public void nullSafeSet(PreparedStatement statement, Object value, int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException
{
CustomName customName = (CustomName) value;
String name = customName.getName();
if (customName.isEncrypted()) {
name = AesEncrypt.encrypt(name, secretKey);
}
statement.setBoolean(0, customName.isEncrypted());
statement.setString(1, name);
}
#Override
public Class<?> returnedClass()
{
return CustomName.class;
}
#Override
public int[] sqlTypes()
{
// I do not know the types of your IS_ENCRYPTED and NAME fields
// So, this place maybe require correction
int[] types = {BooleanType.INSTANCE.sqlType(), StringType.INSTANCE.sqlType()};
return types;
}
}
where CustomName is:
public class CustomName implements Serializable, Cloneable
{
private boolean isEncrypted;
private String name;
public CustomName(boolean isEncrypted, String name)
{
this.isEncrypted = isEncrypted;
this.name = name;
}
// getters , equals, hashCode ...
#Override
public CustomName clone()
{
return new CustomName(isEncrypted, name);
}
}
and then use it:
import org.hibernate.annotations.Type;
import org.hibernate.annotations.Columns;
#Entity
public class UserData {
#Type(type = "com.your.CustomNameType")
#Columns(columns = {
#Column(name = "IS_ENCRYPTED"),
#Column(name = "NAME")
})
private CustomName name;
}
Related
If I have a AttributeConverter that changes the length of the input before persisting it in the database, what would be the correct way to make sure that the modified input doesn't exceed the maximum length allowed by that column (without hardcoding it in the converter) ?
#Column(length = 1024)
#Convert(converter = MyConverter.class)
private String comment;
public class MyConverter implements AttributeConverter<String, String> {
#Override
public String convertToDatabaseColumn(String attribute) {
return "hello world " + attribute;
}
...
}
You can achieve what you want with hibernate custom basic type that implements DynamicParameterizedType interface.
Below you can see a simple example of declaration custom type that read the length property of #Column annotation.
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Objects;
import java.util.Properties;
import javax.persistence.Column;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.DynamicParameterizedType;
import org.hibernate.usertype.UserType;
public class PersistableString implements UserType, DynamicParameterizedType
{
private int sqlType;
private int columnLength;
public PersistableString()
{
this.sqlType = Types.VARCHAR;
}
#Override
public void setParameterValues(Properties parameters)
{
ParameterType reader = (ParameterType) parameters.get(PARAMETER_TYPE);
this.columnLength = getLength(reader);
}
private int getLength(ParameterType reader)
{
int length = -1; // default length
for (Annotation annotation : reader.getAnnotationsMethod()){
if (annotation instanceof Column) {
length = ((Column) annotation).length();
}
}
return length;
}
#Override
public int[] sqlTypes()
{
return new int[] {sqlType};
}
#Override
public Class<?> returnedClass()
{
return String.class;
}
#Override
public boolean equals(Object x, Object y) throws HibernateException
{
return Objects.equals(x, y);
}
#Override
public int hashCode(Object x) throws HibernateException
{
return Objects.hashCode(x);
}
/*
This method will be called when hibernate initializes your entity's
field from the appropriate database table row
*/
#Override
public Object nullSafeGet(ResultSet rs,
String[] names,
SharedSessionContractImplementor session,
Object owner) throws HibernateException, SQLException
{
// you can use this.columnLength here
return rs.getString(names[0]);
}
/*
This method will be called when hibernate persists your entity's field
to the appropriate database table row
*/
#Override
public void nullSafeSet(PreparedStatement st,
Object value,
int index,
SharedSessionContractImplementor session) throws HibernateException, SQLException
{
// you can use this.columnLength here
if (value == null) {
st.setNull(index, sqlType);
}
else {
String val = (String) value;
st.setString(index, val);
}
}
#Override
public Object deepCopy(Object value) throws HibernateException
{
return value;
}
#Override
public boolean isMutable()
{
return false;
}
#Override
public Serializable disassemble(Object value) throws HibernateException
{
return Objects.toString(value);
}
#Override
public Object assemble(Serializable cached, Object owner) throws HibernateException
{
return cached;
}
#Override
public Object replace(Object original, Object target, Object owner) throws HibernateException
{
return original;
}
}
and usage:
import org.hibernate.annotations.Type;
#Entity
#Table
public class Account
{
#Column(name = "acc_name", length = 50)
#Type(type = "com.example.hibernate.PersistableString")
private String name;
#Column(name = "acc_pass", length = 30)
#Type(type = "com.example.hibernate.PersistableString")
private String pass;
#Column(name = "acc_email", length = 355)
#Type(type = "com.example.hibernate.PersistableString")
private String email;
}
I recently mapped a field of a class with a custom hibernate UserType.
this is my custom user type
package service.dao.hibernate;
import java.io.IOException;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.SimpleType;
import com.google.common.base.Objects;
public abstract class JSONUserType implements UserType { //ParameterizedType, Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final ObjectMapper Mapper;
private static final String CLASS_TYPE = "classType";
private static final String TYPE = "type";
private static final int[] SQL_TYPES = new int[] { Types.LONGVARCHAR,
Types.CLOB, Types.BLOB };
private Class classType;
private int sqlType = Types.LONGVARCHAR; // before any guessing
static {
Mapper = new ObjectMapper();
Mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
#Override
public Object assemble(Serializable cached, Object owner)
throws HibernateException {
return this.deepCopy(cached);
}
#Override
public Object deepCopy(Object value) throws HibernateException {
Object copy = null;
if (value != null) {
try {
return Mapper.readValue(Mapper.writeValueAsString(value),
this.classType);
} catch (IOException e) {
throw new HibernateException("unable to deep copy object", e);
}
}
return copy;
}
#Override
public Serializable disassemble(Object value) throws HibernateException {
try {
return Mapper.writeValueAsString(value);
} catch (JsonProcessingException e) {
throw new HibernateException("unable to disassemble object", e);
}
}
#Override
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y) {
return true;
} else if (x == null || y == null) {
return false;
} else {
return x.equals(y);
}
}
#Override
public int hashCode(Object x) throws HibernateException {
return null == x ? 0 : x.hashCode();
}
#Override
public boolean isMutable() {
return true;
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names,
SessionImplementor session, Object owner)
throws HibernateException, SQLException {
Object obj = null;
if (!rs.wasNull()) {
if (this.sqlType == Types.CLOB || this.sqlType == Types.BLOB) {
byte[] bytes = rs.getBytes(names[0]);
if (bytes != null) {
try {
obj = Mapper.readValue(bytes, createJavaType(Mapper));
} catch (IOException e) {
throw new HibernateException(
"unable to read object from result set", e);
}
}
} else {
try {
String content = rs.getString(names[0]);
if (content != null) {
obj = Mapper.readValue(content, createJavaType(Mapper));
}
} catch (IOException e) {
throw new HibernateException(
"unable to read object from result set", e);
}
}
}
return obj;
}
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
SessionImplementor session) throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, this.sqlType);
} else {
if (this.sqlType == Types.CLOB || this.sqlType == Types.BLOB) {
try {
st.setBytes(index, Mapper.writeValueAsBytes(value));
} catch (JsonProcessingException e) {
throw new HibernateException(
"unable to set object to result set", e);
}
} else {
try {
st.setString(index, Mapper.writeValueAsString(value));
} catch (JsonProcessingException e) {
throw new HibernateException(
"unable to set object to result set", e);
}
}
}
}
#Override
public Object replace(Object original, Object target, Object owner)
throws HibernateException {
return this.deepCopy(original);
}
// #Override
// public Class returnedClass() {
// return this.classType;
// }
#Override
public int[] sqlTypes() {
return SQL_TYPES;
}
// #Override
// public void setParameterValues(Properties params) {
// String classTypeName = params.getProperty(CLASS_TYPE);
// try {
// this.classType = ReflectHelper.classForName(classTypeName,
// this.getClass());
// } catch (ClassNotFoundException cnfe) {
// throw new HibernateException("classType not found", cnfe);
// }
// String type = params.getProperty(TYPE);
// if (type != null) {
// this.sqlType = Integer.decode(type).intValue();
// }
// }
/**
* By default we are expecting to use a simple object / not a collection (Set, List)
*
* #param mapper : instance jackson object mapper
*
* #return A jackson JavaType to specify wich object represent the json string representation
*
*/
public JavaType createJavaType (ObjectMapper mapper){
return SimpleType.construct(returnedClass());
}
}
this is the specific user type
package model.common;
import service.dao.hibernate.JSONUserType;
public class DocumentInfoType extends JSONUserType {
#Override
public Class returnedClass() {
return DocumentInfo.class;
}
}
Here is my entity with custom type field
package model.common;
import model.SimpleAuditedEntity;
import model.lk.DocumentMode;
import model.lk.DocumentType;
import service.dao.hibernate.JSONUserType;
import java.io.Serializable;
import javax.persistence.*;
import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
import java.sql.Timestamp;
/**
* The persistent class for the documents database table.
*
*/
#Entity
#Table(name = "documents")
#NamedQuery(name = "Document.findAll", query = "SELECT d FROM Document d")
public class Document extends SimpleAuditedEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "content_type")
private String contentType;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "type")
private DocumentType documentType;
#Column
private Timestamp created;
#Column
private String description;
#Column
private String filename;
#Column
private String name;
#Column
private String ref;
#Type(type = "model.common.DocumentInfoType")
#Column
private DocumentInfo info;
public Document() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getContentType() {
return this.contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public Timestamp getCreated() {
return this.created;
}
public void setCreated(Timestamp created) {
this.created = created;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public String getFilename() {
return this.filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getRef() {
return this.ref;
}
public void setRef(String ref) {
this.ref = ref;
}
/**
* #return the documentType
*/
public DocumentType getDocumentType() {
return documentType;
}
/**
* #param documentType
* the documentType to set
*/
public void setDocumentType(DocumentType documentType) {
this.documentType = documentType;
}
public DocumentMode getDocumentMode() {
return this.documentType != null ? DocumentMode
.getType(this.documentType.getId()) : DocumentMode.UNDEFINED;
}
/**
* #return the info
*/
public DocumentInfo getInfo() {
return info;
}
/**
* #param info the info to set
*/
public void setInfo(DocumentInfo info) {
this.info = info;
}
}
The problem is when I launch the application I get immediately the exception
Caused by: org.hibernate.MappingException: property mapping has wrong number of columns: model.common.Document.info type: model.common.DocumentInfoType
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:497) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.mapping.RootClass.validate(RootClass.java:270) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.cfg.Configuration.validate(Configuration.java:1360) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1851) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:852) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
Any idea about? I've mapped all the columns and I've tried many modifications but nothing!
Thanks in advance
You return an array of SQLTypes from JSONUserType.sqlTypes() that contains 3 elements:
private static final int[] SQL_TYPES = new int[] { Types.LONGVARCHAR,
Types.CLOB, Types.BLOB };
This tells hibernate that your type maps to 3 columns.
You should choose one of the types only.
See the javadoc for UserType.sqlTypes():
Return the SQL type codes for the columns mapped by this type
I got an entity with a column state. States stored in the DB are active and inactive (and some more). I wrote myself an enum like the following
public enum State {
ACTIVE("active"), INACTIVE("inactive");
private String state;
private State(String state) {
this.state = state;
}
}
The entity looks like:
#Entity
#Table(name = "TEST_DB")
public class MyEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
private Integer id;
#Enumerated(EnumType.STRING)
#Column(name = "STATE", nullable = false)
private Integer state;
// constructor, getter, setter
}
Unfortunately I get the following error message:
javax.ejb.EJBTransactionRolledbackException: Unknown name value [active] for enum class [state]
Is it possible to do a case-insensitive hibernate-mapping to an enum?
I was facing with similar problem and found simple answer.
You can do something like:
#Column(name = "my_type")
#ColumnTransformer(read = "UPPER(my_type)", write = "LOWER(?)")
#Enumerated(EnumType.STRING)
private MyType type;
(you don't need for "write" in #ColumnTransformer - for me it's for back compatibility, because my rows only in lower case. Without write Hibernate will write enum in same case, as in code in enum constant)
You can map an enum as an ORDINAL or a STRING with hibernate annotations, for example:
#Enumerated(EnumType.ORDINAL)
private State state;
The ordinal mapping puts the ordinal position of the enum in the database. If you change the order of the enum values in your code this will conflict with existing database state. The string mapping puts the upper case name of the enum in the database. If you rename an enum value, you get the same problem.
If you want to define a custom mapping (like your code above) you can create an implementation of org.hibernate.usertype.UserType which explicitly maps the enum.
First I suggest some changes to your enum to make what follows possible:
public enum State {
ACTIVE("active"), INACTIVE("inactive");
private String stateName;
private State(String stateName) {
this.stateName = stateName;
}
public State forStateName(String stateName) {
for(State state : State.values()) {
if (state.stateName().equals(stateName)) {
return state;
}
}
throw new IllegalArgumentException("Unknown state name " + stateName);
}
public String stateName() {
return stateName;
}
}
And here is a simple (!) implementation of UserType:
public class StateUserType implements UserType {
private static final int[] SQL_TYPES = {Types.VARCHAR};
public int[] sqlTypes() {
return SQL_TYPES;
}
public Class returnedClass() {
return State.class;
}
public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException {
String stateName = resultSet.getString(names[0]);
State result = null;
if (!resultSet.wasNull()) {
result = State.forStateName(stateName);
}
return result;
}
public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index) throws HibernateException, SQLException {
if (null == value) {
preparedStatement.setNull(index, Types.VARCHAR);
} else {
preparedStatement.setString(index, ((State)value).stateName());
}
}
public Object deepCopy(Object value) throws HibernateException{
return value;
}
public boolean isMutable() {
return false;
}
public Object assemble(Serializable cached, Object owner) throws HibernateException
return cached;
}
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable)value;
}
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y) {
return true;
}
if (null == x || null == y) {
return false;
}
return x.equals(y);
}
}
Then the mapping would become:
#Type(type="foo.bar.StateUserType")
private State state;
For another example of how to implement UserType, see: http://www.gabiaxel.com/2011/01/better-enum-mapping-with-hibernate.html
I have an enum class named Status as follows
public enum Status {
PENDING(0), SUCCESS(1), FAILED(-1);
private int st;
private Status(int st){
this.st = st;
}
}
and from other class I try to map this status enum
public void setStatus(Status status) {
this.status = status;
}
#Enumerated(EnumType.ORDINAL)
public Status getStatus() {
return status;
}
when I run this code, I get
java.lang.IllegalArgumentException: Unknown ordinal value for enum class data.Status: -1
at org.hibernate.type.EnumType.nullSafeGet(EnumType.java:93)
at org.hibernate.type.CustomType.nullSafeGet(CustomType.java:124)
at org.hibernate.type.AbstractType.hydrate(AbstractType.java:106)
at
but I already have -1 in enum definition.
You could define your own UserType which defines how Hibernate should map those enums.
Note that the ordinal defines the index of the enum value and thus FAILED would have the ordinal 2. To map the enum using its properties your need a UserType implementation.
Some links:
https://community.jboss.org/wiki/UserTypeForPersistingAnEnumWithAVARCHARColumn
http://javadata.blogspot.de/2011/07/hibernate-and-enum-handling.html (look at the "Paramterized Enumeration in Hibernate" section)
Here is a solution where a string label is used instead of an int id, however it is simple to adapt.
public class User {
#Id
private int id;
#Type(type = "com.example.hibernate.LabeledEnumType")
private Role role;
}
public enum Role implements LabeledEnum {
ADMIN("admin"), USER("user"), ANONYMOUS("anon");
private final String label;
Role(String label) {
this.label = label;
}
#Override
public String getLabel() {
return label;
}
}
public interface LabeledEnum {
String getLabel();
}
public final class LabeledEnumType implements DynamicParameterizedType, UserType {
private Class<? extends Enum> enumClass;
#Override
public Object assemble(Serializable cached, Object owner)
throws HibernateException {
return cached;
}
#Override
public Object deepCopy(Object value) throws HibernateException {
return value;
}
#Override
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable) value;
}
#Override
public boolean equals(Object x, Object y) throws HibernateException {
return x == y;
}
#Override
public int hashCode(Object x) throws HibernateException {
return x == null ? 0 : x.hashCode();
}
#Override
public boolean isMutable() {
return false;
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
String label = rs.getString(names[0]);
if (rs.wasNull()) {
return null;
}
for (Enum value : returnedClass().getEnumConstants()) {
if (value instanceof LabeledEnum) {
LabeledEnum labeledEnum = (LabeledEnum) value;
if (labeledEnum.getLabel().equals(label)) {
return value;
}
}
}
throw new IllegalStateException("Unknown " + returnedClass().getSimpleName() + " label");
}
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, Types.VARCHAR);
} else {
st.setString(index, ((LabeledEnum) value).getLabel());
}
}
#Override
public Object replace(Object original, Object target, Object owner)
throws HibernateException {
return original;
}
#Override
public Class<? extends Enum> returnedClass() {
return enumClass;
}
#Override
public int[] sqlTypes() {
return new int[]{Types.VARCHAR};
}
#Override
public void setParameterValues(Properties parameters) {
ParameterType params = (ParameterType) parameters.get( PARAMETER_TYPE );
enumClass = params.getReturnedClass();
}
}
I would like to suggest following workaround. At first I was supprised it worked but it is really simple:
For enum:
public enum Status {
PENDING(0), SUCCESS(1), FAILED(-1);
private int status;
private Status(int status){
this.status = status;
}
public String getStatus() {
return status;
}
public static Status parse(int id) {
Status status = null; // Default
for (Status item : Status.values()) {
if (item.getStatus().equals(id)) {
Status = item;
break;
}
}
return Status;
}
}
class
class StatedObject{
#Column("status")
private int statusInt;
public Status getStatus() {
return Status.parse(statusInt);
}
public void setStatus(Status paymentStatus) {
this.statusInt = paymentStatus.getStatus();
}
public String getStatusInt() {
return statusInt;
}
public void setStatusInt(int statusInt) {
this.statusInt = statusInt;
}
}
if you are using hibernate in hibernate xml file it would be:
<property name="statusInt " column="status" type="java.lang.Integer" />
that is it
I have error:
com.google.gwt.user.client.rpc.SerializationException: Type 'ru.xxx.empeditor.client.Dept$$EnhancerByCGLIB$$2f6af516' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = ru.xxx.empeditor.client.Dept#e53d4e
Why this class not serializable?
package ru.xxx.empeditor.client;
import java.util.HashSet;
import java.util.Set;
import com.google.gwt.user.client.rpc.IsSerializable;
/**
* Dept generated by hbm2java
*/
public class Dept implements IsSerializable {
private byte deptno;
private String dname;
private String loc;
private Set<Emp> emps = new HashSet<Emp>(0);
public Dept() {
}
public Dept(byte deptno) {
this.deptno = deptno;
}
public Dept(byte deptno, String dname, String loc, Set<Emp> emps) {
this.deptno = deptno;
this.dname = dname;
this.loc = loc;
this.emps = emps;
}
public byte getDeptno() {
return this.deptno;
}
public void setDeptno(byte deptno) {
this.deptno = deptno;
}
public String getDname() {
return this.dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getLoc() {
return this.loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public Set<Emp> getEmps() {
return this.emps;
}
public void setEmps(Set<Emp> emps) {
this.emps = emps;
}
}
Check if the class Emp is serialiable.
Another potential issue (since you are using Hibernate - noticed the auto-generated comment) could be because of Proxies that modify your bean's byte code, as a result of which GWT fails to serialize it. As mentioned here - http://code.google.com/webtoolkit/articles/using_gwt_with_hibernate.html