OPENJPA-2849 coerc native java.sql types to java.time

Many JDBC driver support old java.sql types for select max, min, etc.
Those need to get converted to java.time.* on demand.
This commit is contained in:
Mark Struberg 2021-03-28 19:36:32 +02:00
parent aeb333fb3a
commit 72df0228ca
2 changed files with 149 additions and 23 deletions

View File

@ -25,6 +25,13 @@ import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZoneId;
import java.time.temporal.Temporal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
@ -266,6 +273,8 @@ public class Filters {
/**
* Convert the given value to the given type.
* @param o the given value
* @param type the target type
*/
public static Object convert(Object o, Class<?> type, boolean strictNumericConversion) {
if (o == null)
@ -283,42 +292,93 @@ public class Filters {
// String to Integer
boolean num = o instanceof Number;
if (!num) {
if (type == String.class)
if (type == String.class) {
return o.toString();
else if (type == Boolean.class && o instanceof String)
}
else if (type == Boolean.class && o instanceof String) {
return Boolean.valueOf(o.toString());
else if (type == Integer.class && o instanceof String)
}
else if (type == Integer.class && o instanceof String) {
try {
return new Integer(o.toString());
} catch (NumberFormatException e) {
throw new ClassCastException(_loc.get("cant-convert", o,
o.getClass(), type).getMessage());
}
catch (NumberFormatException e) {
throw new ClassCastException(_loc.get("cant-convert", o, o.getClass(), type).getMessage());
}
}
else if (type == Character.class) {
String str = o.toString();
if (str != null && str.length() == 1)
if (str != null && str.length() == 1) {
return Character.valueOf(str.charAt(0));
} else if (Calendar.class.isAssignableFrom(type) &&
o instanceof Date) {
}
}
else if (Calendar.class.isAssignableFrom(type) && o instanceof Date) {
Calendar cal = Calendar.getInstance();
cal.setTime((Date) o);
return cal;
} else if (Date.class.isAssignableFrom(type) &&
o instanceof Calendar) {
}
else if (Date.class.isAssignableFrom(type) && o instanceof Calendar) {
return ((Calendar) o).getTime();
} else if (Number.class.isAssignableFrom(type)) {
}
else if (Number.class.isAssignableFrom(type)) {
Integer i = null;
if (o instanceof Character) {
i = Integer.valueOf((Character)o);
i = Integer.valueOf((Character) o);
}
else if (o instanceof String && ((String) o).length() == 1) {
i = Integer.valueOf(((String) o));
}
else if (o instanceof String && ((String) o).length() == 1)
i = Integer.valueOf(((String)o));
if (i != null) {
if (type == Integer.class)
if (type == Integer.class) {
return i;
}
num = true;
}
} else if (Temporal.class.isAssignableFrom(type)) {
// handling of Java8 time API.
if (LocalDate.class.equals(type)) {
if (o instanceof java.sql.Date) {
return ((java.sql.Date) o).toLocalDate();
} else if (o instanceof java.util.Date) {
return new java.sql.Date(((java.util.Date)o).getTime()).toLocalDate();
} else if (o instanceof CharSequence) {
return LocalDate.parse((CharSequence) o);
}
} else if (LocalDateTime.class.equals(type)) {
if (o instanceof java.sql.Timestamp) {
return ((java.sql.Timestamp) o).toLocalDateTime();
} else if (o instanceof java.util.Date) {
return ((java.util.Date)o).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
} else if (o instanceof CharSequence) {
return LocalDateTime.parse((CharSequence) o);
}
} else if (LocalTime.class.equals(type)) {
if (o instanceof java.sql.Time) {
return ((java.sql.Time) o).toLocalTime();
} else if (o instanceof java.util.Date) {
return ((java.util.Date)o).toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
} else if (o instanceof CharSequence) {
return LocalTime.parse((CharSequence) o);
}
} else if (OffsetTime.class.equals(type)) {
if (o instanceof java.sql.Time) {
return ((java.sql.Time) o).toLocalTime().atOffset(OffsetDateTime.now().getOffset());
} else if (o instanceof java.util.Date) {
return ((java.util.Date)o).toInstant().atZone(ZoneId.systemDefault()).toOffsetDateTime().toOffsetTime();
} else if (o instanceof CharSequence) {
return OffsetTime.parse((CharSequence) o);
}
} else if (OffsetDateTime.class.equals(type)) {
if (o instanceof java.sql.Timestamp) {
return ((java.sql.Timestamp) o).toInstant().atZone(ZoneId.systemDefault()).toOffsetDateTime();
} else if (o instanceof java.util.Date) {
return ((java.util.Date)o).toInstant().atZone(ZoneId.systemDefault()).toOffsetDateTime();
} else if (o instanceof CharSequence) {
return LocalTime.parse((CharSequence) o);
}
}
} else if (o instanceof String && isJDBCTemporalSyntax(o.toString())) {
try {
Object temporal = parseJDBCTemporalSyntax(o.toString());
@ -331,9 +391,9 @@ public class Filters {
return Enum.valueOf((Class<Enum>)type, o.toString());
}
}
if (!num)
throw new ClassCastException(_loc.get("cant-convert", o,
o.getClass(), type).getMessage());
if (!num) {
throw new ClassCastException(_loc.get("cant-convert", o, o.getClass(), type).getMessage());
}
if (type == Integer.class && allowNumericConversion(o.getClass(), type, strictNumericConversion)) {
return ((Number) o).intValue();
@ -349,12 +409,13 @@ public class Filters {
// does it handle infinity; we need to instead use the Double
// and Float versions, despite wanting to cast it to BigDecimal
double dval = ((Number) o).doubleValue();
if (Double.isNaN(dval) || Double.isInfinite(dval))
if (Double.isNaN(dval) || Double.isInfinite(dval)) {
return Double.valueOf(dval);
}
float fval = ((Number) o).floatValue();
if (Float.isNaN(fval) || Float.isInfinite(fval))
if (Float.isNaN(fval) || Float.isInfinite(fval)) {
return Float.valueOf(fval);
}
return new BigDecimal(o.toString());
} else if (type == BigInteger.class) {
@ -364,7 +425,7 @@ public class Filters {
} else if (type == Byte.class && allowNumericConversion(o.getClass(), type, strictNumericConversion)) {
return Byte.valueOf(((Number) o).byteValue());
} else if (type == Character.class) {
return (char) ((Number) o).intValue();
return (char) ((Number) o).intValue();
} else if (!strictNumericConversion) {
return ((Number) o).intValue();
} else {

View File

@ -21,11 +21,15 @@ package org.apache.openjpa.persistence.simple;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Date;
@ -75,6 +79,67 @@ public class TestJava8TimeTypes extends SingleEMFTestCase {
assertEquals(e.getOffsetTimeField().withOffsetSameInstant(eRead.getOffsetTimeField().getOffset()),
eRead.getOffsetTimeField());
// we've got reports from various functions not properly working with Java8 Dates.
// max function
{
final TypedQuery<LocalDate> qry = em.createQuery("select max(t.localDateField) from Java8TimeTypes AS t", LocalDate.class);
final LocalDate max = qry.getSingleResult();
assertEquals(LocalDate.parse(VAL_LOCAL_DATE), max);
}
{
final TypedQuery<LocalDateTime> qry = em.createQuery("select max(t.localDateTimeField) from Java8TimeTypes AS t", LocalDateTime.class);
final LocalDateTime max = qry.getSingleResult();
assertEquals(LocalDateTime.parse(VAL_LOCAL_DATETIME), max);
}
{
final TypedQuery<LocalTime> qry = em.createQuery("select max(t.localTimeField) from Java8TimeTypes AS t", LocalTime.class);
final LocalTime max = qry.getSingleResult();
assertEquals(LocalTime.parse(VAL_LOCAL_TIME), max);
}
{
final TypedQuery<OffsetTime> qry = em.createQuery("select max(t.offsetTimeField) from Java8TimeTypes AS t", OffsetTime.class);
final OffsetTime max = qry.getSingleResult();
assertEquals(e.getOffsetTimeField().withOffsetSameInstant(eRead.getOffsetTimeField().getOffset()),
max.withOffsetSameInstant(eRead.getOffsetTimeField().getOffset()));
}
{
final TypedQuery<OffsetDateTime> qry = em.createQuery("select max(t.offsetDateTimeField) from Java8TimeTypes AS t", OffsetDateTime.class);
final OffsetDateTime max = qry.getSingleResult();
assertEquals(Instant.from(e.getOffsetDateTimeField()),
Instant.from(max));
}
// min function
{
final TypedQuery<LocalDate> qry = em.createQuery("select min(t.localDateField) from Java8TimeTypes AS t", LocalDate.class);
final LocalDate min = qry.getSingleResult();
assertEquals(LocalDate.parse(VAL_LOCAL_DATE), min);
}
{
final TypedQuery<LocalDateTime> qry = em.createQuery("select min(t.localDateTimeField) from Java8TimeTypes AS t", LocalDateTime.class);
final LocalDateTime min = qry.getSingleResult();
assertEquals(LocalDateTime.parse(VAL_LOCAL_DATETIME), min);
}
{
final TypedQuery<LocalTime> qry = em.createQuery("select min(t.localTimeField) from Java8TimeTypes AS t", LocalTime.class);
final LocalTime min = qry.getSingleResult();
assertEquals(LocalTime.parse(VAL_LOCAL_TIME), min);
}
{
final TypedQuery<OffsetTime> qry = em.createQuery("select min(t.offsetTimeField) from Java8TimeTypes AS t", OffsetTime.class);
final OffsetTime min = qry.getSingleResult();
assertEquals(e.getOffsetTimeField().withOffsetSameInstant(eRead.getOffsetTimeField().getOffset()),
min.withOffsetSameInstant(eRead.getOffsetTimeField().getOffset()));
}
{
final TypedQuery<OffsetDateTime> qry = em.createQuery("select min(t.offsetDateTimeField) from Java8TimeTypes AS t", OffsetDateTime.class);
final OffsetDateTime min = qry.getSingleResult();
assertEquals(Instant.from(e.getOffsetDateTimeField()),
Instant.from(min));
}
}