diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/DB2Platform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/DB2Platform.java index 19d5cf245b..4d8a561a78 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/DB2Platform.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/DB2Platform.java @@ -44,6 +44,8 @@ import org.eclipse.persistence.internal.expressions.SQLSelectStatement; import org.eclipse.persistence.internal.helper.BasicTypeHelperImpl; import org.eclipse.persistence.internal.helper.ClassConstants; +import org.eclipse.persistence.internal.helper.ConversionManager; +import org.eclipse.persistence.internal.helper.DatabaseField; import org.eclipse.persistence.internal.helper.DatabaseTable; import org.eclipse.persistence.internal.helper.Helper; import org.eclipse.persistence.internal.sessions.AbstractRecord; @@ -52,11 +54,15 @@ import org.eclipse.persistence.queries.ValueReadQuery; import org.eclipse.persistence.tools.schemaframework.FieldDefinition; +import static java.sql.Types.TIMESTAMP; + import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.sql.Connection; import java.sql.SQLException; +import java.time.OffsetDateTime; +import java.time.OffsetTime; import java.util.ArrayList; import java.util.Calendar; import java.util.Hashtable; @@ -338,14 +344,35 @@ protected Hashtable, FieldTypeDefinition> buildFieldTypes() { fieldTypeMapping.put(java.time.LocalDate.class, new FieldTypeDefinition("DATE")); fieldTypeMapping.put(java.time.LocalDateTime.class, new FieldTypeDefinition("TIMESTAMP")); - fieldTypeMapping.put(java.time.LocalTime.class, new FieldTypeDefinition("TIME")); + fieldTypeMapping.put(java.time.LocalTime.class, new FieldTypeDefinition("TIME", false)); fieldTypeMapping.put(java.time.OffsetDateTime.class, new FieldTypeDefinition("TIMESTAMP")); fieldTypeMapping.put(java.time.OffsetTime.class, new FieldTypeDefinition("TIMESTAMP")); fieldTypeMapping.put(java.time.Instant.class, new FieldTypeDefinition("TIMESTAMP", false)); - + return fieldTypeMapping; } + @Override + public int getJDBCTypeForSetNull(DatabaseField field) { + + Class javaType = ConversionManager.getObjectClass(field.getType()); + + // Mirror the mappings for OffsetDateTime and OffsetTime + // as set above for fieldTypeMapping. These differ from the + // usual mapping to TIMESTAMP_WITH_TIMEZONE / TIME_WITH_TIMEZONE + + if (javaType == OffsetDateTime.class) { + return TIMESTAMP; + } + + if (javaType == OffsetTime.class) { + return TIMESTAMP; + } + + return super.getJDBCTypeForSetNull(field); + } + + /** * INTERNAL: returns the maximum number of characters that can be used in a * field name on this platform. diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.ddlgeneration/src/test/java/org/eclipse/persistence/testing/tests/jpa/ddlgeneration/FractionalSecondsPrecisionTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.ddlgeneration/src/test/java/org/eclipse/persistence/testing/tests/jpa/ddlgeneration/FractionalSecondsPrecisionTest.java index 8c41973778..cc70594a07 100644 --- a/jpa/eclipselink.jpa.testapps/jpa.test.ddlgeneration/src/test/java/org/eclipse/persistence/testing/tests/jpa/ddlgeneration/FractionalSecondsPrecisionTest.java +++ b/jpa/eclipselink.jpa.testapps/jpa.test.ddlgeneration/src/test/java/org/eclipse/persistence/testing/tests/jpa/ddlgeneration/FractionalSecondsPrecisionTest.java @@ -12,17 +12,19 @@ package org.eclipse.persistence.testing.tests.jpa.ddlgeneration; +import jakarta.persistence.EntityManager; + import java.time.LocalDateTime; import java.time.LocalTime; -import jakarta.persistence.EntityManager; -import junit.framework.Test; -import junit.framework.TestSuite; import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform; import org.eclipse.persistence.internal.databaseaccess.Platform; import org.eclipse.persistence.testing.framework.jpa.junit.JUnitTestCase; import org.eclipse.persistence.testing.models.jpa.ddlgeneration.DateTimeEntity; +import junit.framework.Test; +import junit.framework.TestSuite; + public class FractionalSecondsPrecisionTest extends JUnitTestCase { /** Default persistence unit name. */ @@ -69,56 +71,68 @@ public String getPersistenceUnitName() { // Test that LocalDateTime returned from the database has precision set in @Column annotation. // Event column timestamp has secondPrecision set to 5. public void testLocalDateTimeCustomPrecision() { - // This test makes sense only when platform supports seconds precision in time SQL types - if (supportsFractionalTime()) { - try (EntityManager em = createEntityManager()) { - beginTransaction(em); - try { - em.persist(DATE_TIME_ENTITIES[1]); - commitTransaction(em); - } catch (Exception e) { - if (isTransactionActive(em)) { - rollbackTransaction(em); - } - throw e; + // This test makes sense only when platform supports seconds precision in time SQL type TIMESTAMP + if (!supportsFractionalTime()) { + return; + } + + try (EntityManager em = createEntityManager()) { + beginTransaction(em); + try { + em.persist(DATE_TIME_ENTITIES[1]); + commitTransaction(em); + } catch (Exception e) { + if (isTransactionActive(em)) { + rollbackTransaction(em); } - } - getEntityManagerFactory().getCache().evictAll(); - try (EntityManager em = createEntityManager()) { - DateTimeEntity dateTimeEntity = em.createQuery("SELECT e FROM DateTimeEntity e WHERE e.id = :id", - DateTimeEntity.class) - .setParameter("id", 1L) - .getSingleResult(); - assertEquals(12345_0000, dateTimeEntity.getLocalDateTime().getNano()); + throw e; } } + + getEntityManagerFactory().getCache().evictAll(); + + try (EntityManager em = createEntityManager()) { + DateTimeEntity dateTimeEntity = + em.createQuery("SELECT e FROM DateTimeEntity e WHERE e.id = :id", DateTimeEntity.class) + .setParameter("id", 1L) + .getSingleResult(); + + assertEquals(12345_0000, dateTimeEntity.getLocalDateTime().getNano()); + } + } // Test that LocalTime returned from the database has precision set in @Column annotation. // Event column timestamp has secondPrecision set to 5. public void testLocalTimeCustomPrecision() { - // This test makes sense only when platform supports seconds precision in time SQL types - if (supportsFractionalTime()) { - try (EntityManager em = createEntityManager()) { - beginTransaction(em); - try { - em.persist(DATE_TIME_ENTITIES[2]); - commitTransaction(em); - } catch (Exception e) { - if (isTransactionActive(em)) { - rollbackTransaction(em); - } - throw e; + // This test makes sense only when platform supports seconds precision in time SQL type TIME + if (!supportsFractionalTime() || getPlatform().isDB2()) { + // DB2 is a special case. It supports FractionalTime, but only for TIMESTAMP, not for TIME + return; + } + + try (EntityManager em = createEntityManager()) { + beginTransaction(em); + try { + em.persist(DATE_TIME_ENTITIES[2]); + commitTransaction(em); + } catch (Exception e) { + if (isTransactionActive(em)) { + rollbackTransaction(em); } + throw e; } - getEntityManagerFactory().getCache().evictAll(); - try (EntityManager em = createEntityManager()) { - DateTimeEntity dateTimeEntity = em.createQuery("SELECT e FROM DateTimeEntity e WHERE e.id = :id", - DateTimeEntity.class) - .setParameter("id", 2L) - .getSingleResult(); - assertEquals(1234_00000, dateTimeEntity.getLocalTime().getNano()); - } + } + + getEntityManagerFactory().getCache().evictAll(); + + try (EntityManager em = createEntityManager()) { + DateTimeEntity dateTimeEntity = + em.createQuery("SELECT e FROM DateTimeEntity e WHERE e.id = :id", DateTimeEntity.class) + .setParameter("id", 2L) + .getSingleResult(); + + assertEquals(1234_00000, dateTimeEntity.getLocalTime().getNano()); } }