Skip to content

Commit

Permalink
Fixing enum array mapping issues
Browse files Browse the repository at this point in the history
  • Loading branch information
jasp committed Apr 6, 2015
1 parent 1b90d71 commit 22ab89b
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 30 deletions.
4 changes: 2 additions & 2 deletions grails-app/domain/test/array/TestEnum.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ class TestEnum {
SATURDAY(5),
SUNDAY(6)

private final int value
Day(int value) { this.value = value }
final int id
Day(int id) { this.id = id }
}

Day[] days
Expand Down
4 changes: 2 additions & 2 deletions grails-app/domain/test/criteria/array/Like.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ class Like {
CARROT(7),
LEMON(8)

private final int value
Juice(int value) { this.value = value }
private final int id
Juice(int id) { this.id = id }
}

static mapping = {
Expand Down
38 changes: 22 additions & 16 deletions src/java/net/kaleidos/hibernate/usertype/ArrayType.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
import org.hibernate.usertype.UserType;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
Expand Down Expand Up @@ -101,18 +98,19 @@ public int[] sqlTypes() {

@Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {
Object[] result = null;
Object result = null;
Class typeArrayClass = java.lang.reflect.Array.newInstance(typeClass, 0).getClass();
java.sql.Array array = rs.getArray(names[0]);
Array sqlArray = rs.getArray(names[0]);
if (!rs.wasNull()) {
Object array = sqlArray.getArray();
if (typeClass.isEnum()) {
int length = java.lang.reflect.Array.getLength(array);
Object converted = java.lang.reflect.Array.newInstance(typeClass, length);
int length = array != null ? java.lang.reflect.Array.getLength(array) : 0;
result = java.lang.reflect.Array.newInstance(typeClass, length);
for (int i = 0; i < length; i++) {
java.lang.reflect.Array.set(converted, i, idToEnum(java.lang.reflect.Array.get(array, i)));
java.lang.reflect.Array.set(result, i, idToEnum((Integer)java.lang.reflect.Array.get(array, i)));
}
} else {
result = (Object[]) typeArrayClass.cast(array.getArray());
result = typeArrayClass.cast(array);
}
}
return result;
Expand All @@ -136,7 +134,7 @@ public void nullSafeSet(PreparedStatement st, Object value, int index, SessionIm
if (valueToSet[i] instanceof Integer) {
converted[i] = (Integer) valueToSet[i];
} else {
converted[i] = ((Enum) valueToSet[i]).ordinal();
converted[i] = enumToId(valueToSet[i]);
}
}
valueToSet = converted;
Expand All @@ -150,14 +148,22 @@ public Class<?> getTypeClass() {
return typeClass;
}

private Object idToEnum(Object id) throws HibernateException {
private void ensureBidiMapInitialized() throws HibernateException {
try {
if (bidiMap == null) {
if (bidiMap == null)
bidiMap = new BidiEnumMap(typeClass);
}
return bidiMap.getEnumValue(id);
} catch (Exception e) {
throw new HibernateException("Unable to create bidirectional enum map for " + typeClass, e);
}
}
}

private Object idToEnum(int id) throws HibernateException {
ensureBidiMapInitialized();
return bidiMap.getEnumValue(id);
}

private int enumToId(Object enumValue) throws HibernateException {
ensureBidiMapInitialized();
return bidiMap.getKey(enumValue);
}
}
15 changes: 11 additions & 4 deletions src/java/net/kaleidos/hibernate/usertype/BidiEnumMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public BidiEnumMap(Class<?> enumClass) throws NoSuchMethodException, IllegalAcce
EnumMap enumToKey = new EnumMap(enumClass);
HashMap keytoEnum = new HashMap();

Method idAccessor = enumClass.getMethod(ENUM_ID_ACCESSOR);
Method idAccessor = getIdAccessor(enumClass);

Method valuesAccessor = enumClass.getMethod("values");
Object[] values = (Object[]) valuesAccessor.invoke(enumClass);
Expand All @@ -47,11 +47,18 @@ public BidiEnumMap(Class<?> enumClass) throws NoSuchMethodException, IllegalAcce
this.keytoEnum = Collections.unmodifiableMap(keytoEnum);
}

public Object getEnumValue(Object id) {
private Method getIdAccessor(Class<?> enumClass) throws NoSuchMethodException {
for (Method method : enumClass.getMethods())
if (method.getName().equals(ENUM_ID_ACCESSOR))
return method;
return enumClass.getMethod("ordinal");
}

public Object getEnumValue(int id) {
return keytoEnum.get(id);
}

public Object getKey(Object enumValue) {
return enumToKey.get(enumValue);
public int getKey(Object enumValue) {
return (Integer)enumToKey.get(enumValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,15 @@ class PostgresqlArraysDomainIntegrationSpec extends Specification {

@Unroll
void 'save a domain class with an enum array value #days'() {
setup:
def testEnum = new TestEnum(days: days)

when:
testEnum.save(flush: true)
// Domain saving and retrieving should be in different sessions. Only in that case Hibernate will invoke
// nullSafeGet on the corresponding user type and will not use current session's cache.
def id = TestEnum.withNewSession {
new TestEnum(days: days).save(flush: true, failOnError: true).id
}

then:
testEnum.hasErrors() == false
testEnum.days?.length == days?.size()
TestEnum.get(id).days as List == days

where:
days << [null, [], [TestEnum.Day.MONDAY], [TestEnum.Day.SUNDAY, TestEnum.Day.SATURDAY], [TestEnum.Day.WEDNESDAY, TestEnum.Day.THURSDAY, TestEnum.Day.TUESDAY]]
Expand Down

0 comments on commit 22ab89b

Please sign in to comment.