diff --git a/src/main/java/com/avaje/ebean/config/dbplatform/DatabasePlatform.java b/src/main/java/com/avaje/ebean/config/dbplatform/DatabasePlatform.java index 6289dd810..97671b6ed 100644 --- a/src/main/java/com/avaje/ebean/config/dbplatform/DatabasePlatform.java +++ b/src/main/java/com/avaje/ebean/config/dbplatform/DatabasePlatform.java @@ -197,6 +197,13 @@ public class DatabasePlatform { return platform == null || platform.name().equalsIgnoreCase(name); } + /** + * Return true if this matches the given platform. + */ + public boolean isPlatform(Platform platform) { + return platform.name().equalsIgnoreCase(name); + } + /** * Return the name of the DatabasePlatform. *

diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ArrayElementConverter.java b/src/main/java/com/avaje/ebeaninternal/server/type/ArrayElementConverter.java index 0899fa40a..9bd2d23b7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ArrayElementConverter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ArrayElementConverter.java @@ -5,7 +5,7 @@ import java.util.UUID; /** * Type conversion for use with ScalarTypeArrayList. */ -public interface ArrayElementConverter { +interface ArrayElementConverter { /** * Convert the array element to the logical type. @@ -32,6 +32,11 @@ public interface ArrayElementConverter { */ ArrayElementConverter INTEGER = new IntegerConverter(); + /** + * The Double converter implementation. + */ + ArrayElementConverter DOUBLE = new DoubleConverter(); + class LongConverter implements ArrayElementConverter { @Override @@ -56,6 +61,18 @@ public interface ArrayElementConverter { } } + class DoubleConverter implements ArrayElementConverter { + + @Override + public Double toElement(Object rawValue) { + if (rawValue instanceof Double) { + return (Double) rawValue; + } else { + return ((Number) rawValue).doubleValue(); + } + } + } + /** * String converter (noop based). */ 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 c299d45e1..6ef22fba2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java @@ -7,6 +7,7 @@ import com.avaje.ebean.annotation.EnumValue; import com.avaje.ebean.config.CompoundType; import com.avaje.ebean.config.CompoundTypeProperty; import com.avaje.ebean.config.JsonConfig; +import com.avaje.ebean.config.Platform; import com.avaje.ebean.config.ScalarTypeConverter; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebean.config.dbplatform.DatabasePlatform; @@ -185,6 +186,8 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { */ private ScalarType jsonNodeJsonb; + private final PlatformArrayTypeFactory arrayTypeFactory; + /** * Create the DefaultTypeManager. */ @@ -204,6 +207,8 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { this.extraTypeFactory = new DefaultTypeFactory(config); this.postgres = isPostgres(config.getDatabasePlatform()); + this.arrayTypeFactory = arrayTypeFactory(postgres, config.getDatabasePlatform()); + this.offlineMigrationGeneration = DbOffline.isGenerateMigration(); initialiseStandard(jsonDateTime, config); @@ -220,6 +225,19 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { } } + /** + * Return the factory to use to support DB ARRAY types. + */ + private PlatformArrayTypeFactory arrayTypeFactory(boolean postgres, DatabasePlatform databasePlatform) { + if (postgres) { + return ScalarTypeArrayList.factory(); + } else if (databasePlatform.isPlatform(Platform.H2)) { + return ScalarTypeArrayListH2.factory(); + } + // not supported for this DB platform + return null; + } + /** * Load custom scalar types registered via ExtraTypeFactory and ServiceLoader. */ @@ -397,9 +415,9 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { public ScalarType getArrayScalarType(Class type, DbArray dbArray, Type genericType) { if (type.equals(List.class)) { - if (postgres) { + if (arrayTypeFactory != null) { Type valueType = getValueType(genericType); - return ScalarTypeArrayList.typeFor(valueType); + return arrayTypeFactory.typeFor(valueType); } // fallback to JSON storage in VARCHAR column return new ScalarTypeJsonList.Varchar(getDocType(getValueType(genericType))); diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/PlatformArrayTypeFactory.java b/src/main/java/com/avaje/ebeaninternal/server/type/PlatformArrayTypeFactory.java new file mode 100644 index 000000000..ba6a9fe17 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/type/PlatformArrayTypeFactory.java @@ -0,0 +1,15 @@ +package com.avaje.ebeaninternal.server.type; + +import java.lang.reflect.Type; + +/** + * Factory for platform specific handling/ScalarTypes for DB ARRAY. + */ +public interface PlatformArrayTypeFactory { + + /** + * Return the ScalarType to handle DB ARRAY for the given element type. + */ + ScalarType typeFor(Type valueType); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeArrayList.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeArrayList.java index 0e7b693db..4aa61b15a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeArrayList.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeArrayList.java @@ -22,30 +22,38 @@ import java.util.UUID; public class ScalarTypeArrayList extends ScalarTypeJsonCollection { private static ScalarTypeArrayList UUID = new ScalarTypeArrayList("uuid", DocPropertyType.UUID, ArrayElementConverter.UUID); - private static ScalarTypeArrayList LONG = new ScalarTypeArrayList("bigint", DocPropertyType.LONG, ArrayElementConverter.LONG); - private static ScalarTypeArrayList INTEGER = new ScalarTypeArrayList("integer", DocPropertyType.INTEGER, ArrayElementConverter.INTEGER); - + private static ScalarTypeArrayList DOUBLE = new ScalarTypeArrayList("float", DocPropertyType.DOUBLE, ArrayElementConverter.DOUBLE); private static ScalarTypeArrayList STRING = new ScalarTypeArrayList("varchar", DocPropertyType.STRING, ArrayElementConverter.STRING); - /** - * Return the ScalarType to use based on the List's generic parameter type. - */ - public static ScalarTypeArrayList typeFor(Type valueType) { - if (valueType.equals(UUID.class)) { - return UUID; + static PlatformArrayTypeFactory factory() { + return new Factory(); + } + + static class Factory implements PlatformArrayTypeFactory { + + /** + * Return the ScalarType to use based on the List's generic parameter type. + */ + public ScalarTypeArrayList typeFor(Type valueType) { + if (valueType.equals(UUID.class)) { + return UUID; + } + if (valueType.equals(Long.class)) { + return LONG; + } + if (valueType.equals(Integer.class)) { + return INTEGER; + } + if (valueType.equals(Double.class)) { + return DOUBLE; + } + if (valueType.equals(String.class)) { + return STRING; + } + throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping"); } - if (valueType.equals(Long.class)) { - return LONG; - } - if (valueType.equals(Integer.class)) { - return INTEGER; - } - if (valueType.equals(String.class)) { - return STRING; - } - throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping"); } private final String arrayType; @@ -79,7 +87,7 @@ public class ScalarTypeArrayList extends ScalarTypeJsonCollection { return new ModifyAwareList(list); } - private Object[] toArray(List value) { + protected Object[] toArray(List value) { return value.toArray(); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeArrayListH2.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeArrayListH2.java new file mode 100644 index 000000000..b9b4175d8 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeArrayListH2.java @@ -0,0 +1,67 @@ +package com.avaje.ebeaninternal.server.type; + +import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyType; + +import java.lang.reflect.Type; +import java.sql.SQLException; +import java.sql.Types; +import java.util.List; + +/** + * H2 database support for DB ARRAY. + */ +class ScalarTypeArrayListH2 extends ScalarTypeArrayList { + + private static ScalarTypeArrayListH2 UUID = new ScalarTypeArrayListH2("uuid", DocPropertyType.UUID, ArrayElementConverter.UUID); + private static ScalarTypeArrayListH2 LONG = new ScalarTypeArrayListH2("bigint", DocPropertyType.LONG, ArrayElementConverter.LONG); + private static ScalarTypeArrayListH2 INTEGER = new ScalarTypeArrayListH2("integer", DocPropertyType.INTEGER, ArrayElementConverter.INTEGER); + private static ScalarTypeArrayListH2 DOUBLE = new ScalarTypeArrayListH2("double", DocPropertyType.DOUBLE, ArrayElementConverter.DOUBLE); + private static ScalarTypeArrayListH2 STRING = new ScalarTypeArrayListH2("varchar", DocPropertyType.STRING, ArrayElementConverter.STRING); + + static PlatformArrayTypeFactory factory() { + return new ScalarTypeArrayListH2.Factory(); + } + + static class Factory implements PlatformArrayTypeFactory { + + /** + * Return the ScalarType to use based on the List's generic parameter type. + */ + public ScalarTypeArrayListH2 typeFor(Type valueType) { + if (valueType.equals(java.util.UUID.class)) { + return UUID; + } + if (valueType.equals(Long.class)) { + return LONG; + } + if (valueType.equals(Integer.class)) { + return INTEGER; + } + if (valueType.equals(Double.class)) { + return DOUBLE; + } + if (valueType.equals(String.class)) { + return STRING; + } + throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping"); + } + } + + private ScalarTypeArrayListH2(String arrayType, DocPropertyType docPropertyType, ArrayElementConverter converter) { + super(arrayType, docPropertyType, converter); + } + + @Override + public String getDbColumnDefn() { + return "array"; + } + + @Override + public void bind(DataBind bind, List value) throws SQLException { + if (value == null) { + bind.setNull(Types.ARRAY); + } else { + bind.setObject(toArray(value)); + } + } +} diff --git a/src/test/java/com/avaje/ebeaninternal/server/type/ScalarTypeArrayListTest.java b/src/test/java/com/avaje/ebeaninternal/server/type/ScalarTypeArrayListTest.java index 9a60004e5..22398359e 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/type/ScalarTypeArrayListTest.java +++ b/src/test/java/com/avaje/ebeaninternal/server/type/ScalarTypeArrayListTest.java @@ -14,8 +14,7 @@ public class ScalarTypeArrayListTest { DataReader mock = Mockito.mock(DataReader.class); Mockito.when(mock.getArray()).thenReturn(null); - ScalarTypeArrayList scalarType = ScalarTypeArrayList.typeFor(Long.class); + ScalarType scalarType = ScalarTypeArrayList.factory().typeFor(Long.class); scalarType.read(mock); - } } \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/model/array/EArrayBean.java b/src/test/java/com/avaje/tests/model/array/EArrayBean.java index a16670a8c..c16960592 100644 --- a/src/test/java/com/avaje/tests/model/array/EArrayBean.java +++ b/src/test/java/com/avaje/tests/model/array/EArrayBean.java @@ -27,6 +27,9 @@ public class EArrayBean { @DbArray List otherIds = new ArrayList(); + @DbArray + List doubs; + @Version Long version; @@ -70,6 +73,14 @@ public class EArrayBean { this.otherIds = otherIds; } + public List getDoubs() { + return doubs; + } + + public void setDoubs(List doubs) { + this.doubs = doubs; + } + public Long getVersion() { return version; } diff --git a/src/test/java/com/avaje/tests/model/array/TestDbArray_basic.java b/src/test/java/com/avaje/tests/model/array/TestDbArray_basic.java index 21b945bc2..405cbdfa7 100644 --- a/src/test/java/com/avaje/tests/model/array/TestDbArray_basic.java +++ b/src/test/java/com/avaje/tests/model/array/TestDbArray_basic.java @@ -28,11 +28,17 @@ public class TestDbArray_basic extends BaseTestCase { phNumbers.add("4321"); phNumbers.add("9823"); + + List doubles = new ArrayList(); + doubles.add(1.3); + doubles.add(2.4); + bean.getUids().add(UUID.randomUUID()); bean.getUids().add(UUID.randomUUID()); bean.getOtherIds().add(95L); bean.getOtherIds().add(96L); bean.getOtherIds().add(97L); + bean.setDoubs(doubles); Ebean.save(bean);