mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Merge remote-tracking branch 'upstream/master' into ebean-new
# Conflicts: # ebean-core/src/main/java/io/ebeaninternal/server/core/DScriptRunner.java # ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java # ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlPlugin.java # ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java # ebean-test/src/test/java/io/ebean/json/EJsonTests.java
This commit is contained in:
@@ -67,7 +67,7 @@ final class DScriptRunner implements ScriptRunner {
|
||||
}
|
||||
|
||||
try (InputStream inputStream = UrlHelper.openNoCache(resource);
|
||||
Reader reader = IOUtils.newReader(inputStream)) {
|
||||
Reader reader = IOUtils.newReader(inputStream)) {
|
||||
return readContent(reader);
|
||||
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -200,7 +200,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
List<? extends ScalarType<?>> types = plugin.createTypes(config, objectMapper);
|
||||
for (ScalarType<?> type : types) {
|
||||
log.debug("adding ScalarType {}", type.getClass());
|
||||
addCustomType(type);
|
||||
add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,25 +218,6 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
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.
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Override
|
||||
public void addEnumType(ScalarType<?> scalarType, Class<? extends Enum> enumClass) {
|
||||
Set<Class<?>> mappedClasses = new HashSet<>();
|
||||
mappedClasses.add(enumClass);
|
||||
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 (log.isTraceEnabled()) {
|
||||
String msg = "ScalarType register [" + scalarType.getClass().getName() + "]";
|
||||
@@ -282,19 +263,44 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
"ScalarType of Joda LocalTime not defined. You need to set DatabaseConfig.jodaLocalTimeMode to"
|
||||
+ " either 'normal' or 'utc'. UTC is the old mode using UTC timezone but local time zone is now preferred as 'normal' mode.");
|
||||
}
|
||||
found = checkInterfaceTypes(type);
|
||||
found = checkInheritedTypes(type);
|
||||
}
|
||||
return found;
|
||||
return found != ScalarTypeNotFound.INSTANCE ? found : null; // Do not return ScalarTypeNotFound, otherwise checks will fail
|
||||
}
|
||||
|
||||
private ScalarType<?> checkInterfaceTypes(Class<?> type) {
|
||||
for (Entry<Class<?>, ScalarType<?>> entry : typeMap.entrySet()) {
|
||||
if (entry.getKey().isAssignableFrom(type)) {
|
||||
typeMap.put(type, entry.getValue());
|
||||
return entry.getValue();
|
||||
|
||||
/**
|
||||
* Checks the typeMap for inherited types.
|
||||
*
|
||||
* If e.g. <code>type</code> is a <code>GregorianCalendar</code>, then this method
|
||||
* will check the class hierarchy and will probably return a
|
||||
* <code>ScalarTypeCalendar</code> To speed up a second lookup, it will write
|
||||
* back the found scalarType to typeMap.
|
||||
*
|
||||
* @param type the for which to search for a <code>ScalarType</code>
|
||||
* @return either a valid <code>ScalarType</code> if one could be found or {@link ScalarTypeNotFound#INSTANCE} if not
|
||||
*/
|
||||
private ScalarType<?> checkInheritedTypes(Class<?> type) {
|
||||
// first step loop through inheritance chain
|
||||
Class<?> parent = type;
|
||||
while (parent != null && parent != Object.class) {
|
||||
ScalarType<?> found = typeMap.get(parent);
|
||||
if (found != null && found != ScalarTypeNotFound.INSTANCE) {
|
||||
typeMap.put(type, found); // store type for next lookup
|
||||
return found;
|
||||
}
|
||||
// second step - loop through interfaces of this type
|
||||
for (Class<?> iface: parent.getInterfaces()) {
|
||||
found = checkInheritedTypes(iface);
|
||||
if (found != null && found != ScalarTypeNotFound.INSTANCE) {
|
||||
typeMap.put(type, found); // store type for next lookup
|
||||
return found;
|
||||
}
|
||||
}
|
||||
parent = parent.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
typeMap.put(type, ScalarTypeNotFound.INSTANCE);
|
||||
return ScalarTypeNotFound.INSTANCE; // no success
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -399,7 +405,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
private DocPropertyType getDocType(Type genericType) {
|
||||
if (genericType instanceof Class<?>) {
|
||||
ScalarType<?> found = typeMap.get(genericType);
|
||||
ScalarType<?> found = getScalarType((Class<?>)genericType);
|
||||
if (found != null) {
|
||||
return found.getDocType();
|
||||
}
|
||||
@@ -457,7 +463,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
return scalarType;
|
||||
}
|
||||
|
||||
scalarType = typeMap.get(type);
|
||||
scalarType = getScalarType(type);
|
||||
if (scalarType != null) {
|
||||
if (jdbcType == 0 || scalarType.getJdbcType() == jdbcType) {
|
||||
// matching type
|
||||
@@ -575,7 +581,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
// use JPA normal Enum type (without mapping)
|
||||
scalarEnum = createEnumScalarTypePerSpec(enumType, type);
|
||||
}
|
||||
addEnumType(scalarEnum, enumType);
|
||||
add(scalarEnum);
|
||||
return scalarEnum;
|
||||
}
|
||||
|
||||
@@ -665,16 +671,13 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
scalarType = cls.getDeclaredConstructor().newInstance();
|
||||
}
|
||||
}
|
||||
addCustomType(scalarType);
|
||||
add(scalarType);
|
||||
} catch (Exception e) {
|
||||
log.error("Error loading ScalarType [" + cls.getName() + "]", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addCustomType(ScalarType<?> scalarType) {
|
||||
add(scalarType);
|
||||
}
|
||||
|
||||
private Object initObjectMapper(DatabaseConfig config) {
|
||||
Object objectMapper = config.getObjectMapper();
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
|
||||
import io.ebean.core.type.DataBinder;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
|
||||
/**
|
||||
* Class is required as "null" key in ConcurrentHashMap.
|
||||
* @author Roland Praml, FOCONIS AG
|
||||
*
|
||||
*/
|
||||
class ScalarTypeNotFound implements ScalarType<Void> {
|
||||
|
||||
public static final ScalarTypeNotFound INSTANCE = new ScalarTypeNotFound();
|
||||
private ScalarTypeNotFound() { }
|
||||
@Override
|
||||
public boolean isBinaryType() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJdbcNative() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getJdbcType() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<Void> getType() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void read(DataReader reader) throws SQLException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadIgnore(DataReader reader) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBinder binder, Void value) throws SQLException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toJdbcType(Object value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void toBeanType(Object value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(Void value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(Object value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void parse(String value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocPropertyType getDocType() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDateTimeCapable() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long asVersion(Void value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void convertFromMillis(long dateTime) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void readData(DataInput dataInput) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeData(DataOutput dataOutput, Void v) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void jsonRead(JsonParser parser) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, Void value) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,12 +17,6 @@ public interface TypeManager {
|
||||
*/
|
||||
void add(ScalarType<?> scalarType);
|
||||
|
||||
/**
|
||||
* Register a ScalarType for an Enum with can have multiple classes.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
void addEnumType(ScalarType<?> type, Class<? extends Enum> myEnumClass);
|
||||
|
||||
/**
|
||||
* Return the scalar type for the given logical type.
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.io.File;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class MChecksumTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void calculate() {
|
||||
File file = new File("src/test/resources/dbmigration/index-special-chars/1.0__hello.sql");
|
||||
@@ -15,4 +15,12 @@ public class MChecksumTest {
|
||||
|
||||
assertThat(MChecksum.calculate(file)).isEqualTo(1859426839);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void calculateWithSpecialChars() {
|
||||
File file = new File("src/test/resources/dbmigration/index-special-chars/1.0__hello.sql");
|
||||
assertThat(file).exists();
|
||||
|
||||
assertThat(MChecksum.calculate(file)).isEqualTo(1859426839);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ public class EJsonTests {
|
||||
|
||||
File temp = Files.createTempFile("some", ".json").toFile();
|
||||
try (Writer writer = IOUtils.newWriter(temp)) {
|
||||
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("foo", "bar");
|
||||
EJson.write(map, writer);
|
||||
|
||||
@@ -15,6 +15,8 @@ import org.tests.model.ivo.converter.MoneyTypeConverter;
|
||||
import javax.persistence.EnumType;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
@@ -29,7 +31,6 @@ public class TestTypeManager extends BaseTestCase {
|
||||
DefaultTypeManager typeManager = createTypeManager();
|
||||
|
||||
ScalarType<?> type = typeManager.createEnumScalarType(MyEnum.class, null);
|
||||
typeManager.addEnumType(type, MyEnum.class);
|
||||
|
||||
Object val = type.read(new DummyDataReader("A"));
|
||||
assertThat(val).isEqualTo(MyEnum.Aval);
|
||||
@@ -131,6 +132,13 @@ public class TestTypeManager extends BaseTestCase {
|
||||
return new DefaultTypeManager(config, bootupClasses);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalendar() throws SQLException {
|
||||
|
||||
DefaultTypeManager typeManager = createTypeManager();
|
||||
ScalarType<?> typeB = typeManager.getScalarType(GregorianCalendar.class);
|
||||
assertThat(typeB).isInstanceOf(ScalarTypeCalendar.class);
|
||||
}
|
||||
/**
|
||||
* Test double DataReader implementation.
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,7 @@ import javax.persistence.Version;
|
||||
import java.nio.file.Path;
|
||||
import java.sql.Date;
|
||||
import java.time.*;
|
||||
import java.util.Calendar;
|
||||
|
||||
@Entity
|
||||
public class SomeNewTypesBean {
|
||||
@@ -58,6 +59,8 @@ public class SomeNewTypesBean {
|
||||
|
||||
Duration duration;
|
||||
|
||||
Calendar calendar;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -217,4 +220,12 @@ public class SomeNewTypesBean {
|
||||
public void setDuration(Duration duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public Calendar getCalendar() {
|
||||
return calendar;
|
||||
}
|
||||
|
||||
public void setCalendar(final Calendar calendar) {
|
||||
this.calendar = calendar;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.tests.model.types.SomeNewTypesBean;
|
||||
import java.io.File;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.*;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -44,6 +45,7 @@ public class TestNewTypes extends BaseTestCase {
|
||||
bean.setPath(Paths.get(TEMP_PATH));
|
||||
bean.setPeriod(Period.of(4,3,2));
|
||||
bean.setDuration(Duration.ofMinutes(5));
|
||||
bean.setCalendar(Calendar.getInstance());
|
||||
|
||||
|
||||
DB.save(bean);
|
||||
@@ -104,6 +106,10 @@ public class TestNewTypes extends BaseTestCase {
|
||||
list = DB.find(SomeNewTypesBean.class).where().eq("duration", Duration.ofMinutes(5)).findList();
|
||||
assertThat(list).isNotEmpty();
|
||||
|
||||
// Calendar.getInstance() returns an implementation, which then has to be remapped to ScalarTypeCalendar
|
||||
list = DB.find(SomeNewTypesBean.class).where().le("calendar", Calendar.getInstance()).findList();
|
||||
assertThat(list).isNotEmpty();
|
||||
|
||||
SomeNewTypesBean fetched = DB.find(SomeNewTypesBean.class, bean.getId());
|
||||
|
||||
assertEquals(bean.getZoneId(), fetched.getZoneId());
|
||||
@@ -122,6 +128,7 @@ public class TestNewTypes extends BaseTestCase {
|
||||
assertEquals(bean.getPath(), fetched.getPath());
|
||||
assertEquals(bean.getPeriod(), fetched.getPeriod());
|
||||
assertEquals(bean.getDuration(), fetched.getDuration());
|
||||
assertEquals(bean.getCalendar(), fetched.getCalendar());
|
||||
|
||||
|
||||
String asJson = DB.json().toJson(fetched);
|
||||
@@ -139,10 +146,10 @@ public class TestNewTypes extends BaseTestCase {
|
||||
assertThat(toBean.getOffsetDateTime()).isEqualToIgnoringNanos(bean.getOffsetDateTime());
|
||||
assertEquals(bean.getLocalTime().toSecondOfDay(), toBean.getLocalTime().toSecondOfDay());
|
||||
assertEquals(bean.getInstant().toEpochMilli() / 1000, toBean.getInstant().toEpochMilli() / 1000);
|
||||
// FIXME: This test fails on Windows with: expected:<\tmp> but was:<C:\tmp>
|
||||
assertEquals(bean.getPath(), toBean.getPath());
|
||||
assertEquals(bean.getPeriod(), toBean.getPeriod());
|
||||
assertEquals(bean.getDuration(), toBean.getDuration());
|
||||
assertEquals(bean.getCalendar(), toBean.getCalendar());
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user