diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/history/HistoryPolicy.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/history/HistoryPolicy.java index 1b377c3c48d..193a317a72e 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/history/HistoryPolicy.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/history/HistoryPolicy.java @@ -86,13 +86,15 @@ public Expression additionalHistoryExpression(Expression context, Expression bas * @param tableIndex not null indicates that only expression for a single table should be returned. */ public Expression additionalHistoryExpression(Expression context, Expression base, Integer tableIndex) { - // - AsOfClause clause = base.getAsOfClause(); - Object value = clause.getValue(); Expression join = null; Expression subJoin = null; Expression start = null; Expression end = null; + AsOfClause clause = base.getAsOfClause(); + if (clause == null) { + return null; + } + Object value = clause.getValue(); if (value == null) { return null; // for now nothing as assume mirroring historical tables. diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/DataExpression.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/DataExpression.java index 083fb2f1a5f..1ba3df1ce62 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/DataExpression.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/DataExpression.java @@ -38,7 +38,7 @@ public abstract class DataExpression extends BaseExpression { protected List derivedFields; protected boolean hasBeenNormalized; protected TableAliasLookup tableAliases; - protected AsOfClause asOfClause; + protected AsOfClause asOfClause = AsOfClause.NO_CLAUSE; /** * DataExpression constructor comment. diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/ObjectExpression.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/ObjectExpression.java index 2010d5f469e..10b67f21096 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/ObjectExpression.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/ObjectExpression.java @@ -593,7 +593,7 @@ public List getOwnedTables() { } } else if (descriptor.isAggregateDescriptor()) { return null; - } else if ((descriptor.getHistoryPolicy() != null) && (getAsOfClause().getValue() != null)) { + } else if (descriptor.getHistoryPolicy() != null && getAsOfClause() != null && getAsOfClause().getValue() != null) { tables = descriptor.getHistoryPolicy().getHistoricalTables(); } else if (isUsingOuterJoinForMultitableInheritance()) { tables = descriptor.getInheritancePolicy().getAllTables(); diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/AdvancedTableCreator.java b/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/AdvancedTableCreator.java index e7aec71acfa..576672e6dab 100644 --- a/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/AdvancedTableCreator.java +++ b/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/AdvancedTableCreator.java @@ -41,6 +41,8 @@ import org.eclipse.persistence.tools.schemaframework.ForeignKeyConstraint; import org.eclipse.persistence.tools.schemaframework.TableDefinition; +import java.util.Map; + public class AdvancedTableCreator extends TogglingFastTableCreator { public AdvancedTableCreator() { setName("EJB3EmployeeProject"); @@ -107,7 +109,9 @@ public AdvancedTableCreator() { addTableDefinition(buildRABBITFOOTTable()); addTableDefinition(buildCMP3_HINGETable());//Bug#457480 addTableDefinition(buildCMP3_ROOMTable()); + addTableDefinition(buildCMP3_ROOM_HISTTable()); addTableDefinition(buildCMP3_DOORTable()); + addTableDefinition(buildCMP3_DOOR_HISTTable()); addTableDefinition(buildCMP3_PRODUCTTable()); addTableDefinition(buildCMP3_CANOETable()); addTableDefinition(buildCMP3_LAKETable()); @@ -2659,6 +2663,15 @@ public TableDefinition buildCMP3_ROOMTable() { return table; } + public TableDefinition buildCMP3_ROOM_HISTTable() { + TableDefinition table = buildCMP3_ROOMTable(); + table.setName(table.getName() + "_HIST"); + table.addField(buildHistoryField("START_DATE", true, false)); + table.addField(buildHistoryField("END_DATE", false, true)); + table.setUserDefinedForeignKeyConstraints(Map.of()); + return table; + } + public TableDefinition buildCMP3_PRODUCTTable() { TableDefinition table = new TableDefinition(); table.setName("CMP3_PRODUCT"); @@ -2815,6 +2828,15 @@ public TableDefinition buildCMP3_DOORTable() { return table; } + public TableDefinition buildCMP3_DOOR_HISTTable() { + TableDefinition table = buildCMP3_DOORTable(); + table.setName(table.getName() + "_HIST"); + table.addField(buildHistoryField("START_DATE", true, false)); + table.addField(buildHistoryField("END_DATE", false, true)); + table.setUserDefinedForeignKeyConstraints(Map.of()); + return table; + } + public TableDefinition buildCMP3_CANOETable() { TableDefinition table = new TableDefinition(); table.setName("CMP3_CANOE"); @@ -2953,28 +2975,22 @@ public TableDefinition buildCMP3_PEARLTable() { public TableDefinition buildCMP3_PEARL_HISTTable() { TableDefinition table = buildCMP3_PEARLTable(); table.setName(table.getName() + "_HIST"); - - FieldDefinition fieldSTART = new FieldDefinition(); - fieldSTART.setName("START_DATE"); - fieldSTART.setTypeName("TIMESTAMP"); - fieldSTART.setIsPrimaryKey(true); - fieldSTART.setIsIdentity(false); - fieldSTART.setUnique(false); - fieldSTART.setShouldAllowNull(false); - table.addField(fieldSTART); - - FieldDefinition fieldEND = new FieldDefinition(); - fieldEND.setName("END_DATE"); - fieldEND.setTypeName("TIMESTAMP"); - fieldEND.setIsPrimaryKey(false); - fieldEND.setIsIdentity(false); - fieldEND.setUnique(false); - fieldEND.setShouldAllowNull(true); - table.addField(fieldEND); - + table.addField(buildHistoryField("START_DATE", true, false)); + table.addField(buildHistoryField("END_DATE", false, true)); return table; } + private static FieldDefinition buildHistoryField(String name, boolean isPrimaryKey, boolean allowNull) { + FieldDefinition field = new FieldDefinition(); + field.setName(name); + field.setTypeName("TIMESTAMP"); + field.setIsPrimaryKey(isPrimaryKey); + field.setIsIdentity(false); + field.setUnique(false); + field.setShouldAllowNull(allowNull); + return field; + } + public TableDefinition buildJobTable() { TableDefinition table = new TableDefinition(); table.setName("JOB"); diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/Door.java b/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/Door.java index 43efa6ad62d..7eb3685a095 100644 --- a/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/Door.java +++ b/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/Door.java @@ -22,6 +22,7 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.persistence.Transient; +import org.eclipse.persistence.annotations.Customizer; import org.eclipse.persistence.annotations.Mutable; import org.eclipse.persistence.annotations.ReadTransformer; import org.eclipse.persistence.annotations.WriteTransformer; @@ -36,6 +37,7 @@ @Entity @Table(name="CMP3_DOOR") +@Customizer(AdvancedHistoryCustomizer.class) public class Door implements Serializable, Cloneable { @Id private int id; diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/Room.java b/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/Room.java index a69372b1f11..4d837c9ad5f 100644 --- a/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/Room.java +++ b/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/Room.java @@ -23,6 +23,7 @@ import jakarta.persistence.OneToMany; import jakarta.persistence.QueryHint; import jakarta.persistence.Table; +import org.eclipse.persistence.annotations.Customizer; import org.eclipse.persistence.config.QueryHints; import java.io.Serializable; @@ -42,6 +43,7 @@ @QueryHint(name = QueryHints.QUERY_RESULTS_CACHE, value = "true"), }) }) +@Customizer(AdvancedHistoryCustomizer.class) public class Room implements Serializable, Cloneable { @Id private int id; diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/test/java/org/eclipse/persistence/testing/tests/jpa/advanced/AdvancedJPAJunitTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/test/java/org/eclipse/persistence/testing/tests/jpa/advanced/AdvancedJPAJunitTest.java index a480cecc957..fcf6b50d037 100644 --- a/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/test/java/org/eclipse/persistence/testing/tests/jpa/advanced/AdvancedJPAJunitTest.java +++ b/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/test/java/org/eclipse/persistence/testing/tests/jpa/advanced/AdvancedJPAJunitTest.java @@ -193,6 +193,7 @@ public static Test suite() { suite.addTest(new AdvancedJPAJunitTest("testTransparentIndirectionValueHolderSessionReset")); suite.addTest(new AdvancedJPAJunitTest("testTransparentIndirectionQuerySessionReset")); suite.addTest(new AdvancedJPAJunitTest("testHistoryRelationshipQueryInitialization")); + suite.addTest(new AdvancedJPAJunitTest("testQueryJoinTablesWithHistory")); return suite; } @@ -2827,7 +2828,23 @@ public void testJoinFetchWithRefreshOnRelatedEntity() { closeEntityManager(em); } } - + + /** + * Bug 526836 + * NPE when joining tables that have a history policy applied. + * Internal Exception: java.lang.NullPointerException: Cannot invoke + * "org.eclipse.persistence.history.AsOfClause.getValue()" because the return value of + * "org.eclipse.persistence.internal.expressions.ObjectExpression.getAsOfClause()" is null + */ + public void testQueryJoinTablesWithHistory() { + + EntityManager em = createEntityManager(); + + // Previously would throw a null pointer exception if both tables had a history + // policy applied like @Customizer(AdvancedHistoryCustomizer.class) + em.createQuery("select d from Door d left join Room r on d.room = r"); + } + /** * Bug 464088 * Test non-historized Entity eager-referencing a historized Entity's queries function correctly