diff --git a/src/main/java/com/avaje/ebean/config/JsonConfig.java b/src/main/java/com/avaje/ebean/config/JsonConfig.java new file mode 100644 index 000000000..a813be3fe --- /dev/null +++ b/src/main/java/com/avaje/ebean/config/JsonConfig.java @@ -0,0 +1,29 @@ +package com.avaje.ebean.config; + +/** + * Configuration for JSON features. + */ +public abstract class JsonConfig { + + /** + * Defined the format used for DateTime types. + */ + public enum DateTime { + + /** + * Format as epoch millis. + */ + MILLIS, + + /** + * Format as epoch with nanos. + */ + NANOS, + + /** + * Format as ISO-8601 date format. + */ + ISO8601 + } + +} diff --git a/src/main/java/com/avaje/ebean/config/ServerConfig.java b/src/main/java/com/avaje/ebean/config/ServerConfig.java index 5618389c3..a7e322eb7 100644 --- a/src/main/java/com/avaje/ebean/config/ServerConfig.java +++ b/src/main/java/com/avaje/ebean/config/ServerConfig.java @@ -121,6 +121,11 @@ public class ServerConfig { */ private AutofetchConfig autofetchConfig = new AutofetchConfig(); + /** + * The JSON format used for DateTime types. Default to millis. + */ + private JsonConfig.DateTime jsonDateTime = JsonConfig.DateTime.MILLIS; + /** * The database platform name. Used to imply a DatabasePlatform to use. */ @@ -270,6 +275,20 @@ public class ServerConfig { this.jsonFactory = jsonFactory; } + /** + * Return the JSON format used for DateTime types. + */ + public JsonConfig.DateTime getJsonDateTime() { + return jsonDateTime; + } + + /** + * Set the JSON format to use for DateTime types. + */ + public void setJsonDateTime(JsonConfig.DateTime jsonDateTime) { + this.jsonDateTime = jsonDateTime; + } + /** * Return the name of the EbeanServer. */ @@ -1368,6 +1387,13 @@ public class ServerConfig { lazyLoadBatchSize = p.getInt("lazyLoadBatchSize", 1); queryBatchSize = p.getInt("queryBatchSize", DEFAULT_QUERY_BATCH_SIZE); + String jsonDateTimeFormat = p.get("jsonDateTime", null); + if (jsonDateTimeFormat != null) { + jsonDateTime = JsonConfig.DateTime.valueOf(jsonDateTimeFormat); + } else { + jsonDateTime = JsonConfig.DateTime.MILLIS; + } + ddlGenerate = p.getBoolean("ddl.generate", false); ddlRun = p.getBoolean("ddl.run", false); diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java index 7b0d79fb5..fa7a5ddaf 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java @@ -41,29 +41,6 @@ import com.fasterxml.jackson.core.JsonToken; */ public class BeanProperty implements ElPropertyValue { - /** - * Advanced bean deployment. To exclude this property from update where - * clause. - */ - public static final String EXCLUDE_FROM_UPDATE_WHERE = "EXCLUDE_FROM_UPDATE_WHERE"; - - /** - * Advanced bean deployment. To exclude this property from delete where - * clause. - */ - public static final String EXCLUDE_FROM_DELETE_WHERE = "EXCLUDE_FROM_DELETE_WHERE"; - - /** - * Advanced bean deployment. To exclude this property from insert. - */ - public static final String EXCLUDE_FROM_INSERT = "EXCLUDE_FROM_INSERT"; - - /** - * Advanced bean deployment. To exclude this property from update set - * clause. - */ - public static final String EXCLUDE_FROM_UPDATE = "EXCLUDE_FROM_UPDATE"; - /** * Flag to mark this at part of the unique id. */ @@ -488,15 +465,7 @@ public class BeanProperty implements ElPropertyValue { * Return true if the underlying type is mutable. */ public boolean isMutableScalarType() { - if (scalarType == null) { - return false; - } - return scalarType.isMutable(); - } - - public void copyProperty(EntityBean sourceBean, EntityBean destBean) { - Object value = getValue(sourceBean); - setValue(destBean, value); + return scalarType != null && scalarType.isMutable(); } /** @@ -506,10 +475,6 @@ public class BeanProperty implements ElPropertyValue { return descriptor.getEncryptKey(this); } - public String getDecryptProperty() { - return dbEncryptFunction.getDecryptSql(this.getName()); - } - public String getDecryptProperty(String propertyName) { return dbEncryptFunction.getDecryptSql(propertyName); } @@ -575,24 +540,6 @@ public class BeanProperty implements ElPropertyValue { return owningType.isAssignableFrom(type); } - public Object readSetOwning(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { - - try { - Object value = scalarType.read(ctx.getDataReader()); - if (value == null || bean == null) { - // not setting the value... - } else { - if (owningType.equals(type)) { - setValue(bean, value); - } - } - return value; - } catch (Exception e) { - String msg = "Error readSet on " + descriptor + "." + name; - throw new PersistenceException(msg, e); - } - } - public void loadIgnore(DbReadContext ctx) { scalarType.loadIgnore(ctx.getDataReader()); } @@ -616,16 +563,13 @@ public class BeanProperty implements ElPropertyValue { public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { try { - Object value = scalarType.read(ctx.getDataReader()); - if (bean == null || (type != null && !owningType.isAssignableFrom(type))) { - // not setting the value... - } else { - setValue(bean, value); - } - return value; + Object value = scalarType.read(ctx.getDataReader()); + if (bean != null && ((type == null || owningType.isAssignableFrom(type)))) { + setValue(bean, value); + } + return value; } catch (Exception e) { - String msg = "Error readSet on " + descriptor + "." + name; - throw new PersistenceException(msg, e); + throw new PersistenceException("Error readSet on " + descriptor + "." + name, e); } } @@ -653,21 +597,6 @@ public class BeanProperty implements ElPropertyValue { return scalarType.readData(dataInput); } - public boolean isCascadeValidate() { - return cascadeValidate; - } - - /** - * Checks to see if a bean is a reference (will be lazy loaded) or a - * BeanCollection that has not yet been populated. - *

- * For base types this returns true. - *

- */ - public boolean isValueLoaded(Object value) { - return true; - } - public BeanProperty getBeanProperty() { return this; } @@ -737,11 +666,9 @@ public class BeanProperty implements ElPropertyValue { } } - private static Object[] NO_ARGS = new Object[0]; - public Object getCacheDataValue(EntityBean bean) { return getValue(bean); - } + } public void setCacheDataValue(EntityBean bean, Object cacheData) { setValue(bean, cacheData); @@ -759,19 +686,6 @@ public class BeanProperty implements ElPropertyValue { throw new RuntimeException(msg, ex); } } - - /** - * Explicitly use reflection to get value. - */ - public Object getValueViaReflection(Object bean) { - try { - return readMethod.invoke(bean, NO_ARGS); - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; - throw new RuntimeException(msg, ex); - } - } public Object getValueIntercept(EntityBean bean) { try { @@ -826,13 +740,6 @@ public class BeanProperty implements ElPropertyValue { return name; } - /** - * This is a full ElGetValue. - */ - public boolean isDeployOnly() { - return false; - } - @Override public boolean containsFormulaWithJoin() { return formula && sqlFormulaJoin != null; @@ -902,7 +809,7 @@ public class BeanProperty implements ElPropertyValue { /** * Return the scalarType. */ - public ScalarType getScalarType() { + public ScalarType getScalarType() { return scalarType; } @@ -923,7 +830,7 @@ public class BeanProperty implements ElPropertyValue { } public Object parseDateTime(long systemTimeMillis) { - return scalarType.parseDateTime(systemTimeMillis); + return scalarType.convertFromMillis(systemTimeMillis); } /** @@ -1031,10 +938,6 @@ public class BeanProperty implements ElPropertyValue { return version; } - public String getDeployProperty() { - return dbColumn; - } - /** * The database column name this is mapped to. */ @@ -1168,13 +1071,6 @@ public class BeanProperty implements ElPropertyValue { return embedded; } - /** - * Return an extra attribute set on this property. - */ - public String getExtraAttribute(String key) { - return extraAttributeMap.get(key); - } - /** * Return the default value. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index 0745915b7..2d8d42c55 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -385,12 +385,6 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { return null; } - - @Override - public boolean isValueLoaded(Object value) { - return !(value instanceof BeanCollection) || ((BeanCollection) value).isPopulated(); - } - public void add(BeanCollection collection, EntityBean bean) { help.add(collection, bean); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index 997b04659..2a463971b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -265,14 +265,6 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { } } - @Override - public boolean isValueLoaded(Object value) { - if (value instanceof EntityBean) { - return ((EntityBean) value)._ebean_getIntercept().isLoaded(); - } - return true; - } - /** * Return meta data for the deployment of the embedded bean specific to this * property. diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChain.java b/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChain.java index c23dc7465..af9db6390 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChain.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChain.java @@ -215,7 +215,7 @@ public class ElPropertyChain implements ElPropertyValue { } public Object parseDateTime(long systemTimeMillis) { - return scalarType.parseDateTime(systemTimeMillis); + return scalarType.convertFromMillis(systemTimeMillis); } public StringParser getStringParser() { diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/DateTimeJsonParser.java b/src/main/java/com/avaje/ebeaninternal/server/type/DateTimeJsonParser.java index decc9ccbd..df303c431 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/DateTimeJsonParser.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/DateTimeJsonParser.java @@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.type; import java.sql.Timestamp; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.Date; import java.util.TimeZone; public class DateTimeJsonParser { @@ -31,4 +32,7 @@ public class DateTimeJsonParser { } } + public String format(Date value) { + return dtFormat().format(value); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/DecimalUtils.java b/src/main/java/com/avaje/ebeaninternal/server/type/DecimalUtils.java new file mode 100644 index 000000000..235e94882 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/type/DecimalUtils.java @@ -0,0 +1,89 @@ +package com.avaje.ebeaninternal.server.type; + +/* + * Copyright 2013 FasterXML.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.time.Duration; +import java.time.Instant; + +/** + * Utilities to aid in the translation of decimal types to/from multiple parts. + * + * @author Nick Williams + * @since 2.2.0 + */ +public final class DecimalUtils { + + private static final char[] ZEROES = new char[]{'0', '0', '0', '0', '0', '0', '0', '0', '0'}; + + private static final BigDecimal ONE_BILLION = new BigDecimal(1000000000L); + + private DecimalUtils() { + throw new RuntimeException("DecimalUtils cannot be instantiated."); + } + + public static Duration toDuration(BigDecimal value) { + + long seconds = value.longValue(); + int nanoseconds = extractNanosecondDecimal(value, seconds); + + return Duration.ofSeconds(seconds, nanoseconds); + } + + public static BigDecimal toDecimal(Duration instant) { + return new BigDecimal(toDecimal(instant.getSeconds(), instant.getNano())); + } + + public static Timestamp toTimestamp(BigDecimal value) { + + long seconds = value.longValue(); + int nanoseconds = extractNanosecondDecimal(value, seconds); + + Timestamp ts = new Timestamp(seconds * 1000); + ts.setNanos(nanoseconds); + return ts; + } + + public static BigDecimal toDecimal(Timestamp instant) { + long millis = instant.getTime(); + long secs = millis/1000; + return new BigDecimal(toDecimal(secs, instant.getNanos())); + } + + public static Instant toInstant(BigDecimal value) { + + long seconds = value.longValue(); + int nanoseconds = DecimalUtils.extractNanosecondDecimal(value, seconds); + return Instant.ofEpochSecond(seconds, nanoseconds); + } + + public static BigDecimal toDecimal(Instant instant) { + return new BigDecimal(toDecimal(instant.getEpochSecond(), instant.getNano())); + } + + public static String toDecimal(long seconds, int nanoseconds) { + StringBuilder string = new StringBuilder(Integer.toString(nanoseconds)); + if (string.length() < 9) + string.insert(0, ZEROES, 0, 9 - string.length()); + return seconds + "." + string; + } + + public static int extractNanosecondDecimal(BigDecimal value, long integer) { + return value.subtract(new BigDecimal(integer)).multiply(ONE_BILLION).intValue(); + } +} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeFactory.java b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeFactory.java index dab788a76..ffbd27410 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeFactory.java @@ -4,6 +4,7 @@ import java.math.BigInteger; import java.sql.Types; import java.util.Calendar; +import com.avaje.ebean.config.JsonConfig; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebeaninternal.server.core.BasicTypeConverter; @@ -80,27 +81,27 @@ public class DefaultTypeFactory { /** * Create the default ScalarType for java.util.Date. */ - public ScalarType createUtilDate() { + public ScalarType createUtilDate(JsonConfig.DateTime mode) { // by default map anonymous java.util.Date to java.sql.Timestamp. // String mapType = // properties.getProperty("type.mapping.java.util.Date","timestamp"); int utilDateType = getTemporalMapType("timestamp"); - return createUtilDate(utilDateType); + return createUtilDate(mode, utilDateType); } /** * Create a ScalarType for java.util.Date explicitly specifying the type to * map to. */ - public ScalarType createUtilDate(int utilDateType) { + public ScalarType createUtilDate(JsonConfig.DateTime mode, int utilDateType) { switch (utilDateType) { case Types.DATE: return new ScalarTypeUtilDate.DateType(); case Types.TIMESTAMP: - return new ScalarTypeUtilDate.TimestampType(); + return new ScalarTypeUtilDate.TimestampType(mode); default: throw new RuntimeException("Invalid type " + utilDateType); @@ -110,23 +111,19 @@ public class DefaultTypeFactory { /** * Create the default ScalarType for java.util.Calendar. */ - public ScalarType createCalendar() { - // by default map anonymous java.util.Calendar to java.sql.Timestamp. - // String mapType = - // properties.getProperty("type.mapping.java.util.Calendar", - // "timestamp"); - int jdbcType = getTemporalMapType("timestamp"); + public ScalarType createCalendar(JsonConfig.DateTime mode) { - return createCalendar(jdbcType); + int jdbcType = getTemporalMapType("timestamp"); + return createCalendar(mode, jdbcType); } /** * Create a ScalarType for java.util.Calendar explicitly specifying the type * to map to. */ - public ScalarType createCalendar(int jdbcType) { + public ScalarType createCalendar(JsonConfig.DateTime mode, int jdbcType) { - return new ScalarTypeCalendar(jdbcType); + return new ScalarTypeCalendar(mode, jdbcType); } private int getTemporalMapType(String mapType) { 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 bbff03a0c..1d8b4533d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java @@ -23,6 +23,7 @@ import java.util.TimeZone; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import com.avaje.ebean.config.*; import org.joda.time.DateMidnight; import org.joda.time.DateTime; import org.joda.time.LocalDate; @@ -33,10 +34,6 @@ import org.slf4j.LoggerFactory; import com.avaje.ebean.annotation.EnumMapping; import com.avaje.ebean.annotation.EnumValue; -import com.avaje.ebean.config.CompoundType; -import com.avaje.ebean.config.CompoundTypeProperty; -import com.avaje.ebean.config.ScalarTypeConverter; -import com.avaje.ebean.config.ServerConfig; import com.avaje.ebeaninternal.api.ClassUtil; import com.avaje.ebeaninternal.server.core.BootupClasses; import com.avaje.ebeaninternal.server.lib.util.StringHelper; @@ -103,8 +100,6 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { private final ScalarType dateType = new ScalarTypeDate(); - private final ScalarType timestampType = new ScalarTypeTimestamp(); - private final ScalarType inetAddressType = new ScalarTypeInetAddress(); private final ScalarType urlType = new ScalarTypeURL(); private final ScalarType uriType = new ScalarTypeURI(); @@ -116,7 +111,6 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { private final ScalarType classType = new ScalarTypeClass(); - private final ScalarTypeLongToTimestamp longToTimestamp = new ScalarTypeLongToTimestamp(); private final List> customScalarTypes = new ArrayList>(); @@ -126,6 +120,8 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { private final ReflectionBasedTypeBuilder reflectScalarBuilder; + private final JsonConfig.DateTime jsonDateTime; + /** * Create the DefaultTypeManager. */ @@ -134,6 +130,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { int clobType = config == null ? Types.CLOB : config.getDatabasePlatform().getClobDbType(); int blobType = config == null ? Types.BLOB : config.getDatabasePlatform().getBlobDbType(); + this.jsonDateTime = config.getJsonDateTime(); this.checkImmutable = new CheckImmutable(this); this.reflectScalarBuilder = new ReflectionBasedTypeBuilder(this); @@ -146,12 +143,12 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { this.extraTypeFactory = new DefaultTypeFactory(config); - initialiseStandard(clobType, blobType, config.isUuidStoreAsBinary()); - initialiseJavaTimeTypes(); - initialiseJodaTypes(); + initialiseStandard(jsonDateTime, clobType, blobType, config.isUuidStoreAsBinary()); + initialiseJavaTimeTypes(jsonDateTime); + initialiseJodaTypes(jsonDateTime); if (bootupClasses != null) { - initialiseCustomScalarTypes(bootupClasses); + initialiseCustomScalarTypes(jsonDateTime, bootupClasses); initialiseScalarConverters(bootupClasses); initialiseCompoundTypes(bootupClasses); } @@ -313,19 +310,15 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { if (jdbcType == 0 || scalarType.getJdbcType() == jdbcType) { // matching type return (ScalarType) scalarType; - } else { - // sometime like java.util.Date or java.util.Calendar - // that that does not map to the same jdbc type as the - // server wide settings. } } // a util Date with jdbcType not matching server wide settings if (type.equals(java.util.Date.class)) { - return (ScalarType) extraTypeFactory.createUtilDate(jdbcType); + return (ScalarType) extraTypeFactory.createUtilDate(jsonDateTime, jdbcType); } // a Calendar with jdbcType not matching server wide settings if (type.equals(java.util.Calendar.class)) { - return (ScalarType) extraTypeFactory.createCalendar(jdbcType); + return (ScalarType) extraTypeFactory.createCalendar(jsonDateTime, jdbcType); } String msg = "Unmatched ScalarType for " + type + " jdbcType:" + jdbcType; @@ -417,7 +410,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { public ScalarType createEnumScalarType(Class enumType) { // get the mapping information from EnumMapping - EnumMapping enumMapping = (EnumMapping) enumType.getAnnotation(EnumMapping.class); + EnumMapping enumMapping = enumType.getAnnotation(EnumMapping.class); if (enumMapping == null) { // look for EnumValue annotations instead return createEnumScalarType2(enumType); @@ -468,7 +461,9 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { * interface and register it with this TypeManager. *

*/ - protected void initialiseCustomScalarTypes(BootupClasses bootupClasses) { + protected void initialiseCustomScalarTypes(JsonConfig.DateTime mode, BootupClasses bootupClasses) { + + ScalarTypeLongToTimestamp longToTimestamp = new ScalarTypeLongToTimestamp(mode); customScalarTypes.add(longToTimestamp); @@ -604,12 +599,12 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { return propParamTypes[1]; } - protected void initialiseJavaTimeTypes() { + protected void initialiseJavaTimeTypes(JsonConfig.DateTime mode) { if (ClassUtil.isPresent("java.time.LocalDate", this.getClass())) { logger.debug("Registering java.time data types"); typeMap.put(java.time.LocalDate.class, new ScalarTypeLocalDate()); - typeMap.put(java.time.LocalDateTime.class, new ScalarTypeLocalDateTime()); - typeMap.put(OffsetDateTime.class, new ScalarTypeOffsetDateTime()); + typeMap.put(java.time.LocalDateTime.class, new ScalarTypeLocalDateTime(mode)); + typeMap.put(OffsetDateTime.class, new ScalarTypeOffsetDateTime(mode)); } } @@ -617,16 +612,16 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { * Detect if Joda classes are in the classpath and if so register the Joda * data types. */ - protected void initialiseJodaTypes() { + protected void initialiseJodaTypes(JsonConfig.DateTime mode) { // detect if Joda classes are in the classpath if (ClassUtil.isPresent("org.joda.time.LocalDateTime", this.getClass())) { // Joda classes are in the classpath so register the types logger.debug("Registering Joda data types"); - typeMap.put(LocalDateTime.class, new ScalarTypeJodaLocalDateTime()); + typeMap.put(LocalDateTime.class, new ScalarTypeJodaLocalDateTime(mode)); + typeMap.put(DateTime.class, new ScalarTypeJodaDateTime(mode)); typeMap.put(LocalDate.class, new ScalarTypeJodaLocalDate()); typeMap.put(LocalTime.class, new ScalarTypeJodaLocalTime()); - typeMap.put(DateTime.class, new ScalarTypeJodaDateTime()); typeMap.put(DateMidnight.class, new ScalarTypeJodaDateMidnight()); } } @@ -635,12 +630,12 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { * Register all the standard types supported. This is the standard JDBC types * plus some other common types such as java.util.Date and java.util.Calendar. */ - protected void initialiseStandard(int platformClobType, int platformBlobType, boolean binaryUUID) { + protected void initialiseStandard(JsonConfig.DateTime mode, int platformClobType, int platformBlobType, boolean binaryUUID) { - ScalarType utilDateType = extraTypeFactory.createUtilDate(); + ScalarType utilDateType = extraTypeFactory.createUtilDate(mode); typeMap.put(java.util.Date.class, utilDateType); - ScalarType calType = extraTypeFactory.createCalendar(); + ScalarType calType = extraTypeFactory.createCalendar(mode); typeMap.put(Calendar.class, calType); ScalarType mathBigIntType = extraTypeFactory.createMathBigInteger(); @@ -655,8 +650,6 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { if (booleanType.getJdbcType() == Types.BIT) { // for MapBeans ... BIT types are assumed to be booleans nativeMap.put(Types.BIT, booleanType); - } else { - // boolean mapping to Types.Integer, Types.VARCHAR or Types.Boolean } // Store UUID as binary(16) or varchar(40) @@ -744,6 +737,9 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable { nativeMap.put(Types.TIME, timeType); typeMap.put(Date.class, dateType); nativeMap.put(Types.DATE, dateType); + + ScalarType timestampType = new ScalarTypeTimestamp(mode); + typeMap.put(Timestamp.class, timestampType); nativeMap.put(Types.TIMESTAMP, timestampType); diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java index c49cfcb00..b59bb38f0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java @@ -162,19 +162,11 @@ public interface ScalarType extends StringParser, StringFormatter, ScalarData */ public T parse(String value); - /** - * Convert the systemTimeMillis into the appropriate java object. - *

- * For non dateTime types this will throw an exception. - *

- */ - public T parseDateTime(long dateTime); - /** * Return true if the type can accept long systemTimeMillis input. *

* This is used to determine if is is sensible to use the - * {@link #parseDateTime(long)} method. + * {@link #convertFromMillis(long)} method. *

*

* This includes the Date, Calendar, sql Date, Time, Timestamp, JODA types @@ -184,24 +176,32 @@ public interface ScalarType extends StringParser, StringFormatter, ScalarData */ public boolean isDateTimeCapable(); - /** + /** + * Convert the systemTimeMillis into the appropriate java object. + *

+ * For non dateTime types this will throw an exception. + *

+ */ + public T convertFromMillis(long dateTime); + + /** * Read the value from binary input. */ - public Object readData(DataInput dataInput) throws IOException; + public T readData(DataInput dataInput) throws IOException; /** * Write the value to binary output. */ - public void writeData(DataOutput dataOutput, Object v) throws IOException; + public void writeData(DataOutput dataOutput, T v) throws IOException; /** * Read the value from JsonParser. */ - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException; + public T jsonRead(JsonParser ctx, JsonToken event) throws IOException; /** * Write the value to the JsonGenerator. */ - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException; + public void jsonWrite(JsonGenerator ctx, String name, T value) throws IOException; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java index 5d18df075..8f39b6cfd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java @@ -64,7 +64,7 @@ public abstract class ScalarTypeBaseDate extends ScalarTypeBase { return convertFromDate(date); } - public T parseDateTime(long systemTimeMillis) { + public T convertFromMillis(long systemTimeMillis) { Date ts = new Date(systemTimeMillis); return convertFromDate(ts); } @@ -74,20 +74,20 @@ public abstract class ScalarTypeBaseDate extends ScalarTypeBase { } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public T jsonRead(JsonParser ctx, JsonToken event) throws IOException { if (JsonToken.VALUE_NUMBER_INT == event) { - return parseDateTime(ctx.getLongValue()); + return convertFromMillis(ctx.getLongValue()); } else { return convertFromDate(Date.valueOf(ctx.getText())); } } - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, T value) throws IOException { long millis = convertToMillis(value); ctx.writeNumberField(name, millis); } - public Object readData(DataInput dataInput) throws IOException { + public T readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; } else { @@ -98,9 +98,8 @@ public abstract class ScalarTypeBaseDate extends ScalarTypeBase { } @SuppressWarnings("unchecked") - public void writeData(DataOutput dataOutput, Object v) throws IOException { + public void writeData(DataOutput dataOutput, T value) throws IOException { - T value = (T) v; if (value == null) { dataOutput.writeBoolean(false); } else { diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java index f3f53de1f..b267544a3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java @@ -1,39 +1,69 @@ package com.avaje.ebeaninternal.server.type; +import com.avaje.ebean.config.JsonConfig; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; + import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.math.BigDecimal; import java.sql.SQLException; import java.sql.Timestamp; import java.sql.Types; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; +import java.time.Instant; /** * Base type for DateTime types. */ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { - protected DateTimeJsonParser dateTimeParser = new DateTimeJsonParser(); - - public ScalarTypeBaseDateTime(Class type, boolean jdbcNative, int jdbcType) { + + protected final DateTimeJsonParser dateTimeParser = new DateTimeJsonParser(); + + protected final JsonConfig.DateTime mode; + + public ScalarTypeBaseDateTime(JsonConfig.DateTime mode, Class type, boolean jdbcNative, int jdbcType) { super(type, jdbcNative, jdbcType); + this.mode = mode; } - public abstract long convertToMillis(Object value); - + /** + * Convert the value to a Timestamp. + */ public abstract Timestamp convertToTimestamp(T t); + /** + * Convert to the value from a Timestamp. + */ public abstract T convertFromTimestamp(Timestamp ts); + /** + * Convert from epoch millis to the value. + */ + public abstract T convertFromMillis(long systemTimeMillis); + + /** + * Convert from the value to epoch millis. + */ + public abstract long convertToMillis(T value); + + /** + * Convert the value to time with nanos format. + */ + protected abstract String toJsonNanos(T value); + + /** + * Convert the value to ISO8601 format. + */ + protected abstract String toJsonISO8601(T value); + public void bind(DataBind b, T value) throws SQLException { if (value == null) { b.setNull(Types.TIMESTAMP); } else { - Timestamp ts = convertToTimestamp(value); - b.setTimestamp(ts); + b.setTimestamp(convertToTimestamp(value)); } } @@ -47,23 +77,49 @@ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { } } + /** + * Helper method that given epoch seconds and nanos return a JSON nanos formatted string. + */ + protected String toJsonNanos(long epochSecs, int nanos) { + return DecimalUtils.toDecimal(epochSecs, nanos); + } + @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public T jsonRead(JsonParser ctx, JsonToken event) throws IOException { - if (JsonToken.VALUE_NUMBER_INT == event) { - long millis = ctx.getLongValue(); - return parseDateTime(millis); - - } else { - String jsonDateTime = ctx.getText(); - return convertFromTimestamp(dateTimeParser.parse(jsonDateTime)); + switch (event) { + case VALUE_NUMBER_INT: { + return convertFromMillis(ctx.getLongValue()); + } + case VALUE_NUMBER_FLOAT: { + BigDecimal value = ctx.getDecimalValue(); + //Instant instant = DecimalUtils.toInstant(value); + Timestamp timestamp = DecimalUtils.toTimestamp(value); + convertFromTimestamp(timestamp);//Timestamp.from(instant)); + } + default: { + String jsonDateTime = ctx.getText(); + return convertFromTimestamp(dateTimeParser.parse(jsonDateTime)); + } } } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { - long millis = convertToMillis(value); - ctx.writeNumberField(name, millis); + public void jsonWrite(JsonGenerator generator, String name, T value) throws IOException { + + switch (mode) { + case ISO8601: { + generator.writeNumber(toJsonISO8601(value)); + break; + } + case NANOS: { + generator.writeNumber(toJsonNanos(value)); + break; + } + default: { + generator.writeNumberField(name, convertToMillis(value)); + } + } } public String formatValue(T t) { @@ -76,16 +132,12 @@ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { return convertFromTimestamp(ts); } - public T parseDateTime(long systemTimeMillis) { - Timestamp ts = new Timestamp(systemTimeMillis); - return convertFromTimestamp(ts); - } public boolean isDateTimeCapable() { return true; } - public Object readData(DataInput dataInput) throws IOException { + public T readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; } else { @@ -95,10 +147,8 @@ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { } } - @SuppressWarnings("unchecked") - public void writeData(DataOutput dataOutput, Object v) throws IOException { + public void writeData(DataOutput dataOutput, T value) throws IOException { - T value = (T) v; if (value == null) { dataOutput.writeBoolean(false); } else { diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java index 0df4a1b06..750400b95 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java @@ -86,7 +86,7 @@ public abstract class ScalarTypeBaseVarchar extends ScalarTypeBase { } @Override - public T parseDateTime(long systemTimeMillis) { + public T convertFromMillis(long systemTimeMillis) { throw new TextException("Not Supported"); } @@ -102,7 +102,7 @@ public abstract class ScalarTypeBaseVarchar extends ScalarTypeBase { } @Override - public Object readData(DataInput dataInput) throws IOException { + public T readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; } else { @@ -111,10 +111,8 @@ public abstract class ScalarTypeBaseVarchar extends ScalarTypeBase { } } - @SuppressWarnings("unchecked") - public void writeData(DataOutput dataOutput, Object v) throws IOException { + public void writeData(DataOutput dataOutput, T value) throws IOException { - T value = (T) v; if (value == null) { dataOutput.writeBoolean(false); } else { @@ -125,12 +123,12 @@ public abstract class ScalarTypeBaseVarchar extends ScalarTypeBase { } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public T jsonRead(JsonParser ctx, JsonToken event) throws IOException { return parse(ctx.getValueAsString()); } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, T value) throws IOException { ctx.writeStringField(name, format(value)); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java index 4aee53063..23d4fad29 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java @@ -17,75 +17,73 @@ import com.fasterxml.jackson.core.JsonToken; */ public class ScalarTypeBigDecimal extends ScalarTypeBase { - public ScalarTypeBigDecimal() { - super(BigDecimal.class, true, Types.DECIMAL); - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - double val = dataInput.readDouble(); - return new BigDecimal(val); - } + public ScalarTypeBigDecimal() { + super(BigDecimal.class, true, Types.DECIMAL); + } + + public BigDecimal readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + double val = dataInput.readDouble(); + return new BigDecimal(val); } + } - public void writeData(DataOutput dataOutput, Object v) throws IOException { - - BigDecimal b = (BigDecimal)v; - if (b == null){ - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - dataOutput.writeDouble(b.doubleValue()); - } + public void writeData(DataOutput dataOutput, BigDecimal b) throws IOException { + + if (b == null) { + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeDouble(b.doubleValue()); } - - public void bind(DataBind b, BigDecimal value) throws SQLException { - if (value == null){ - b.setNull(Types.DECIMAL); - } else { - b.setBigDecimal(value); - } - } + } - public BigDecimal read(DataReader dataReader) throws SQLException { - - return dataReader.getBigDecimal(); - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.toBigDecimal(value); - } - - public BigDecimal toBeanType(Object value) { - return BasicTypeConverter.toBigDecimal(value); - } - - public String formatValue(BigDecimal t) { - return t.toPlainString(); + public void bind(DataBind b, BigDecimal value) throws SQLException { + if (value == null) { + b.setNull(Types.DECIMAL); + } else { + b.setBigDecimal(value); } + } - public BigDecimal parse(String value) { - return new BigDecimal(value); - } + public BigDecimal read(DataReader dataReader) throws SQLException { - public BigDecimal parseDateTime(long systemTimeMillis) { - return BigDecimal.valueOf(systemTimeMillis); - } + return dataReader.getBigDecimal(); + } - public boolean isDateTimeCapable() { - return true; - } + public Object toJdbcType(Object value) { + return BasicTypeConverter.toBigDecimal(value); + } + + public BigDecimal toBeanType(Object value) { + return BasicTypeConverter.toBigDecimal(value); + } + + public String formatValue(BigDecimal t) { + return t.toPlainString(); + } + + public BigDecimal parse(String value) { + return new BigDecimal(value); + } + + public BigDecimal convertFromMillis(long systemTimeMillis) { + return BigDecimal.valueOf(systemTimeMillis); + } + + public boolean isDateTimeCapable() { + return true; + } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public BigDecimal jsonRead(JsonParser ctx, JsonToken event) throws IOException { return ctx.getDecimalValue(); } - - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { - ctx.writeNumberField(name, (BigDecimal)value); + + public void jsonWrite(JsonGenerator ctx, String name, BigDecimal value) throws IOException { + ctx.writeNumberField(name, (BigDecimal) value); } - } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java index 5a9db4c98..67780c3bb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java @@ -261,7 +261,7 @@ public class ScalarTypeBoolean { return Boolean.valueOf(value); } - public Boolean parseDateTime(long systemTimeMillis) { + public Boolean convertFromMillis(long systemTimeMillis) { throw new TextException("Not Supported"); } @@ -269,32 +269,30 @@ public class ScalarTypeBoolean { return false; } - public Object readData(DataInput dataInput) throws IOException { + public Boolean readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; } else { - boolean val = dataInput.readBoolean(); - return Boolean.valueOf(val); + return dataInput.readBoolean(); } } - public void writeData(DataOutput dataOutput, Object v) throws IOException { + public void writeData(DataOutput dataOutput, Boolean val) throws IOException { - Boolean val = (Boolean) v; if (val == null) { dataOutput.writeBoolean(false); } else { dataOutput.writeBoolean(true); - dataOutput.writeBoolean(val.booleanValue()); + dataOutput.writeBoolean(val); } } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) { + public Boolean jsonRead(JsonParser ctx, JsonToken event) { return JsonToken.VALUE_TRUE == event ? Boolean.TRUE : Boolean.FALSE; } - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, Boolean value) throws IOException { ctx.writeBooleanField(name, (Boolean) value); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java index f92cccf94..1ac6dfcf2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java @@ -42,12 +42,12 @@ public class ScalarTypeByte extends ScalarTypeBase { } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, Byte value) throws IOException { throw new IOException("Not supported"); } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public Byte jsonRead(JsonParser ctx, JsonToken event) throws IOException { throw new IOException("Not supported"); } @@ -59,7 +59,7 @@ public class ScalarTypeByte extends ScalarTypeBase { throw new TextException("Not supported"); } - public Byte parseDateTime(long systemTimeMillis) { + public Byte convertFromMillis(long systemTimeMillis) { throw new TextException("Not Supported"); } @@ -67,18 +67,16 @@ public class ScalarTypeByte extends ScalarTypeBase { return false; } - public Object readData(DataInput dataInput) throws IOException { + public Byte readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; } else { - byte val = dataInput.readByte(); - return Byte.valueOf(val); + return dataInput.readByte(); } } - public void writeData(DataOutput dataOutput, Object v) throws IOException { + public void writeData(DataOutput dataOutput, Byte val) throws IOException { - Byte val = (Byte) v; if (val == null) { dataOutput.writeBoolean(false); } else { diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java index 73d198fbc..2f03c15aa 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java @@ -45,12 +45,12 @@ public abstract class ScalarTypeBytesBase extends ScalarTypeBase { } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, byte[] value) throws IOException { ctx.writeBinaryField(name, (byte[]) value); } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public byte[] jsonRead(JsonParser ctx, JsonToken event) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(500); ctx.readBinaryValue(out); return out.toByteArray(); @@ -64,7 +64,7 @@ public abstract class ScalarTypeBytesBase extends ScalarTypeBase { throw new TextException("Not supported"); } - public byte[] parseDateTime(long systemTimeMillis) { + public byte[] convertFromMillis(long systemTimeMillis) { throw new TextException("Not supported"); } @@ -72,7 +72,7 @@ public abstract class ScalarTypeBytesBase extends ScalarTypeBase { return false; } - public Object readData(DataInput dataInput) throws IOException { + public byte[] readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; } else { @@ -83,10 +83,11 @@ public abstract class ScalarTypeBytesBase extends ScalarTypeBase { } } - public void writeData(DataOutput dataOutput, Object v) throws IOException { + public void writeData(DataOutput dataOutput, byte[] v) throws IOException { if (v == null) { dataOutput.writeBoolean(false); } else { + dataOutput.writeBoolean(true); byte[] bytes = convertToBytes(v); dataOutput.writeInt(bytes.length); dataOutput.write(bytes); diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java index e2fe6aa48..4dfbc2b1f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java @@ -67,12 +67,12 @@ public class ScalarTypeBytesEncrypted implements ScalarType { } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, byte[] value) throws IOException { ctx.writeBinaryField(name, (byte[]) value); } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public byte[] jsonRead(JsonParser ctx, JsonToken event) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(500); ctx.readBinaryValue(out); return out.toByteArray(); @@ -90,8 +90,8 @@ public class ScalarTypeBytesEncrypted implements ScalarType { return baseType.parse(value); } - public byte[] parseDateTime(long systemTimeMillis) { - return baseType.parseDateTime(systemTimeMillis); + public byte[] convertFromMillis(long systemTimeMillis) { + return baseType.convertFromMillis(systemTimeMillis); } public byte[] read(DataReader dataReader) throws SQLException { @@ -113,17 +113,26 @@ public class ScalarTypeBytesEncrypted implements ScalarType { baseType.accumulateScalarTypes(propName, list); } - public Object readData(DataInput dataInput) throws IOException { - int len = dataInput.readInt(); - byte[] value = new byte[len]; - dataInput.readFully(value); - return value; + public byte[] readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + int len = dataInput.readInt(); + byte[] value = new byte[len]; + dataInput.readFully(value); + return value; + } } - public void writeData(DataOutput dataOutput, Object v) throws IOException { - byte[] value = (byte[]) v; - dataOutput.writeInt(value.length); - dataOutput.write(value); + public void writeData(DataOutput dataOutput, byte[] value) throws IOException { + + if (value == null) { + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeInt(value.length); + dataOutput.write(value); + } } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java index ed214945d..ee790cc87 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java @@ -6,6 +6,7 @@ import java.sql.Timestamp; import java.sql.Types; import java.util.Calendar; +import com.avaje.ebean.config.JsonConfig; import com.avaje.ebeaninternal.server.core.BasicTypeConverter; /** @@ -13,25 +14,32 @@ import com.avaje.ebeaninternal.server.core.BasicTypeConverter; */ public class ScalarTypeCalendar extends ScalarTypeBaseDateTime { - public ScalarTypeCalendar(int jdbcType) { - super(Calendar.class, false, jdbcType); + public ScalarTypeCalendar(JsonConfig.DateTime mode, int jdbcType) { + super(mode, Calendar.class, false, jdbcType); } public void bind(DataBind b, Calendar value) throws SQLException { if (value == null) { b.setNull(Types.TIMESTAMP); } else { - Calendar date = (Calendar) value; + if (jdbcType == Types.TIMESTAMP) { - Timestamp timestamp = new Timestamp(date.getTimeInMillis()); + Timestamp timestamp = new Timestamp(value.getTimeInMillis()); b.setTimestamp(timestamp); } else { - Date d = new Date(date.getTimeInMillis()); + Date d = new Date(value.getTimeInMillis()); b.setDate(d); } } } + @Override + public Calendar convertFromMillis(long systemTimeMillis) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(systemTimeMillis); + return calendar; + } + @Override public Calendar convertFromTimestamp(Timestamp ts) { Calendar calendar = Calendar.getInstance(); @@ -40,8 +48,18 @@ public class ScalarTypeCalendar extends ScalarTypeBaseDateTime { } @Override - public long convertToMillis(Object value) { - return ((Calendar) value).getTimeInMillis(); + protected String toJsonNanos(Calendar value) { + return String.valueOf(value.getTime()); + } + + @Override + protected String toJsonISO8601(Calendar value) { + return dateTimeParser.format(value.getTime()); + } + + @Override + public long convertToMillis(Calendar value) { + return value.getTimeInMillis(); } @Override @@ -49,10 +67,12 @@ public class ScalarTypeCalendar extends ScalarTypeBaseDateTime { return new Timestamp(t.getTimeInMillis()); } + @Override public Object toJdbcType(Object value) { return BasicTypeConverter.convert(value, jdbcType); } + @Override public Calendar toBeanType(Object value) { return BasicTypeConverter.toCalendar(value); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java index f9e96f15c..ec947202f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java @@ -64,11 +64,11 @@ public class ScalarTypeCharArray extends ScalarTypeBaseVarchar { } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public char[] jsonRead(JsonParser ctx, JsonToken event) throws IOException { return ctx.getValueAsString().toCharArray(); } - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, char[] value) throws IOException { ctx.writeStringField(name, String.valueOf(value)); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDouble.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDouble.java index fa27077d4..10b5c97fe 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDouble.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDouble.java @@ -55,7 +55,7 @@ public class ScalarTypeDouble extends ScalarTypeBase { } @Override - public Double parseDateTime(long systemTimeMillis) { + public Double convertFromMillis(long systemTimeMillis) { return Double.valueOf(systemTimeMillis); } @@ -65,34 +65,32 @@ public class ScalarTypeDouble extends ScalarTypeBase { } @Override - public Object readData(DataInput dataInput) throws IOException { + public Double readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; } else { - double val = dataInput.readDouble(); - return Double.valueOf(val); + return dataInput.readDouble(); } } @Override - public void writeData(DataOutput dataOutput, Object v) throws IOException { + public void writeData(DataOutput dataOutput, Double value) throws IOException { - Double value = (Double) v; if (value == null) { dataOutput.writeBoolean(false); } else { dataOutput.writeBoolean(true); - dataOutput.writeDouble(value.doubleValue()); + dataOutput.writeDouble(value); } } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public Double jsonRead(JsonParser ctx, JsonToken event) throws IOException { return ctx.getDoubleValue(); } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { - ctx.writeNumberField(name, (Double) value); + public void jsonWrite(JsonGenerator ctx, String name, Double value) throws IOException { + ctx.writeNumberField(name, value); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDuration.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDuration.java new file mode 100644 index 000000000..23cbbcd42 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDuration.java @@ -0,0 +1,104 @@ +package com.avaje.ebeaninternal.server.type; + +import com.avaje.ebean.text.TextException; +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; +import java.time.Duration; +import java.time.LocalTime; + +/** + * ScalarType for java.time.Duration + */ +public class ScalarTypeDuration extends ScalarTypeBase { + + public ScalarTypeDuration() { + super(Duration.class, false, Types.BIGINT); + } + + protected ScalarTypeDuration(int jdbcType) { + super(Duration.class, false, jdbcType); + } + + @Override + public void bind(DataBind bind, Duration value) throws SQLException { + if (value == null) { + bind.setNull(Types.BIGINT); + } else { + bind.setLong(value.getSeconds()); + } + } + + @Override + public Duration read(DataReader dataReader) throws SQLException { + return Duration.ofSeconds(dataReader.getLong()); + } + + @Override + public Duration readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + return Duration.ofSeconds(dataInput.readLong()); + } + } + + @Override + public void writeData(DataOutput dataOutput, Duration value) throws IOException { + if (value == null) { + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeLong(value.getSeconds()); + } + } + + @Override + public Object toJdbcType(Object value) { + if (value instanceof Long) return value; + return ((Duration)value).getSeconds(); + } + + @Override + public Duration toBeanType(Object value) { + if (value instanceof Duration) return (Duration) value; + return Duration.ofSeconds(BasicTypeConverter.toLong(value)); + } + + @Override + public String formatValue(Duration v) { + return v.toString(); + } + + @Override + public Duration parse(String value) { + return Duration.parse(value); + } + + @Override + public boolean isDateTimeCapable() { + return false; + } + + @Override + public Duration convertFromMillis(long systemTimeMillis) { + throw new TextException("Not Supported"); + } + + @Override + public Duration jsonRead(JsonParser ctx, JsonToken event) throws IOException { + return Duration.parse(ctx.getValueAsString()); + } + + @Override + public void jsonWrite(JsonGenerator ctx, String name, Duration value) throws IOException { + ctx.writeStringField(name, value.toString()); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDurationWithNanos.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDurationWithNanos.java new file mode 100644 index 000000000..78d792614 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDurationWithNanos.java @@ -0,0 +1,82 @@ +package com.avaje.ebeaninternal.server.type; + +import com.avaje.ebean.text.TextException; +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.math.BigDecimal; +import java.sql.SQLException; +import java.sql.Types; +import java.time.Duration; + +/** + * ScalarType for java.time.Duration with Nanos precision. + *

+ * Stored in the DB as DECIMAL value. + *

+ */ +public class ScalarTypeDurationWithNanos extends ScalarTypeDuration { + + public ScalarTypeDurationWithNanos() { + super(Types.DECIMAL); + } + + public BigDecimal convertToBigDecimal(Duration value) { + return DecimalUtils.toDecimal(value); + } + + public Duration convertFromBigDecimal(BigDecimal value) { + return DecimalUtils.toDuration(value); + } + + @Override + public void bind(DataBind bind, Duration value) throws SQLException { + if (value == null) { + bind.setNull(Types.DECIMAL); + } else { + bind.setBigDecimal(convertToBigDecimal(value)); + } + } + + @Override + public Duration read(DataReader dataReader) throws SQLException { + return convertFromBigDecimal(dataReader.getBigDecimal()); + } + + @Override + public Duration readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + return convertFromBigDecimal(new BigDecimal(dataInput.readUTF())); + } + } + + @Override + public void writeData(DataOutput dataOutput, Duration value) throws IOException { + if (value == null) { + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeUTF(convertToBigDecimal(value).toString()); + } + } + + @Override + public Object toJdbcType(Object value) { + if (value instanceof BigDecimal) return value; + return convertToBigDecimal((Duration)value); + } + + @Override + public Duration toBeanType(Object value) { + if (value instanceof Duration) return (Duration) value; + return convertFromBigDecimal(BasicTypeConverter.toBigDecimal(value)); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEncryptedWrapper.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEncryptedWrapper.java index 9c3eeccdf..041fd9471 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEncryptedWrapper.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEncryptedWrapper.java @@ -34,12 +34,12 @@ public class ScalarTypeEncryptedWrapper implements ScalarType { } @Override - public Object readData(DataInput dataInput) throws IOException { + public T readData(DataInput dataInput) throws IOException { return wrapped.readData(dataInput); } @Override - public void writeData(DataOutput dataOutput, Object v) throws IOException { + public void writeData(DataOutput dataOutput, T v) throws IOException { wrapped.writeData(dataOutput, v); } @@ -113,8 +113,8 @@ public class ScalarTypeEncryptedWrapper implements ScalarType { } @Override - public T parseDateTime(long systemTimeMillis) { - return wrapped.parseDateTime(systemTimeMillis); + public T convertFromMillis(long systemTimeMillis) { + return wrapped.convertFromMillis(systemTimeMillis); } @Override @@ -133,12 +133,12 @@ public class ScalarTypeEncryptedWrapper implements ScalarType { } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public T jsonRead(JsonParser ctx, JsonToken event) throws IOException { return wrapped.jsonRead(ctx, event); } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, T value) throws IOException { wrapped.jsonWrite(ctx, name, value); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEnumStandard.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEnumStandard.java index 6fdfb1e72..19ee664d6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEnumStandard.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEnumStandard.java @@ -222,7 +222,7 @@ public class ScalarTypeEnumStandard { } @Override - public Object parseDateTime(long systemTimeMillis) { + public Object convertFromMillis(long systemTimeMillis) { throw new TextException("Not Supported"); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeFloat.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeFloat.java index 7fee93bd7..96807eab4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeFloat.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeFloat.java @@ -55,7 +55,7 @@ public class ScalarTypeFloat extends ScalarTypeBase { } @Override - public Float parseDateTime(long systemTimeMillis) { + public Float convertFromMillis(long systemTimeMillis) { return Float.valueOf(systemTimeMillis); } @@ -65,7 +65,7 @@ public class ScalarTypeFloat extends ScalarTypeBase { } @Override - public Object readData(DataInput dataInput) throws IOException { + public Float readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; } else { @@ -75,24 +75,23 @@ public class ScalarTypeFloat extends ScalarTypeBase { } @Override - public void writeData(DataOutput dataOutput, Object v) throws IOException { + public void writeData(DataOutput dataOutput, Float value) throws IOException { - Float value = (Float) v; if (value == null) { dataOutput.writeBoolean(false); } else { dataOutput.writeBoolean(true); - dataOutput.writeFloat(value.floatValue()); + dataOutput.writeFloat(value); } } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public Float jsonRead(JsonParser ctx, JsonToken event) throws IOException { return ctx.getFloatValue(); } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, Float value) throws IOException { ctx.writeNumberField(name, (Float) value); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeInstant.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeInstant.java new file mode 100644 index 000000000..00997022d --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeInstant.java @@ -0,0 +1,59 @@ +package com.avaje.ebeaninternal.server.type; + +import com.avaje.ebean.config.JsonConfig; + +import java.sql.Timestamp; +import java.sql.Types; +import java.time.Instant; + +/** + * ScalarType for java.sql.Timestamp. + */ +public class ScalarTypeInstant extends ScalarTypeBaseDateTime { + + public ScalarTypeInstant(JsonConfig.DateTime mode) { + super(mode, Instant.class, true, Types.TIMESTAMP); + } + + @Override + protected String toJsonNanos(Instant value) { + return toJsonNanos(value.getEpochSecond(), value.getNano()); + } + + @Override + protected String toJsonISO8601(Instant value) { + return value.toString(); + } + + @Override + public long convertToMillis(Instant value) { + return value.toEpochMilli(); + } + + @Override + public Instant convertFromMillis(long systemTimeMillis) { + return Instant.ofEpochMilli(systemTimeMillis); + } + + @Override + public Instant convertFromTimestamp(Timestamp ts) { + return ts.toInstant(); + } + + @Override + public Timestamp convertToTimestamp(Instant t) { + return Timestamp.from(t); + } + + @Override + public Object toJdbcType(Object value) { + if (value instanceof Timestamp) return value; + return convertToTimestamp((Instant) value); + } + + @Override + public Instant toBeanType(Object value) { + if (value instanceof Instant) return (Instant) value; + return convertFromTimestamp((Timestamp) value); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeInteger.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeInteger.java index 101e84a6c..3199fb609 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeInteger.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeInteger.java @@ -36,13 +36,22 @@ public class ScalarTypeInteger extends ScalarTypeBase { } @Override - public Object readData(DataInput dataInput) throws IOException { - return Integer.valueOf(dataInput.readInt()); + public Integer readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + return Integer.valueOf(dataInput.readInt()); + } } @Override - public void writeData(DataOutput dataOutput, Object v) throws IOException { - dataOutput.writeInt((Integer) v); + public void writeData(DataOutput dataOutput, Integer value) throws IOException { + if (value == null) { + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeInt(value); + } } @Override @@ -66,7 +75,7 @@ public class ScalarTypeInteger extends ScalarTypeBase { } @Override - public Integer parseDateTime(long systemTimeMillis) { + public Integer convertFromMillis(long systemTimeMillis) { throw new TextException("Not Supported"); } @@ -76,12 +85,12 @@ public class ScalarTypeInteger extends ScalarTypeBase { } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public Integer jsonRead(JsonParser ctx, JsonToken event) throws IOException { return ctx.getIntValue(); } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { - ctx.writeNumberField(name, (Integer) value); + public void jsonWrite(JsonGenerator ctx, String name, Integer value) throws IOException { + ctx.writeNumberField(name, value); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaDateTime.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaDateTime.java index 5df0cf9b4..9f0d5de86 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaDateTime.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaDateTime.java @@ -3,22 +3,39 @@ package com.avaje.ebeaninternal.server.type; import java.sql.Timestamp; import java.sql.Types; +import com.avaje.ebean.config.JsonConfig; import org.joda.time.DateTime; import com.avaje.ebeaninternal.server.core.BasicTypeConverter; +import org.joda.time.LocalDateTime; /** * ScalarType for Joda DateTime. This maps to a JDBC Timestamp. */ public class ScalarTypeJodaDateTime extends ScalarTypeBaseDateTime { - public ScalarTypeJodaDateTime() { - super(DateTime.class, false, Types.TIMESTAMP); + public ScalarTypeJodaDateTime(JsonConfig.DateTime mode) { + super(mode, DateTime.class, false, Types.TIMESTAMP); } @Override - public long convertToMillis(Object value) { - return ((DateTime) value).getMillis(); + public long convertToMillis(DateTime value) { + return value.getMillis(); + } + + @Override + protected String toJsonNanos(DateTime value) { + return String.valueOf(value.toDateTime().getMillis()); + } + + @Override + protected String toJsonISO8601(DateTime value) { + return value.toString(); + } + + @Override + public DateTime convertFromMillis(long systemTimeMillis) { + return new DateTime(systemTimeMillis); } @Override diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDate.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDate.java index 1fe6822dd..20f125361 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDate.java @@ -48,7 +48,7 @@ public class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate { } @Override - public LocalDate parseDateTime(long systemTimeMillis) { + public LocalDate convertFromMillis(long systemTimeMillis) { return new LocalDate(systemTimeMillis); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDateTime.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDateTime.java index d139a5da2..8f4c57745 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDateTime.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDateTime.java @@ -2,7 +2,9 @@ package com.avaje.ebeaninternal.server.type; import java.sql.Timestamp; import java.sql.Types; +import java.util.Date; +import com.avaje.ebean.config.JsonConfig; import org.joda.time.LocalDateTime; import com.avaje.ebeaninternal.server.core.BasicTypeConverter; @@ -12,13 +14,29 @@ import com.avaje.ebeaninternal.server.core.BasicTypeConverter; */ public class ScalarTypeJodaLocalDateTime extends ScalarTypeBaseDateTime { - public ScalarTypeJodaLocalDateTime() { - super(LocalDateTime.class, false, Types.TIMESTAMP); + public ScalarTypeJodaLocalDateTime(JsonConfig.DateTime mode) { + super(mode, LocalDateTime.class, false, Types.TIMESTAMP); } @Override - public long convertToMillis(Object value) { - return ((LocalDateTime) value).toDateTime().getMillis(); + protected String toJsonNanos(LocalDateTime value) { + return String.valueOf(value.toDateTime().getMillis()); + } + + @Override + protected String toJsonISO8601(LocalDateTime value) { + return value.toString(); + } + + @Override + public long convertToMillis(LocalDateTime value) { + return value.toDateTime().getMillis(); + } + + + @Override + public LocalDateTime convertFromMillis(long systemTimeMillis) { + return new LocalDateTime(systemTimeMillis); } @Override @@ -47,9 +65,4 @@ public class ScalarTypeJodaLocalDateTime extends ScalarTypeBaseDateTime { } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, LocalTime value) throws IOException { ctx.writeStringField(name, value.toString()); } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public LocalTime jsonRead(JsonParser ctx, JsonToken event) throws IOException { if (JsonToken.VALUE_NUMBER_INT == event) { long millis = ctx.getLongValue(); - return parseDateTime(millis); + return convertFromMillis(millis); } else { String string = ctx.getValueAsString(); throw new RuntimeException("convert " + string); @@ -88,7 +88,7 @@ public class ScalarTypeJodaLocalTime extends ScalarTypeBase { } @Override - public LocalTime parseDateTime(long systemTimeMillis) { + public LocalTime convertFromMillis(long systemTimeMillis) { return new LocalTime(systemTimeMillis); } @@ -98,7 +98,7 @@ public class ScalarTypeJodaLocalTime extends ScalarTypeBase { } @Override - public Object readData(DataInput dataInput) throws IOException { + public LocalTime readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; } else { @@ -108,9 +108,8 @@ public class ScalarTypeJodaLocalTime extends ScalarTypeBase { } @Override - public void writeData(DataOutput dataOutput, Object v) throws IOException { + public void writeData(DataOutput dataOutput, LocalTime value) throws IOException { - Time value = (Time) v; if (value == null) { dataOutput.writeBoolean(false); } else { diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocalDate.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocalDate.java index d69073fee..ed83eb957 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocalDate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocalDate.java @@ -52,7 +52,7 @@ public class ScalarTypeLocalDate extends ScalarTypeBaseDate { } @Override - public LocalDate parseDateTime(long systemTimeMillis) { + public LocalDate convertFromMillis(long systemTimeMillis) { return new Timestamp(systemTimeMillis).toLocalDateTime().toLocalDate(); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocalDateTime.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocalDateTime.java index 3a937910b..2bce3d064 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocalDateTime.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocalDateTime.java @@ -1,21 +1,40 @@ package com.avaje.ebeaninternal.server.type; +import com.avaje.ebean.config.JsonConfig; + import java.sql.Timestamp; import java.sql.Types; -import java.time.*; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; /** * ScalarType for java.sql.Timestamp. */ public class ScalarTypeLocalDateTime extends ScalarTypeBaseDateTime { - public ScalarTypeLocalDateTime() { - super(LocalDateTime.class, true, Types.TIMESTAMP); + public ScalarTypeLocalDateTime(JsonConfig.DateTime mode) { + super(mode, LocalDateTime.class, true, Types.TIMESTAMP); } @Override - public long convertToMillis(Object value) { - return ((LocalDateTime) value).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + public LocalDateTime convertFromMillis(long systemTimeMillis) { + return new Timestamp(systemTimeMillis).toLocalDateTime(); + } + + @Override + public long convertToMillis(LocalDateTime value) { + return value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + @Override + protected String toJsonNanos(LocalDateTime value) { + return String.valueOf(convertToMillis(value)); + } + + @Override + protected String toJsonISO8601(LocalDateTime value) { + return value.toString(); } @Override diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocalTime.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocalTime.java new file mode 100644 index 000000000..b8a24312c --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocalTime.java @@ -0,0 +1,100 @@ +package com.avaje.ebeaninternal.server.type; + +import com.avaje.ebean.text.TextException; +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; +import java.time.LocalTime; +import java.time.Year; + +/** + * ScalarType for java.time.Year + */ +public class ScalarTypeLocalTime extends ScalarTypeBase { + + public ScalarTypeLocalTime() { + super(LocalTime.class, true, Types.BIGINT); + } + + @Override + public void bind(DataBind bind, LocalTime value) throws SQLException { + if (value == null) { + bind.setNull(Types.BIGINT); + } else { + bind.setLong(value.toNanoOfDay()); + } + } + + @Override + public LocalTime read(DataReader dataReader) throws SQLException { + return LocalTime.ofNanoOfDay(dataReader.getLong()); + } + + @Override + public LocalTime readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + return LocalTime.ofNanoOfDay(dataInput.readLong()); + } + } + + @Override + public void writeData(DataOutput dataOutput, LocalTime value) throws IOException { + if (value == null) { + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeLong(value.toNanoOfDay()); + } + } + + @Override + public Object toJdbcType(Object value) { + if (value instanceof Long) return value; + return ((LocalTime)value).toNanoOfDay(); + } + + @Override + public LocalTime toBeanType(Object value) { + if (value instanceof LocalTime) return (LocalTime) value; + return LocalTime.ofNanoOfDay(BasicTypeConverter.toLong(value)); + } + + @Override + public String formatValue(LocalTime v) { + return v.toString(); + } + + @Override + public LocalTime parse(String value) { + return LocalTime.parse(value); + } + + @Override + public boolean isDateTimeCapable() { + return false; + } + + @Override + public LocalTime convertFromMillis(long systemTimeMillis) { + throw new TextException("Not Supported"); + } + + @Override + public LocalTime jsonRead(JsonParser ctx, JsonToken event) throws IOException { + return LocalTime.parse(ctx.getValueAsString()); + } + + @Override + public void jsonWrite(JsonGenerator ctx, String name, LocalTime value) throws IOException { + ctx.writeStringField(name, value.toString()); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLong.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLong.java index 1427553b4..1bf541144 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLong.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLong.java @@ -55,8 +55,8 @@ public class ScalarTypeLong extends ScalarTypeBase { } @Override - public Long parseDateTime(long systemTimeMillis) { - return Long.valueOf(systemTimeMillis); + public Long convertFromMillis(long systemTimeMillis) { + return systemTimeMillis; } @Override @@ -65,34 +65,32 @@ public class ScalarTypeLong extends ScalarTypeBase { } @Override - public Object readData(DataInput dataInput) throws IOException { + public Long readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; } else { - long val = dataInput.readLong(); - return Long.valueOf(val); + return dataInput.readLong(); } } @Override - public void writeData(DataOutput dataOutput, Object v) throws IOException { + public void writeData(DataOutput dataOutput, Long value) throws IOException { - Long value = (Long) v; if (value == null) { dataOutput.writeBoolean(false); } else { dataOutput.writeBoolean(true); - dataOutput.writeLong(value.longValue()); + dataOutput.writeLong(value); } } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public Long jsonRead(JsonParser ctx, JsonToken event) throws IOException { return ctx.getLongValue(); } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, Long value) throws IOException { ctx.writeNumberField(name, (Long) value); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLongToTimestamp.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLongToTimestamp.java index d01d67469..7a9b119d2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLongToTimestamp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLongToTimestamp.java @@ -1,10 +1,12 @@ package com.avaje.ebeaninternal.server.type; +import com.avaje.ebean.config.JsonConfig; + import java.sql.Timestamp; public class ScalarTypeLongToTimestamp extends ScalarTypeWrapper { - public ScalarTypeLongToTimestamp() { - super(Long.class, new ScalarTypeTimestamp(), new LongToTimestampConverter()); + public ScalarTypeLongToTimestamp(JsonConfig.DateTime mode) { + super(Long.class, new ScalarTypeTimestamp(mode), new LongToTimestampConverter()); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeMathBigInteger.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeMathBigInteger.java index 1ea865e7a..521462b65 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeMathBigInteger.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeMathBigInteger.java @@ -3,9 +3,11 @@ package com.avaje.ebeaninternal.server.type; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.math.BigDecimal; import java.math.BigInteger; import java.sql.SQLException; import java.sql.Types; +import java.time.Instant; import com.avaje.ebeaninternal.server.core.BasicTypeConverter; import com.fasterxml.jackson.core.JsonGenerator; @@ -61,7 +63,7 @@ public class ScalarTypeMathBigInteger extends ScalarTypeBase { } @Override - public BigInteger parseDateTime(long systemTimeMillis) { + public BigInteger convertFromMillis(long systemTimeMillis) { return BigInteger.valueOf(systemTimeMillis); } @@ -71,19 +73,18 @@ public class ScalarTypeMathBigInteger extends ScalarTypeBase { } @Override - public Object readData(DataInput dataInput) throws IOException { + public BigInteger readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; } else { long val = dataInput.readLong(); - return Long.valueOf(val); + return BigInteger.valueOf(val); } } @Override - public void writeData(DataOutput dataOutput, Object v) throws IOException { + public void writeData(DataOutput dataOutput, BigInteger value) throws IOException { - Long value = (Long) v; if (value == null) { dataOutput.writeBoolean(false); } else { @@ -93,12 +94,12 @@ public class ScalarTypeMathBigInteger extends ScalarTypeBase { } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public BigInteger jsonRead(JsonParser ctx, JsonToken event) throws IOException { return ctx.getDecimalValue().toBigInteger(); } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, BigInteger value) throws IOException { ctx.writeNumberField(name, ((BigInteger) value).longValue()); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java index 8fc004792..9a5d99750 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java @@ -1,7 +1,10 @@ package com.avaje.ebeaninternal.server.type; +import com.avaje.ebean.config.JsonConfig; + import java.sql.Timestamp; import java.sql.Types; +import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZoneId; @@ -10,13 +13,28 @@ import java.time.ZoneId; */ public class ScalarTypeOffsetDateTime extends ScalarTypeBaseDateTime { - public ScalarTypeOffsetDateTime() { - super(OffsetDateTime.class, true, Types.TIMESTAMP); + public ScalarTypeOffsetDateTime(JsonConfig.DateTime mode) { + super(mode, OffsetDateTime.class, true, Types.TIMESTAMP); } @Override - public long convertToMillis(Object value) { - return ((OffsetDateTime) value).toInstant().toEpochMilli(); + protected String toJsonNanos(OffsetDateTime value) { + return toJsonNanos(value.toEpochSecond(), value.getNano()); + } + + @Override + protected String toJsonISO8601(OffsetDateTime value) { + return value.toString(); + } + + @Override + public long convertToMillis(OffsetDateTime value) { + return value.toInstant().toEpochMilli(); + } + + @Override + public OffsetDateTime convertFromMillis(long systemTimeMillis) { + return OffsetDateTime.ofInstant(Instant.ofEpochMilli(systemTimeMillis), ZoneId.systemDefault()); } @Override diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypePostgresHstore.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypePostgresHstore.java index 847f5fc4b..7b8a0e4c8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypePostgresHstore.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypePostgresHstore.java @@ -88,7 +88,7 @@ public class ScalarTypePostgresHstore extends ScalarTypeBase { } @Override - public Map parseDateTime(long dateTime) { + public Map convertFromMillis(long dateTime) { throw new RuntimeException("Should never be called"); } @@ -98,19 +98,28 @@ public class ScalarTypePostgresHstore extends ScalarTypeBase { } @Override - public Object readData(DataInput dataInput) throws IOException { - String json = dataInput.readUTF(); - return parse(json); + public Map readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + String json = dataInput.readUTF(); + return parse(json); + } } @Override - public void writeData(DataOutput dataOutput, Object v) throws IOException { - String json = format(v); - dataOutput.writeUTF(json); + public void writeData(DataOutput dataOutput, Map v) throws IOException { + if (v == null) { + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + String json = format(v); + dataOutput.writeUTF(json); + } } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, Map value) throws IOException { // write the field name followed by the Map/JSON Object if (value == null) { ctx.writeNullField(name); @@ -121,7 +130,7 @@ public class ScalarTypePostgresHstore extends ScalarTypeBase { } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public Map jsonRead(JsonParser ctx, JsonToken event) throws IOException { // at this point the BeanProperty has read the START_OBJECT token // to check for a null value. Pass the START_OBJECT token through to // the EJson parsing so that it knows the first token has been read diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeShort.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeShort.java index 6d6245897..9c9871251 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeShort.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeShort.java @@ -56,7 +56,7 @@ public class ScalarTypeShort extends ScalarTypeBase { } @Override - public Short parseDateTime(long systemTimeMillis) { + public Short convertFromMillis(long systemTimeMillis) { throw new TextException("Not Supported"); } @@ -66,34 +66,32 @@ public class ScalarTypeShort extends ScalarTypeBase { } @Override - public Object readData(DataInput dataInput) throws IOException { + public Short readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; } else { - short val = dataInput.readShort(); - return Short.valueOf(val); + return dataInput.readShort(); } } @Override - public void writeData(DataOutput dataOutput, Object v) throws IOException { + public void writeData(DataOutput dataOutput, Short value) throws IOException { - Short value = (Short) v; if (value == null) { dataOutput.writeBoolean(false); } else { dataOutput.writeBoolean(true); - dataOutput.writeShort(value.shortValue()); + dataOutput.writeShort(value); } } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public Short jsonRead(JsonParser ctx, JsonToken event) throws IOException { return ctx.getShortValue(); } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, Short value) throws IOException { ctx.writeNumberField(name, (Short) value); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeString.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeString.java index a66153e74..9584d26a7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeString.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeString.java @@ -55,7 +55,7 @@ public class ScalarTypeString extends ScalarTypeBase { } @Override - public String parseDateTime(long systemTimeMillis) { + public String convertFromMillis(long systemTimeMillis) { return String.valueOf(systemTimeMillis); } @@ -65,7 +65,7 @@ public class ScalarTypeString extends ScalarTypeBase { } @Override - public Object readData(DataInput dataInput) throws IOException { + public String readData(DataInput dataInput) throws IOException { if (!dataInput.readBoolean()) { return null; } else { @@ -74,9 +74,8 @@ public class ScalarTypeString extends ScalarTypeBase { } @Override - public void writeData(DataOutput dataOutput, Object v) throws IOException { + public void writeData(DataOutput dataOutput, String value) throws IOException { - String value = (String) v; if (value == null) { dataOutput.writeBoolean(false); } else { @@ -86,12 +85,12 @@ public class ScalarTypeString extends ScalarTypeBase { } @Override - public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException { + public String jsonRead(JsonParser ctx, JsonToken event) throws IOException { return ctx.getValueAsString(); } @Override - public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException { + public void jsonWrite(JsonGenerator ctx, String name, String value) throws IOException { ctx.writeStringField(name, (String) value); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeTime.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeTime.java index 5568ff179..955430f65 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeTime.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeTime.java @@ -56,7 +56,7 @@ public class ScalarTypeTime extends ScalarTypeBase