From 2a6d38eef2cf683aa5a42e4194d05f1abfd0c80a Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Sun, 6 Nov 2016 20:33:56 +1300 Subject: [PATCH] #852 - javax.persistence.PersistenceException: No ScalarType registered for class Foo$1 ... where Foo is an Enum where individual instances override a method (example below). --- .../server/deploy/parse/DeployUtil.java | 5 +-- .../server/type/DefaultTypeManager.java | 32 +++++++++++-------- .../server/type/TypeManager.java | 5 +++ .../com/avaje/ebean/server/type/MyEnum.java | 28 ++++++++++++++++ .../ebean/server/type/TestTypeManager.java | 27 +++++++++++++++- src/test/resources/logback-test.xml | 8 ++--- 6 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 src/test/java/com/avaje/ebean/server/type/MyEnum.java diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java index 6ac806c88..8fc97f4a2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java @@ -119,14 +119,15 @@ public class DeployUtil { ScalarType scalarType = typeManager.getScalarType(enumType); if (scalarType == null) { // look for @DbEnumValue or @EnumValue annotations etc - scalarType = typeManager.createEnumScalarType((Class>)enumType); + Class> enumClass = (Class>)enumType; + scalarType = typeManager.createEnumScalarType(enumClass); if (scalarType == null) { // use JPA normal Enum type (without mapping) EnumType type = enumerated != null ? enumerated.value() : null; scalarType = createEnumScalarTypePerSpec(enumType, type); } - typeManager.add(scalarType); + typeManager.addEnumType(scalarType, enumClass); } prop.setScalarType(scalarType); prop.setDbType(scalarType.getJdbcType()); diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java index ef34b6fec..67fb843e3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java @@ -62,19 +62,7 @@ import java.time.YearMonth; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Currency; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.ServiceLoader; -import java.util.Set; -import java.util.TimeZone; -import java.util.UUID; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** @@ -330,6 +318,24 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { logAdd(scalarType); } + /** + * Register the ScalarType for an enum. This is special in the sense that an Enum + * can have many classes if it uses method overrides and we need to register all + * the variations/classes for the enum. + */ + @Override + public void addEnumType(ScalarType scalarType, Class enumClass) { + + Set> mappedClasses = new HashSet<>(); + for (Object value : EnumSet.allOf(enumClass).toArray()) { + mappedClasses.add(value.getClass()); + } + for (Class cls: mappedClasses) { + typeMap.put(cls, scalarType); + } + logAdd(scalarType); + } + private void logAdd(ScalarType scalarType) { if (logger.isDebugEnabled()) { String msg = "ScalarType register [" + scalarType.getClass().getName() + "]"; diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/TypeManager.java b/src/main/java/com/avaje/ebeaninternal/server/type/TypeManager.java index 7b880cd9a..358ab024f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/TypeManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/TypeManager.java @@ -30,6 +30,11 @@ public interface TypeManager { */ void add(ScalarType scalarType); + /** + * Register a ScalarType for an Enum with can have multiple classes. + */ + void addEnumType(ScalarType type, Class myEnumClass); + /** * Return the Internal CompoundType handler for a given compound type. */ diff --git a/src/test/java/com/avaje/ebean/server/type/MyEnum.java b/src/test/java/com/avaje/ebean/server/type/MyEnum.java new file mode 100644 index 000000000..50579a7cf --- /dev/null +++ b/src/test/java/com/avaje/ebean/server/type/MyEnum.java @@ -0,0 +1,28 @@ +package com.avaje.ebean.server.type; + +import com.avaje.ebean.annotation.EnumValue; + +/** + * Enum with method overrides (and hence multiple actual classes). + */ +public enum MyEnum { + + @EnumValue("A")Aval { + @Override + public String doSomething() { + return "bar"; + } + }, + @EnumValue("B")Bval, + + @EnumValue("C")Cval { + @Override + public String doSomething() { + return "baz"; + } + }; + + public String doSomething() { + return "foo"; + } +} diff --git a/src/test/java/com/avaje/ebean/server/type/TestTypeManager.java b/src/test/java/com/avaje/ebean/server/type/TestTypeManager.java index 5d7516ec2..6f3ce1442 100644 --- a/src/test/java/com/avaje/ebean/server/type/TestTypeManager.java +++ b/src/test/java/com/avaje/ebean/server/type/TestTypeManager.java @@ -23,6 +23,31 @@ import static org.assertj.core.api.Assertions.assertThat; public class TestTypeManager extends BaseTestCase { + @Test + public void testEnumWithSubclasses() throws SQLException { + + DefaultTypeManager typeManager = createTypeManager(); + + ScalarType type = typeManager.createEnumScalarType(MyEnum.class); + typeManager.addEnumType(type, MyEnum.class); + + Object val = type.read(new DummyDataReader("A")); + assertThat(val).isEqualTo(MyEnum.Aval); + val = type.read(new DummyDataReader("B")); + assertThat(val).isEqualTo(MyEnum.Bval); + val = type.read(new DummyDataReader("C")); + assertThat(val).isEqualTo(MyEnum.Cval); + + ScalarType typeGeneral = typeManager.getScalarType(MyEnum.class); + assertThat(typeGeneral).isNotNull(); + ScalarType typeB = typeManager.getScalarType(MyEnum.Bval.getClass()); + assertThat(typeB).isNotNull(); + ScalarType typeA = typeManager.getScalarType(MyEnum.Aval.getClass()); + assertThat(typeA).isNotNull(); + ScalarType typeC = typeManager.getScalarType(MyEnum.Cval.getClass()); + assertThat(typeC).isNotNull(); + } + @Test public void testEnumWithChar() throws SQLException { @@ -88,7 +113,7 @@ public class TestTypeManager extends BaseTestCase { String val; - public DummyDataReader(String val) { + DummyDataReader(String val) { super(null, null); this.val = val; } diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index ecacef46d..44bfd40d3 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -78,10 +78,10 @@ - - - - + + + +