Author: jgrassel Date: Thu Dec 5 20:33:25 2013 New Revision: 1548277 URL: http://svn.apache.org/r1548277 Log: OPENJPA-2453: Add support to retain milliseconds of 'un-rounded' Date field
Modified: openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java openjpa/branches/2.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/temporal/TestTemporalTimestamp.java openjpa/branches/2.2.x/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml Modified: openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java URL: http://svn.apache.org/viewvc/openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java?rev=1548277&r1=1548276&r2=1548277&view=diff ============================================================================== --- openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java (original) +++ openjpa/branches/2.2.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java Thu Dec 5 20:33:25 2013 @@ -298,7 +298,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"; @@ -786,8 +810,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 @@ -5615,5 +5647,32 @@ 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; + } } } Modified: openjpa/branches/2.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/temporal/TestTemporalTimestamp.java URL: http://svn.apache.org/viewvc/openjpa/branches/2.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/temporal/TestTemporalTimestamp.java?rev=1548277&r1=1548276&r2=1548277&view=diff ============================================================================== --- openjpa/branches/2.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/temporal/TestTemporalTimestamp.java (original) +++ openjpa/branches/2.2.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/temporal/TestTemporalTimestamp.java Thu Dec 5 20:33:25 2013 @@ -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.Op 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 exten 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 exten 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 exten 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 exten 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(); } Modified: openjpa/branches/2.2.x/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml URL: http://svn.apache.org/viewvc/openjpa/branches/2.2.x/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml?rev=1548277&r1=1548276&r2=1548277&view=diff ============================================================================== --- openjpa/branches/2.2.x/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml (original) +++ openjpa/branches/2.2.x/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml Thu Dec 5 20:33:25 2013 @@ -1382,6 +1382,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>