mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-11-12 22:41:35 +00:00
#2336 - @DbArray List or Set of UUID or enum returned as String (not converted) when DB does not support ARRAY
This commit is contained in:
1 parent
6e10d641b2
commit
da6f7d671b
7 files changed
+193
-35
No files matched your search
@@ -162,30 +162,22 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the factory to use to support DB ARRAY types.
|
||||
*/
|
||||
private PlatformArrayTypeFactory arrayTypeListFactory(DatabasePlatform databasePlatform) {
|
||||
if (databasePlatform.isNativeArrayType()) {
|
||||
return ScalarTypeArrayList.factory();
|
||||
} else if (databasePlatform.isPlatform(Platform.H2)) {
|
||||
return ScalarTypeArrayListH2.factory();
|
||||
}
|
||||
// not supported for this DB platform
|
||||
return null;
|
||||
return new PlatformArrayTypeJsonList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the factory to use to support DB ARRAY types.
|
||||
*/
|
||||
private PlatformArrayTypeFactory arrayTypeSetFactory(DatabasePlatform databasePlatform) {
|
||||
if (databasePlatform.isNativeArrayType()) {
|
||||
return ScalarTypeArraySet.factory();
|
||||
} else if (databasePlatform.isPlatform(Platform.H2)) {
|
||||
return ScalarTypeArraySetH2.factory();
|
||||
}
|
||||
// not supported for this DB platform
|
||||
return null;
|
||||
return new PlatformArrayTypeJsonSet();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,25 +297,17 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
}
|
||||
|
||||
private ScalarType<?> getArrayScalarTypeSet(Type valueType, boolean nullable) {
|
||||
if (arrayTypeSetFactory != null) {
|
||||
if (isEnumType(valueType)) {
|
||||
return arrayTypeSetFactory.typeForEnum(createEnumScalarType(asEnumClass(valueType), null), nullable);
|
||||
}
|
||||
return arrayTypeSetFactory.typeFor(valueType, nullable);
|
||||
if (isEnumType(valueType)) {
|
||||
return arrayTypeSetFactory.typeForEnum(createEnumScalarType(asEnumClass(valueType), null), nullable);
|
||||
}
|
||||
// fallback to JSON storage in VARCHAR column
|
||||
return new ScalarTypeJsonSet.Varchar(getDocType(valueType), nullable, false); // TODO: keepSource for @DbArray?
|
||||
return arrayTypeSetFactory.typeFor(valueType, nullable);
|
||||
}
|
||||
|
||||
private ScalarType<?> getArrayScalarTypeList(Type valueType, boolean nullable) {
|
||||
if (arrayTypeListFactory != null) {
|
||||
if (isEnumType(valueType)) {
|
||||
return arrayTypeListFactory.typeForEnum(createEnumScalarType(asEnumClass(valueType), null), nullable);
|
||||
}
|
||||
return arrayTypeListFactory.typeFor(valueType, nullable);
|
||||
if (isEnumType(valueType)) {
|
||||
return arrayTypeListFactory.typeForEnum(createEnumScalarType(asEnumClass(valueType), null), nullable);
|
||||
}
|
||||
// fallback to JSON storage in VARCHAR column
|
||||
return new ScalarTypeJsonList.Varchar(getDocType(valueType), nullable, false); // TODO: keepSource for @DbArray?
|
||||
return arrayTypeListFactory.typeFor(valueType, nullable);
|
||||
}
|
||||
|
||||
private Class<? extends Enum<?>> asEnumClass(Type valueType) {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Fallback for DbArray as Json List.
|
||||
*/
|
||||
class PlatformArrayTypeJsonList implements PlatformArrayTypeFactory {
|
||||
|
||||
private DocPropertyType docType(Type valueType) {
|
||||
if (valueType.equals(Long.class)) {
|
||||
return DocPropertyType.LONG;
|
||||
}
|
||||
if (valueType.equals(Integer.class)) {
|
||||
return DocPropertyType.INTEGER;
|
||||
}
|
||||
if (valueType.equals(Double.class)) {
|
||||
return DocPropertyType.DOUBLE;
|
||||
}
|
||||
return DocPropertyType.TEXT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> typeFor(Type valueType, boolean nullable) {
|
||||
if (valueType.equals(UUID.class)) {
|
||||
// TODO: keepSource for @DbArray?
|
||||
return new ScalarTypeJsonList.VarcharWithConverter(DocPropertyType.UUID, nullable, false, ArrayElementConverter.UUID);
|
||||
}
|
||||
return new ScalarTypeJsonList.Varchar(docType(valueType), nullable, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> typeForEnum(ScalarType<?> scalarType, boolean nullable) {
|
||||
final ArrayElementConverter.EnumConverter converter = new ArrayElementConverter.EnumConverter(scalarType);
|
||||
return new ScalarTypeJsonList.VarcharWithConverter(scalarType.getDocType(), nullable, false, converter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Fallback for DbArray as Json Set.
|
||||
*/
|
||||
class PlatformArrayTypeJsonSet implements PlatformArrayTypeFactory {
|
||||
|
||||
private DocPropertyType docType(Type valueType) {
|
||||
if (valueType.equals(Long.class)) {
|
||||
return DocPropertyType.LONG;
|
||||
}
|
||||
if (valueType.equals(Integer.class)) {
|
||||
return DocPropertyType.INTEGER;
|
||||
}
|
||||
if (valueType.equals(Double.class)) {
|
||||
return DocPropertyType.DOUBLE;
|
||||
}
|
||||
return DocPropertyType.TEXT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> typeFor(Type valueType, boolean nullable) {
|
||||
if (valueType.equals(UUID.class)) {
|
||||
// TODO: keepSource for @DbArray?
|
||||
return new ScalarTypeJsonSet.VarcharWithConverter(DocPropertyType.UUID, nullable, false, ArrayElementConverter.UUID);
|
||||
}
|
||||
return new ScalarTypeJsonSet.Varchar(docType(valueType), nullable, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> typeForEnum(ScalarType<?> scalarType, boolean nullable) {
|
||||
final ArrayElementConverter.EnumConverter converter = new ArrayElementConverter.EnumConverter(scalarType);
|
||||
return new ScalarTypeJsonSet.VarcharWithConverter(scalarType.getDocType(), nullable, false, converter);
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,13 @@ import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.text.TextException;
|
||||
import io.ebean.text.json.EJson;
|
||||
import io.ebeaninternal.json.ModifyAwareList;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -36,6 +38,46 @@ final class ScalarTypeJsonList {
|
||||
return new ScalarTypeJsonList.Varchar(docType, nullable, keepSource);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static final class VarcharWithConverter extends ScalarTypeJsonList.Base {
|
||||
private final ArrayElementConverter converter;
|
||||
|
||||
VarcharWithConverter(DocPropertyType docType, boolean nullable, boolean keepSource, ArrayElementConverter converter) {
|
||||
super(Types.VARCHAR, docType, nullable, keepSource);
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
List readJsonConvert(String json) {
|
||||
try {
|
||||
return convertElements(EJson.parseList(json, false));
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as List", json, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List convertElements(List<Object> rawList) {
|
||||
if (rawList == null) {
|
||||
return null;
|
||||
}
|
||||
final List result = new ArrayList<>(rawList.size());
|
||||
for (Object o : rawList) {
|
||||
result.add(converter.fromSerialized(o));
|
||||
}
|
||||
return new ModifyAwareList(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List parse(String value) {
|
||||
try {
|
||||
return convertElements(EJson.parseList(value, false));
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as List", value, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to DB VARCHAR.
|
||||
*/
|
||||
@@ -86,6 +128,10 @@ final class ScalarTypeJsonList {
|
||||
if (keepSource) {
|
||||
reader.pushJson(json);
|
||||
}
|
||||
return readJsonConvert(json);
|
||||
}
|
||||
|
||||
List readJsonConvert(String json) {
|
||||
try {
|
||||
// parse JSON into modifyAware list
|
||||
return EJson.parseList(json, true);
|
||||
@@ -130,7 +176,7 @@ final class ScalarTypeJsonList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List parse(String value) {
|
||||
public List parse(String value) {
|
||||
try {
|
||||
return EJson.parseList(value, false);
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
import io.ebean.text.TextException;
|
||||
import io.ebean.text.json.EJson;
|
||||
import io.ebeaninternal.json.ModifyAwareSet;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
@@ -38,6 +39,45 @@ final class ScalarTypeJsonSet {
|
||||
return new ScalarTypeJsonSet.Varchar(docPropertyType, nullable, keepSource);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static final class VarcharWithConverter extends ScalarTypeJsonSet.Base {
|
||||
private final ArrayElementConverter converter;
|
||||
|
||||
VarcharWithConverter(DocPropertyType docType, boolean nullable, boolean keepSource, ArrayElementConverter converter) {
|
||||
super(Types.VARCHAR, docType, nullable, keepSource);
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
Set readJsonConvert(String json) {
|
||||
try {
|
||||
return convertElements(EJson.parseSet(json, false));
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as List", json, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Set convertElements(Set<Object> rawSet) {
|
||||
if (rawSet == null) {
|
||||
return null;
|
||||
}
|
||||
final Set result = new LinkedHashSet(rawSet.size());
|
||||
for (Object o : rawSet) {
|
||||
result.add(converter.fromSerialized(o));
|
||||
}
|
||||
return new ModifyAwareSet(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set parse(String value) {
|
||||
try {
|
||||
return convertElements(EJson.parseSet(value, false));
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Failed to parse JSON content as Set: [" + value + "]", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* List mapped to DB VARCHAR.
|
||||
*/
|
||||
@@ -86,8 +126,11 @@ final class ScalarTypeJsonSet {
|
||||
if (keepSource) {
|
||||
reader.pushJson(json);
|
||||
}
|
||||
return readJsonConvert(json);
|
||||
}
|
||||
|
||||
Set readJsonConvert(String json) {
|
||||
try {
|
||||
// parse JSON into modifyAware list
|
||||
return EJson.parseSet(json, true);
|
||||
} catch (IOException e) {
|
||||
throw new TextException("Failed to parse JSON [{}] as Set", json, e);
|
||||
@@ -130,7 +173,7 @@ final class ScalarTypeJsonSet {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Set parse(String value) {
|
||||
public Set parse(String value) {
|
||||
try {
|
||||
return convertList(EJson.parseList(value));
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -179,11 +179,13 @@ public class TestDbArray_asSet extends BaseTestCase {
|
||||
|
||||
Ebean.save(bean);
|
||||
// load cache
|
||||
Ebean.find(EArraySetBean.class, bean.getId());
|
||||
final EArraySetBean entry = Ebean.find(EArraySetBean.class, bean.getId());
|
||||
assertThat(entry.getUids()).hasSameElementsAs(uids);
|
||||
assertThat(entry.getStatus()).hasSameElementsAs(statuses);
|
||||
|
||||
// hit cache
|
||||
EArraySetBean found = Ebean.find(EArraySetBean.class, bean.getId());
|
||||
|
||||
assertThat(found.getUids()).isEqualTo(uids);
|
||||
assertThat(found.getStatus()).isEqualTo(statuses);
|
||||
assertThat(found.getUids()).hasSameElementsAs(uids);
|
||||
assertThat(found.getStatus()).hasSameElementsAs(statuses);
|
||||
}
|
||||
}
|
||||
@@ -268,11 +268,12 @@ public class TestDbArray_basic extends BaseTestCase {
|
||||
|
||||
DB.save(bean);
|
||||
// load cache
|
||||
DB.find(EArrayBean.class, bean.getId());
|
||||
final EArrayBean entry = DB.find(EArrayBean.class, bean.getId());
|
||||
assertThat(entry.getUids()).hasSameElementsAs(uids);
|
||||
assertThat(entry.getStatuses()).hasSameElementsAs(statuses);
|
||||
// hit cache
|
||||
EArrayBean found = DB.find(EArrayBean.class, bean.getId());
|
||||
|
||||
assertThat(found.getUids()).isEqualTo(uids);
|
||||
assertThat(found.getStatuses()).isEqualTo(statuses);
|
||||
assertThat(found.getUids()).hasSameElementsAs(uids);
|
||||
assertThat(found.getStatuses()).hasSameElementsAs(statuses);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user