OPENJPA-2453: Add support to retain milliseconds of 'un-rounded' Date field.

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1566872 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Heath Thomann 2014-02-10 23:55:57 +00:00
parent 731649a2c9
commit d802d74930
3 changed files with 167 additions and 6 deletions

View File

@ -301,7 +301,31 @@ public class DBDictionary
public int maxEmbeddedClobSize = -1;
public int inClauseLimit = -1;
public int datePrecision = MILLI;
/**
* @deprecated Use 'dateMillisecondBehavior' instead.
*/
@Deprecated
public boolean roundTimeToMillisec = true;
/*
* This defines how the milliseconds of a Date field are handled
* when the Date is retrieved from the database, as follows:
*
* ROUND: This is the default. The
* Date will be rounded to the nearest millisecond.
* DROP: The milliseconds will be dropped, thus rounding is not
* performed. As an example, a date of '2010-01-01 12:00:00.687701'
* stored in the database will become '2010-01-01 12:00:00.000' in
* the Date field of the entity.
* RETAIN: The milliseconds will not be rounded and retained. As an
* example, a date of '2010-01-01 12:00:00.687701' stored in the
* database will become '2010-01-01 12:00:00.687' in the Date field
* of the entity.
*/
public enum DateMillisecondBehaviors { DROP, ROUND, RETAIN };
private DateMillisecondBehaviors dateMillisecondBehavior;
public int characterColumnSize = 255;
public String arrayTypeName = "ARRAY";
public String bigintTypeName = "BIGINT";
@ -793,8 +817,16 @@ public class DBDictionary
// get the fractional seconds component, rounding away anything beyond
// milliseconds
int fractional = 0;
if (roundTimeToMillisec) {
fractional = (int) Math.round(tstamp.getNanos() / (double) MILLI);
switch (getMillisecondBehavior()) {
case DROP :
fractional = 0;
break;
case RETAIN :
fractional = (int) (tstamp.getNanos() / (double) MILLI);
break;
case ROUND :
fractional = (int) Math.round(tstamp.getNanos() / (double) MILLI);
break;
}
// get the millis component; some JDBC drivers round this to the
@ -5622,6 +5654,33 @@ public class DBDictionary
public String getIdentityColumnName() {
return null;
}
/**
* Default behavior is ROUND
*/
public DateMillisecondBehaviors getMillisecondBehavior() {
// If the user hasn't configured this property, fall back
// to looking at the old property in the mean time till
// we can get rid of it.
if (dateMillisecondBehavior == null) {
if (roundTimeToMillisec) {
dateMillisecondBehavior = DateMillisecondBehaviors.ROUND;
} else {
dateMillisecondBehavior = DateMillisecondBehaviors.DROP;
}
}
return dateMillisecondBehavior;
}
public void setDateMillisecondBehavior(String str) {
if (str != null) {
// Tolerate different case
str = str.toUpperCase();
dateMillisecondBehavior = DateMillisecondBehaviors.valueOf(str);
} else {
dateMillisecondBehavior = null;
}
}
protected boolean isUsingRange(long start, long end) {

View File

@ -18,6 +18,7 @@
*/
package org.apache.openjpa.persistence.temporal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
@ -31,8 +32,8 @@ import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.test.SQLListenerTestCase;
public class TestTemporalTimestamp extends SQLListenerTestCase {
private OpenJPAEntityManager em;
private OpenJPAEntityManager em;
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public void setUp() {
setSupportedDatabases(
@ -42,8 +43,34 @@ public class TestTemporalTimestamp extends SQLListenerTestCase {
return;
}
setUp(CLEAR_TABLES, TemporalEntity.class, "openjpa.jdbc.DBDictionary", "RoundTimeToMillisec=false");
setUp(CLEAR_TABLES, TemporalEntity.class, "openjpa.jdbc.DBDictionary",
"DateMillisecondBehavior=" + DBDictionary.DateMillisecondBehaviors.DROP,
"openjpa.Log", "SQL=TRACE,Tests=TRACE", "openjpa.ConnectionFactoryProperties"
,"PrintParameters=true");
assertNotNull(emf);
// noRound:
// expected:<253402322399000> but was:<253402325999000>
/* Date got = new Date(253402322399000L);
Date exp = new Date(253402325999000L);
System.out.println("NoRounding");
System.out.println("Got = " + sdf.format(got).toString());
System.out.println("Exp = " + sdf.format(exp).toString());
*/
//testNoRoundingNoMillisecondLoss
//expected:<253402322399999> but was:<253402325999999>
/* got = new Date(253402322399999L);
exp = new Date(253402325999999L);
System.out.println("testNoRoundingNoMillisecondLoss");
System.out.println("Got = " + sdf.format(got).toString());
System.out.println("Exp = " + sdf.format(exp).toString());
*/
//expected:<253402322400000> but was:<253402326000000>
/* got = new Date(253402322400000L);
exp = new Date(253402326000000L);
System.out.println("testRounding");
System.out.println("Got = " + sdf.format(got).toString());
System.out.println("Exp = " + sdf.format(exp).toString());
*/
loadDB();
}
@ -62,6 +89,32 @@ public class TestTemporalTimestamp extends SQLListenerTestCase {
assertEquals(testDate.getMinutes(), 59);
assertEquals(testDate.getSeconds(), 59);
assertEquals(testDate.getYear(), 8099);
assertTrue(sdf.format(testDate).toString().endsWith(".000"));
}
em.close();
}
public void testNoRoundingNoMillisecondLoss() {
JDBCConfiguration conf = (JDBCConfiguration) emf.getConfiguration();
DBDictionary dict = conf.getDBDictionaryInstance();
dict.setDateMillisecondBehavior(DBDictionary.DateMillisecondBehaviors.RETAIN.toString());
em = emf.createEntityManager();
final List<TemporalEntity> temporalEntityList = findAll(em);
assertNotNull(temporalEntityList);
assertNotEquals(temporalEntityList.size(), 0);
for (final TemporalEntity temporalEntity : temporalEntityList) {
Date testDate = temporalEntity.getTestDate();
assertEquals(testDate.getDay(), 5);
assertEquals(testDate.getMonth(), 11);
assertEquals(testDate.getDate(), 31);
assertEquals(testDate.getHours(), 23);
assertEquals(testDate.getMinutes(), 59);
assertEquals(testDate.getSeconds(), 59);
assertEquals(testDate.getYear(), 8099);
assertTrue(sdf.format(testDate).toString().endsWith(".999"));
System.out.println("sdf.format(testDate).toString() = " +
sdf.format(testDate).toString());
}
em.close();
}
@ -70,7 +123,7 @@ public class TestTemporalTimestamp extends SQLListenerTestCase {
JDBCConfiguration conf = (JDBCConfiguration) emf.getConfiguration();
DBDictionary dict = conf.getDBDictionaryInstance();
// set value back to default
dict.roundTimeToMillisec = true;
dict.setDateMillisecondBehavior(DBDictionary.DateMillisecondBehaviors.ROUND.toString());
em = emf.createEntityManager();
final List<TemporalEntity> temporalEntityList = findAll(em);
@ -85,6 +138,8 @@ public class TestTemporalTimestamp extends SQLListenerTestCase {
assertEquals(testDate.getMinutes(), 0);
assertEquals(testDate.getSeconds(), 0);
assertEquals(testDate.getYear(), 8100);
// assertEquals(testDate.getTime(), 253402326000000L);
assertTrue(sdf.format(testDate).toString().endsWith(".000"));
}
em.close();
}

View File

@ -1409,6 +1409,53 @@ before storing them in the database.
Defaults to 1000000.
</para>
</listitem>
<listitem id="DBDictionary.DateMillisecondBehavior">
<para>
<indexterm>
<primary>
JDBC
</primary>
<secondary>
DateMillisecondBehavior
</secondary>
</indexterm>
<literal>DateMillisecondBehavior</literal>:
When retrieving a <literal>Date</literal> value from a database which stores the value in
a TIMESTAMP column, the values retrieved will be rounded to the nearest
millisecond. So a date of '2010-01-01 12:00:00.687701' stored in the
database will become '2010-01-01 12:00:00.688' in the <literal>Date</literal> field of the
entity. The following date stored in the database as '9999-12-31 23:59:59.9999'
will become '10000-01-01 00:00:00.000'. This rounding may not be desirable. With this
property, a user has options which will direct OpenJPA how to handle the milliseconds. This
property can be set to one of the enums defined in
<literal>DBDictionary.DateMillisecondBehaviors</literal>. The options defined in
<literal>DBDictionary.DateMillisecondBehaviors</literal> are as follows:
<itemizedlist>
<listitem>
<para>
<literal>DateMillisecondBehaviors.ROUND</literal>: This is the default. The
<literal>Date</literal> will be rounded to the nearest millisecond, as described above.
</para>
</listitem>
<listitem>
<para>
<literal>DateMillisecondBehaviors.DROP</literal>: The milliseconds will be dropped, thus
rounding is not performed. As an example, a date of '2010-01-01 12:00:00.687701' stored in the
database will become '2010-01-01 12:00:00.000' in the <literal>Date</literal> field of the
entity.
</para>
</listitem>
<listitem>
<para>
<literal>DateMillisecondBehaviors.RETAIN</literal>: The milliseconds will not be rounded, but will
be retained. As an example, a date of '2010-01-01 12:00:00.687701' stored in the
database will become '2010-01-01 12:00:00.687' in the <literal>Date</literal> field of the
entity.
</para>
</listitem>
</itemizedlist>
</para>
</listitem>
<listitem id="DBDictionary.DateTypeName">
<para>
<indexterm>