Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for java.time #159

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions src/main/java/com/j256/ormlite/db/BaseDatabaseType.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,26 @@ public void appendColumnArg(String tableName, StringBuilder sb, FieldType fieldT
appendDateType(sb, fieldType, fieldWidth);
break;

case LOCAL_DATE:
appendLocalDateType(sb, fieldType, fieldWidth);
break;

case LOCAL_TIME:
appendLocalTimeType(sb, fieldType, fieldWidth);
break;

case LOCAL_DATE_TIME:
appendLocalDateTimeType(sb, fieldType, fieldWidth);
break;

case OFFSET_TIME:
appendOffsetTimeType(sb, fieldType, fieldWidth);
break;

case OFFSET_DATE_TIME:
appendOffsetDateTimeType(sb, fieldType, fieldWidth);
break;

case CHAR:
appendCharType(sb, fieldType, fieldWidth);
break;
Expand Down Expand Up @@ -208,6 +228,41 @@ protected void appendDateType(StringBuilder sb, FieldType fieldType, int fieldWi
sb.append("TIMESTAMP");
}

/**
* Output the SQL type for a Java LocalDate.
*/
protected void appendLocalDateType(StringBuilder sb, FieldType fieldType, int fieldWidth) {
sb.append("DATE");
}

/**
* Output the SQL type for a Java LocalTime.
*/
protected void appendLocalTimeType(StringBuilder sb, FieldType fieldType, int fieldWidth) {
sb.append("TIME");
}

/**
* Output the SQL type for a Java LocalDateTime.
*/
protected void appendLocalDateTimeType(StringBuilder sb, FieldType fieldType, int fieldWidth) {
sb.append("TIMESTAMP");
}

/**
* Output the SQL type for a Java OffsetTime.
*/
protected void appendOffsetTimeType(StringBuilder sb, FieldType fieldType, int fieldWidth) {
sb.append("TIME WITH TIME ZONE");
}

/**
* Output the SQL type for a Java OffsetDateTime or Instant.
*/
protected void appendOffsetDateTimeType(StringBuilder sb, FieldType fieldType, int fieldWidth) {
sb.append("TIMESTAMP WITH TIME ZONE");
}

/**
* Output the SQL type for a Java boolean.
*/
Expand Down
64 changes: 64 additions & 0 deletions src/main/java/com/j256/ormlite/field/DataType.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@
import com.j256.ormlite.field.types.StringType;
import com.j256.ormlite.field.types.TimeStampType;
import com.j256.ormlite.field.types.UuidType;
import com.j256.ormlite.field.types.LocalDateType;
import com.j256.ormlite.field.types.LocalTimeType;
import com.j256.ormlite.field.types.LocalDateTimeType;
import com.j256.ormlite.field.types.OffsetTimeType;
import com.j256.ormlite.field.types.OffsetDateTimeType;
import com.j256.ormlite.field.types.InstantType;
import com.j256.ormlite.field.types.LocalDateSqlType;
import com.j256.ormlite.field.types.LocalTimeSqlType;
import com.j256.ormlite.field.types.LocalDateTimeSqlType;
import com.j256.ormlite.field.types.OffsetTimeSqlType;

/**
* Data type enumeration that corresponds to a {@link DataPersister}.
Expand Down Expand Up @@ -249,6 +259,60 @@ public enum DataType {
* Marker for fields that are unknown.
*/
UNKNOWN(null),
/**
* Persists the {@link java.time.LocalDate} Java class. By default this will use
* {@link #LOCAL_DATE} so you will need to specify this using {@link DatabaseField#dataType()}.
*
*/
LOCAL_DATE_SQL(LocalDateSqlType.getSingleton()),
/**
* Persists the {@link java.time.LocalTime} Java class. By default this will use
* {@link #LOCAL_TIME} so you will need to specify this using {@link DatabaseField#dataType()}.
*
*/
LOCAL_TIME_SQL(LocalTimeSqlType.getSingleton()),
/**
* Persists the {@link java.time.LocalDateTime} Java class. By default this will use
* {@link #LOCAL_DATE_TIME} so you will need to specify this using {@link DatabaseField#dataType()}.
*
*/
LOCAL_DATE_TIME_SQL(LocalDateTimeSqlType.getSingleton()),
/**
* Persists the {@link java.time.OffsetTime} Java class. By default this will use
* {@link #OFFSET_TIME} so you will need to specify this using {@link DatabaseField#dataType()}.
*
*/
OFFSET_TIME_SQL(OffsetTimeSqlType.getSingleton()),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the name for this is a little confusing since we are not storing this as a java.sql type.

Also, I think the Javadocs for the *_SQL types could be expanded to actually describe what's different about them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, this is true for all *_SQL types, but I don't know how to name them differently.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is true for all *_SQL types

Well the only one called that right now is SQL_DATE and for good reason.

Types more like this situation is DATE_LONG, DATE_INTEGER, BOOLEAN_INTEGER, etc. I'm not sure what the best name is. OFFSET_TIME_TIMESTAMP? Does that sound clumsy?

I'd still modify the Javadoc for these enums to explain their purpose either way.

/**
* Persists the {@link java.time.LocalDate} Java class.
*
*/
LOCAL_DATE(LocalDateType.getSingleton()),
/**
* Persists the {@link java.time.LocalTime} Java class.
*
*/
LOCAL_TIME(LocalTimeType.getSingleton()),
/**
* Persists the {@link java.time.LocalDateTime} Java class.
*
*/
LOCAL_DATE_TIME(LocalDateTimeType.getSingleton()),
/**
* Persists the {@link java.time.OffsetTime} Java class.
*
*/
OFFSET_TIME(OffsetTimeType.getSingleton()),
/**
* Persists the {@link java.time.OffsetDateTime} Java class.
*
*/
OFFSET_DATE_TIME(OffsetDateTimeType.getSingleton()),
/**
* Persists the {@link java.time.Instant} Java class.
*
*/
INSTANT(InstantType.getSingleton()),
// end
;

Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/j256/ormlite/field/SqlType.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ public enum SqlType {
// for other types handled by custom persisters
OTHER,
UNKNOWN,
// java.time
LOCAL_DATE,
LOCAL_TIME,
LOCAL_DATE_TIME,
OFFSET_TIME,
OFFSET_DATE_TIME,
// end
;
}
85 changes: 85 additions & 0 deletions src/main/java/com/j256/ormlite/field/types/InstantType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.j256.ormlite.field.types;

import java.lang.reflect.Field;
import java.sql.SQLException;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

import com.j256.ormlite.field.FieldType;
import com.j256.ormlite.field.SqlType;
import com.j256.ormlite.misc.SqlExceptionUtil;
import com.j256.ormlite.support.DatabaseResults;

/**
* A custom persister that is able to store the java.time.LocalDate class in the database as Date object.
*
* @author graynk
*/
public class InstantType extends BaseDataType {

private static InstantType singleton;
public static InstantType getSingleton() {
if (singleton == null) {
try {
Class.forName("java.time.Instant", false, null);
singleton = new InstantType();
} catch (ClassNotFoundException e) {
return null; // No java.time on classpath;
}
}
return singleton;
}
private InstantType() { super(SqlType.OFFSET_DATE_TIME, new Class<?>[] { Instant.class }); }
protected InstantType(SqlType sqlType, Class<?>[] classes) { super(sqlType, classes); }

@Override
public Object parseDefaultString(FieldType fieldType, String defaultStr) throws SQLException {
try {
return OffsetDateTime.parse(defaultStr, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss[.SSS]x"));
} catch (NumberFormatException e) {
throw SqlExceptionUtil.create("Problems with field " + fieldType +
" parsing default LocalDateTime value: " + defaultStr, e);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should say "Instant".

}
}

@Override
public Object resultToSqlArg(FieldType fieldType, DatabaseResults results, int columnPos) throws SQLException {
return results.getOffsetDateTime(columnPos);
}

@Override
public Object sqlArgToJava(FieldType fieldType, Object sqlArg, int columnPos) {
OffsetDateTime value = (OffsetDateTime) sqlArg;
return value.toInstant();
}

@Override
public Object javaToSqlArg(FieldType fieldType, Object javaObject) {
Instant instant = (Instant) javaObject;
// ZoneOffset.UTC is evaluated at InstantType creation, fails on Java 6. Using ZoneId.of() instead
return OffsetDateTime.ofInstant(instant, ZoneId.of("UTC"));
}

@Override
public boolean isValidForVersion() {
return true;
}

@Override
public Object moveToNextValue(Object currentValue) {
Instant datetime = (Instant) currentValue;
return datetime.plusNanos(1);
}

@Override
public boolean isValidForField(Field field) {
return (field.getType() == Instant.class);
}

@Override
public boolean isArgumentHolderRequired() {
return true;
}
}
83 changes: 83 additions & 0 deletions src/main/java/com/j256/ormlite/field/types/LocalDateSqlType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.j256.ormlite.field.types;

import java.lang.reflect.Field;
import java.sql.Date;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

import com.j256.ormlite.field.FieldType;
import com.j256.ormlite.field.SqlType;
import com.j256.ormlite.misc.SqlExceptionUtil;
import com.j256.ormlite.support.DatabaseResults;

/**
* A custom persister that is able to store the java.time.LocalDate class in the database as Date object.
*
* @author graynk
*/
public class LocalDateSqlType extends BaseDataType {

private static LocalDateSqlType singleton;
public static LocalDateSqlType getSingleton() {
if (singleton == null) {
try {
Class.forName("java.time.LocalDate", false, null);
singleton = new LocalDateSqlType();
} catch (ClassNotFoundException e) {
return null; // No java.time on classpath;
}
}
return singleton;
}
private LocalDateSqlType() { super(SqlType.LOCAL_DATE, new Class<?>[] { LocalDate.class }); }
protected LocalDateSqlType(SqlType sqlType, Class<?>[] classes) { super(sqlType, classes); }

@Override
public Object parseDefaultString(FieldType fieldType, String defaultStr) throws SQLException {
try {
return Date.valueOf(LocalDate.parse(defaultStr, DateTimeFormatter.ISO_LOCAL_DATE));
} catch (NumberFormatException e) {
throw SqlExceptionUtil.create("Problems with field " + fieldType +
" parsing default LocalDate value: " + defaultStr, e);
}
}

@Override
public Object resultToSqlArg(FieldType fieldType, DatabaseResults results, int columnPos) throws SQLException {
return results.getDate(columnPos);
}

@Override
public Object javaToSqlArg(FieldType fieldType, Object javaObject) {
LocalDate date = (LocalDate) javaObject;
return Date.valueOf(date);
}

@Override
public Object sqlArgToJava(FieldType fieldType, Object sqlArg, int columnPos) {
Date date = (Date) sqlArg;
return date.toLocalDate();
}

@Override
public boolean isValidForVersion() {
return true;
}

@Override
public Object moveToNextValue(Object currentValue) {
LocalDate date = (LocalDate) currentValue;
return date.plusDays(1);
}

@Override
public boolean isArgumentHolderRequired() {
return true;
}

@Override
public boolean isValidForField(Field field) {
return (field.getType() == LocalDate.class);
}
}
Loading