diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java
index 6659d228337..27f3455ca55 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java
@@ -3805,6 +3805,33 @@ public void writeAddColumnClause(Writer writer, AbstractSession session, TableDe
field.appendDBString(writer, session, table);
}
+ /**
+ * INTERNAL:
+ * May need to override this method if the platform supports ALTER TABLE DROP COLUMN <column>
+ * and the generated sql doesn't work.
+ * Write the string that follows ALTER TABLE to create a sql statement for
+ * the platform in order to drop existing column from an existing table.
+ */
+ public void writeDropColumnClause(Writer writer, AbstractSession session, TableDefinition table, String fieldName) throws IOException {
+ writer.write("DROP COLUMN ");
+ writer.write(fieldName);
+ }
+
+ /**
+ * INTERNAL:
+ * May need to override this method if the platform supports TRUNCATE TABLE <table>
+ * and the generated sql doesn't work.
+ * Write the string that creates TRUNCATE TABLE sql statement for the platform in order
+ * to truncate an existing table.
+ */
+ public void writeTruncateTable(Writer writer, AbstractSession session, TableDefinition table) throws IOException {
+ String tableName = table.getTable() == null
+ ? table.getName()
+ : table.getTable().getName();
+ writer.write("TRUNCATE TABLE ");
+ writer.write(tableName);
+ }
+
/**
* INTERNAL:
* Override this method if the platform supports storing JDBC connection user name during
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/ExceptionLocalizationResource.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/ExceptionLocalizationResource.java
index 28e799a007d..e8f0b196fdb 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/ExceptionLocalizationResource.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/localization/i18n/ExceptionLocalizationResource.java
@@ -265,7 +265,11 @@ public class ExceptionLocalizationResource extends ListResourceBundle {
{ "json_pgsql_pgobject_conversion", "Database PGobject conversion failed."},
{ "json_pgsql_unknown_type", "Unknown JSON type returned from database."},
{ "json_ora21c_jsonvalue_to_oraclevalue", "Could not convert JsonValue to OracleJsonValue."},
- { "json_ora21c_resultset_to_jsonvalue", "Could not convert JDBC ResultSet type to JsonValue."}
+ { "json_ora21c_resultset_to_jsonvalue", "Could not convert JDBC ResultSet type to JsonValue."},
+ { "schema_validation_failed", "Schema validation failed"},
+ { "schema_validation_missing_table", "The {0} table vas not found in the schema"},
+ { "schema_validation_table_surplus_columns", "The {0} table has surplus columns in the schema"},
+ { "schema_validation_table_missing_columns", "The {0} table has missing columns in the schema"}
};
/**
* Return the lookup table.
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/FieldDefinition.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/FieldDefinition.java
index c7992fc5c19..15a66aec3ae 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/FieldDefinition.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/FieldDefinition.java
@@ -110,20 +110,41 @@ public FieldDefinition(String name, String typeName) {
this.name = name;
this.typeName = typeName;
}
-
/**
* INTERNAL:
* Append the database field definition string to the table creation statement.
+ *
* @param writer Target writer where to write field definition string.
* @param session Current session context.
* @param table Database table being processed.
* @throws ValidationException When invalid or inconsistent data were found.
*/
public void appendDBString(final Writer writer, final AbstractSession session,
- final TableDefinition table) throws ValidationException {
+ final TableDefinition table) {
+ appendDBString(writer, session, table, null);
+ }
+
+ /**
+ * INTERNAL:
+ * Append the database field definition string to the table creation/modification statement.
+ *
+ * @param writer Target writer where to write field definition string.
+ * @param session Current session context.
+ * @param table Database table being processed.
+ * @param alterSeparator Field definition is part of ALTER/MODIFY COUMN statement when not {@code null}
+ * and {@code alterSeparator} is appended after column name
+ * @throws ValidationException When invalid or inconsistent data were found.
+ */
+ public void appendDBString(final Writer writer, final AbstractSession session,
+ final TableDefinition table, String alterSeparator) throws ValidationException {
try {
- writer.write(name);
- writer.write(" ");
+ writer.write(name);
+ writer.write(" ");
+
+ if (alterSeparator != null) {
+ writer.write(alterSeparator);
+ writer.write(" ");
+ }
if (getTypeDefinition() != null) { //apply user-defined complete type definition
writer.write(typeDefinition);
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/SchemaManager.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/SchemaManager.java
index a3d1da864c0..e778d6f0e8a 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/SchemaManager.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/SchemaManager.java
@@ -39,8 +39,10 @@
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.Vector;
+import java.util.function.Consumer;
/**
*
@@ -1126,6 +1128,25 @@ public void replaceDefaultTables(boolean createSequenceTables, boolean createSeq
}
}
+ public void truncateDefaultTables(boolean generateFKConstraints) {
+ boolean shouldLogExceptionStackTrace = getSession().getSessionLog().shouldLogExceptionStackTrace();
+ session.getSessionLog().setShouldLogExceptionStackTrace(false);
+
+ try {
+ TableCreator tableCreator = getDefaultTableCreator(generateFKConstraints);
+ tableCreator.truncateTables(session, this, generateFKConstraints);
+ } catch (DatabaseException exception) {
+ // Ignore error
+ } finally {
+ session.getSessionLog().setShouldLogExceptionStackTrace(shouldLogExceptionStackTrace);
+ }
+ }
+
+ public boolean validateDefaultTables(Consumer> onFailed, boolean generateFKConstraints) {
+ TableCreator tableCreator = getDefaultTableCreator(generateFKConstraints);
+ return tableCreator.validateTables(session, this, onFailed);
+ }
+
public void setSession(DatabaseSessionImpl session) {
this.session = session;
}
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/TableCreator.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/TableCreator.java
index f1a308a179b..c6b7ed1f817 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/TableCreator.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/TableCreator.java
@@ -17,7 +17,9 @@
// - 389090: JPA 2.1 DDL Generation Support
package org.eclipse.persistence.tools.schemaframework;
+import jakarta.persistence.PersistenceException;
import org.eclipse.persistence.exceptions.DatabaseException;
+import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
@@ -28,13 +30,18 @@
import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.sessions.Session;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Consumer;
/**
* Purpose: This class is responsible for creating the tables defined in the project.
@@ -351,6 +358,30 @@ public void replaceTables(DatabaseSession session, SchemaManager schemaManager,
replaceTablesAndConstraints(schemaManager, session, createSequenceTables, createSequences);
}
+ void truncateTables(DatabaseSession session, SchemaManager schemaManager, boolean generateFKConstraints) {
+ TableCreator tableCreator = schemaManager.getDefaultTableCreator(generateFKConstraints);
+ String sequenceTableName = tableCreator.getSequenceTableName(session);
+ List tables = tableCreator.getTableDefinitions();
+ dropConstraints(session, schemaManager, false);
+ for (TableDefinition table : tables) {
+ if (!table.getName().equals(sequenceTableName)) {
+ try {
+ Writer stmtWriter = new StringWriter();
+ session.getPlatform().writeTruncateTable(stmtWriter, ((AbstractSession) session), table);
+ ((AbstractSession) session)
+ .priviledgedExecuteNonSelectingCall(
+ new org.eclipse.persistence.queries.SQLCall(stmtWriter.toString()));
+ } catch (DatabaseException ex) {
+ //Ignore database exception. eg. If there is no table to delete, it gives database exception.
+ throw ex;
+ } catch (IOException ex) {
+ throw new PersistenceException(ex);
+ }
+ }
+ }
+ createConstraints(tables, session, schemaManager, false);
+ }
+
protected void replaceTablesAndConstraints(SchemaManager schemaManager, DatabaseSession session, boolean createSequenceTables, boolean createSequences) {
buildConstraints(schemaManager, true);
boolean ignore = shouldIgnoreDatabaseException();
@@ -442,6 +473,137 @@ protected void extendTablesAndConstraints(SchemaManager schemaManager, DatabaseS
}
}
+ /**
+ * Validate tables in the database.
+ * Found issues are passed as {@link List} of {@link TableValidationException} to provided consumer
+ * when validation failed.
+ *
+ * @param session Active database session.
+ * @param schemaManager Database schema manipulation manager.
+ * @param onFailed {@link Consumer} to accept {@link List} of {@link TableValidationException}
+ * containing validation failures. Consumer is called only when validation failed.
+ * @return Value of {@code true} when validation passed or {@code false} otherwise.
+ */
+ public boolean validateTables(final DatabaseSession session,
+ final SchemaManager schemaManager,
+ Consumer> onFailed) {
+ //final String sequenceTableName = getSequenceTableName(session);
+ List tableDefinitions = getTableDefinitions();
+ List exceptions = new ArrayList<>(tableDefinitions.size());
+ tableDefinitions.forEach(tableDefinition -> {
+ String tableName = tableDefinition.getTable() == null
+ ? tableDefinition.getName()
+ : tableDefinition.getTable().getName();
+ if (schemaManager.checkTableExists(tableDefinition)) {
+ List columnsInfo = readColumnInfo((AbstractSession) session, tableDefinition);
+ if (columnsInfo != null && !columnsInfo.isEmpty()) {
+ final Map columns = parseColumnInfo((AbstractSession) session,
+ tableDefinition,
+ columnsInfo);
+ // Build list of missing columns
+ CheckDatabaseColumns check = new CheckDatabaseColumns(session, columns.size());
+ processColumnns(tableDefinition,
+ columns,
+ check::checkExisting,
+ check::addMissing,
+ check::surplusColumns);
+ // Missing columns
+ if (!check.getMissingColumns().isEmpty()) {
+ exceptions.add(
+ new TableValidationException.MissingColumns(tableName, check.getMissingColumns()));
+ }
+ // Surplus columns
+ if (!check.getSurplusFields().isEmpty()) {
+ exceptions.add(
+ new TableValidationException.SurplusColumns(
+ tableName,
+ check.getSurplusFields().stream().map(DatabaseField::getName).toList()));
+ }
+ if (!check.getExistingColumnsDiff().isEmpty()) {
+ exceptions.add(
+ new TableValidationException.DifferentColumns(tableName, check.getExistingColumnsDiff()));
+ }
+ }
+ } else {
+ exceptions.add(new TableValidationException.MissingTable(tableName));
+ }
+ });
+ if (exceptions.isEmpty()) {
+ return true;
+ } else {
+ // Pass validation failures to provided consumer
+ onFailed.accept(exceptions);
+ return false;
+ }
+ }
+
+ private static final class CheckDatabaseColumns {
+
+ final DatabaseSession session;
+ final List missingColumns;
+ final List existingColumnsDiff;
+ Set surplusFields;
+
+ private CheckDatabaseColumns(DatabaseSession session, int size) {
+ this.session = session;
+ this.missingColumns = new ArrayList<>(size);
+ this.existingColumnsDiff = new LinkedList<>();
+ this.surplusFields = null;
+ }
+
+ // Database columns check callback for missing column (existing in TableDefinition but not in database)
+ private void addMissing(FieldDefinition fieldDefinition, DatabaseField databaseField) {
+ missingColumns.add(databaseField.getName());
+ }
+
+ // Database columns check callback for column existing in both TableDefinition and the database
+ private void checkExisting(FieldDefinition fieldDefinition, DatabaseField databaseField, AbstractRecord dbRecord) {
+ FieldTypeDefinition expectedDbType = DatabaseObjectDefinition.getFieldTypeDefinition(session.getPlatform(),
+ fieldDefinition.getType(),
+ fieldDefinition.getTypeName());
+ String typeName = (String) dbRecord.get("TYPE_NAME");
+ if (typeName != null) {
+ // Type mismatch
+ if (!typeName.equals(expectedDbType.getName())) {
+ existingColumnsDiff.add(
+ new TableValidationException.DifferentColumns.TypeDifference(databaseField.getName(),
+ expectedDbType.getName(),
+ typeName));
+ }
+ }
+ }
+
+ // Database columns check callback for set of surplus fields (existing in database but not in TableDefinition)
+ private void surplusColumns(Set databaseFields) {
+ this.surplusFields = databaseFields;
+ }
+
+ private Set getSurplusFields() {
+ return surplusFields;
+ }
+
+ private List getMissingColumns() {
+ return missingColumns;
+ }
+
+ private List getExistingColumnsDiff() {
+ return existingColumnsDiff;
+ }
+
+ private interface ExistingField {
+ void accept(FieldDefinition fieldDefinition, DatabaseField databaseField, AbstractRecord dbRecord);
+ }
+
+ private interface MissingField {
+ void accept(FieldDefinition fieldDefinition, DatabaseField databaseField);
+ }
+
+ private interface SurplusFields {
+ void accept(Set surplusFields);
+ }
+
+ }
+
/**
* This creates/extends the tables on the database.
* @param session Active database session.
@@ -475,72 +637,25 @@ public void extendTables(final DatabaseSession session, final SchemaManager sche
if (alreadyExists) {
//Assume the table exists, so lookup the column info
- //While SQL is case insensitive, getColumnInfo is and will not return the table info unless the name is passed in
+ //While SQL is case-insensitive, getColumnInfo is and will not return the table info unless the name is passed in
//as it is stored internally.
- String tableName = table.getTable()==null? table.getName(): table.getTable().getName();
- final boolean usesDelimiting = (table.getTable()!=null && table.getTable().shouldUseDelimiters());
- List columnInfo = null;
-
- columnInfo = abstractSession.getAccessor().getColumnInfo(tableName, null, abstractSession);
-
- if (!usesDelimiting && (columnInfo == null || columnInfo.isEmpty()) ) {
- tableName = tableName.toUpperCase();
- columnInfo = abstractSession.getAccessor().getColumnInfo(tableName, null, abstractSession);
- if (( columnInfo == null || columnInfo.isEmpty()) ){
- tableName = tableName.toLowerCase();
- columnInfo = abstractSession.getAccessor().getColumnInfo(tableName, null, abstractSession);
- }
- }
+
+ List columnInfo = readColumnInfo(abstractSession, table);
+
if (columnInfo != null && !columnInfo.isEmpty()) {
- //Table exists, add individual fields as necessary
-
- //hash the table's existing columns by name
- final Map columns = new HashMap<>(columnInfo.size());
- final DatabaseField columnNameLookupField = new DatabaseField("COLUMN_NAME");
- final DatabaseField schemaLookupField = new DatabaseField("TABLE_SCHEM");
- boolean schemaMatchFound = false;
- // Determine the probably schema for the table, this is a heuristic, so should not cause issues if wrong.
- String qualifier = table.getQualifier();
- if ((qualifier == null) || (qualifier.length() == 0)) {
- qualifier = session.getDatasourcePlatform().getTableQualifier();
- if ((qualifier == null) || (qualifier.length() == 0)) {
- qualifier = session.getLogin().getUserName();
- // Oracle DB DS defined in WLS does not contain user name so it's stored in platform.
- if ((qualifier == null) || (qualifier.length() == 0)) {
- final DatabasePlatform platform = session.getPlatform();
- if (platform.supportsConnectionUserName()) {
- qualifier = platform.getConnectionUserName();
- }
+ // Table exists, parse read columns
+ final Map columns = parseColumnInfo(abstractSession, table, columnInfo);
+ // Add missing fields to the database
+ processMissingColumnns(table, columns, (fieldDef, dbField) -> {
+ try {
+ table.addFieldOnDatabase(abstractSession, fieldDef);
+ } catch (final DatabaseException addFieldEx) {
+ session.getSessionLog().log(SessionLog.FINEST, SessionLog.DDL, "cannot_add_field_to_table", dbField.getName(), table.getFullName(), addFieldEx.getMessage());
+ if (!shouldIgnoreDatabaseException()) {
+ throw addFieldEx;
}
}
- }
- final boolean checkSchema = (qualifier != null) && (qualifier.length() > 0);
- for (final AbstractRecord record : columnInfo) {
- final String fieldName = (String)record.get(columnNameLookupField);
- if (fieldName != null && fieldName.length() > 0) {
- final DatabaseField column = new DatabaseField(fieldName);
- if (session.getPlatform().shouldForceFieldNamesToUpperCase()) {
- column.useUpperCaseForComparisons(true);
- }
- final String schema = (String)record.get(schemaLookupField);
- // Check the schema as well. Ignore columns for other schema if a schema match is found.
- if (schemaMatchFound) {
- if (qualifier.equalsIgnoreCase(schema)) {
- columns.put(column, record);
- }
- } else {
- if (checkSchema) {
- if (qualifier.equalsIgnoreCase(schema)) {
- schemaMatchFound = true;
- // Remove unmatched columns from other schemas.
- columns.clear();
- }
- }
- // If none of the schemas match what is expected, assume what is expected is wrong, and use all columns.
- columns.put(column, record);
- }
- }
- }
+ });
//Go through each field we need to have in the table to see if it already exists
for (final FieldDefinition fieldDef : table.getFields()){
@@ -575,4 +690,121 @@ public void extendTables(final DatabaseSession session, final SchemaManager sche
session.getDatasourcePlatform().initIdentitySequences(session, DEFAULT_IDENTITY_GENERATOR);
}
+
+ // Reads column information from the database.
+ private List readColumnInfo(AbstractSession session, TableDefinition table) {
+ String tableName = table.getTable() == null ? table.getName() : table.getTable().getName();
+ boolean notUsesDelimiting = table.getTable() == null || !table.getTable().shouldUseDelimiters();
+
+ List columnInfo = session.getAccessor().getColumnInfo(tableName, null, session);
+ if (notUsesDelimiting && (columnInfo == null || columnInfo.isEmpty()) ) {
+ tableName = tableName.toUpperCase();
+ columnInfo = session.getAccessor().getColumnInfo(tableName, null, session);
+ if (( columnInfo == null || columnInfo.isEmpty()) ){
+ tableName = tableName.toLowerCase();
+ columnInfo = session.getAccessor().getColumnInfo(tableName, null, session);
+ }
+ }
+ return columnInfo;
+ }
+
+ // Parse column information read from the database.
+ private static Map parseColumnInfo(AbstractSession session,
+ TableDefinition table,
+ List columnInfo) {
+ // Hash the table's existing columns by name
+ final Map columns = new HashMap<>(columnInfo.size());
+ final DatabaseField columnNameLookupField = new DatabaseField("COLUMN_NAME");
+ final DatabaseField schemaLookupField = new DatabaseField("TABLE_SCHEM");
+ boolean schemaMatchFound = false;
+ // Determine the probable schema for the table, this is a heuristic, so should not cause issues if wrong.
+ String qualifier = table.getQualifier();
+ if ((qualifier == null) || (qualifier.length() == 0)) {
+ qualifier = session.getDatasourcePlatform().getTableQualifier();
+ if ((qualifier == null) || (qualifier.length() == 0)) {
+ qualifier = session.getLogin().getUserName();
+ // Oracle DB DS defined in WLS does not contain username, so it's stored in platform.
+ if ((qualifier == null) || (qualifier.length() == 0)) {
+ final DatabasePlatform platform = session.getPlatform();
+ if (platform.supportsConnectionUserName()) {
+ qualifier = platform.getConnectionUserName();
+ }
+ }
+ }
+ }
+ final boolean checkSchema = (qualifier != null) && (qualifier.length() > 0);
+ for (final AbstractRecord record : columnInfo) {
+ final String fieldName = (String)record.get(columnNameLookupField);
+ if (fieldName != null && fieldName.length() > 0) {
+ final DatabaseField column = new DatabaseField(fieldName);
+ if (session.getPlatform().shouldForceFieldNamesToUpperCase()) {
+ column.useUpperCaseForComparisons(true);
+ }
+ final String schema = (String)record.get(schemaLookupField);
+ // Check the schema as well. Ignore columns for other schema if a schema match is found.
+ if (schemaMatchFound) {
+ if (qualifier.equalsIgnoreCase(schema)) {
+ columns.put(column, record);
+ }
+ } else {
+ if (checkSchema) {
+ if (qualifier.equalsIgnoreCase(schema)) {
+ schemaMatchFound = true;
+ // Remove unmatched columns from other schemas.
+ columns.clear();
+ }
+ }
+ // If none of the schemas match what is expected, assume what is expected is wrong, and use all columns.
+ columns.put(column, record);
+ }
+ }
+ }
+ return columns;
+ }
+
+ // Run provided action for each column missing in the database.
+ private static void processMissingColumnns(TableDefinition table,
+ Map columns,
+ CheckDatabaseColumns.MissingField missingAction) {
+ processColumnns(table, columns, null, missingAction, null);
+ }
+
+ // Run provided action for each column missing in the database.
+ // Optionally provide set of database columns not present in the TableDefinition.
+ private static void processColumnns(TableDefinition table,
+ Map columns,
+ CheckDatabaseColumns.ExistingField existingAction,
+ CheckDatabaseColumns.MissingField missingAction,
+ CheckDatabaseColumns.SurplusFields surplusAction) {
+ // Surplus database fields if consumer was provided
+ boolean isSurplusAction = surplusAction != null;
+ Set surplusSet = isSurplusAction ? new HashSet<>(columns.keySet()) : null;
+ // Process all fields from TableDefinition
+ for (final FieldDefinition fieldDef : table.getFields()) {
+ DatabaseField dbField = fieldDef.getDatabaseField();
+ if (dbField == null) {
+ dbField = new DatabaseField(fieldDef.getName());
+ }
+ // Run action for missing column
+ AbstractRecord dbColumn = columns.get(dbField);
+ if (dbColumn == null && missingAction != null) {
+ missingAction.accept(fieldDef, dbField);
+ // Handle existing column
+ } else {
+ // Run action for existing column
+ if (existingAction != null) {
+ existingAction.accept(fieldDef, dbField, dbColumn);
+ }
+ // Update surplus columns set
+ if (isSurplusAction) {
+ surplusSet.remove(dbField);
+ }
+ }
+ }
+ // Supply set of database columns not present in the TableDefinition when requested
+ if (isSurplusAction) {
+ surplusAction.accept(surplusSet);
+ }
+ }
+
}
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/TableDefinition.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/TableDefinition.java
index bbac93712d7..87e2a83c796 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/TableDefinition.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/TableDefinition.java
@@ -145,6 +145,30 @@ public Writer buildAddFieldWriter(AbstractSession session, FieldDefinition field
return writer;
}
+ /**
+ * INTERNAL:
+ * Execute the SQL alter table to drop the field from the table.
+ */
+ public void dropFieldOnDatabase(final AbstractSession session, String fieldName) {
+ session.priviledgedExecuteNonSelectingCall(
+ new SQLCall(buildDropFieldWriter(session, fieldName, new StringWriter()).toString()));
+ }
+
+ /**
+ * INTERNAL:
+ * Return the alter table statement to drop the field from the table.
+ */
+ public Writer buildDropFieldWriter(AbstractSession session, String fieldName, Writer writer) throws ValidationException {
+ try {
+ writer.write("ALTER TABLE " + getFullName() + " ");
+ session.getPlatform().writeDropColumnClause(writer, session, this, fieldName);
+ writer.write(" ");
+ } catch (IOException ioException) {
+ throw ValidationException.fileError(ioException);
+ }
+ return writer;
+ }
+
/**
* PUBLIC:
* Add a foreign key constraint to the table.
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/TableValidationException.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/TableValidationException.java
new file mode 100644
index 00000000000..e63b56e2aa0
--- /dev/null
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/tools/schemaframework/TableValidationException.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 1998, 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+// 11/29/2023: Tomas Kraus
+// - New Jakarta Persistence 3.2 Features
+package org.eclipse.persistence.tools.schemaframework;
+
+import java.util.List;
+
+import org.eclipse.persistence.internal.localization.ExceptionLocalization;
+
+public abstract class TableValidationException extends Exception {
+ private final String table;
+ private final Type type;
+
+ private TableValidationException(String message, String table, Type type) {
+ super(message);
+ this.table = table;
+ this.type = type;
+ }
+
+ public String getTable() {
+ return table;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public abstract T as(Class type);
+
+ public static final class MissingTable extends TableValidationException {
+
+ MissingTable(String table) {
+ super(ExceptionLocalization.buildMessage(
+ "schema_validation_missing_table", new String[] {table}),
+ table, Type.MISSING_TABLE);
+ }
+
+ public T as(Class cls) {
+ if (cls == MissingTable.class) {
+ return cls.cast(this);
+ }
+ throw new IllegalArgumentException(
+ String.format("Cannot cast this TableValidationException implementation as %s", cls.getName()));
+ }
+
+ }
+
+ public static final class MissingColumns extends TableValidationException {
+
+ private final List columns;
+
+ MissingColumns(String table, List columns) {
+ super(ExceptionLocalization.buildMessage(
+ "schema_validation_table_missing_columns", new String[] {table}),
+ table, Type.MISSING_COLUMNS);
+ this.columns = columns;
+ }
+
+ public List getColumns() {
+ return columns;
+ }
+
+ public T as(Class cls) {
+ if (cls == MissingColumns.class) {
+ return cls.cast(this);
+ }
+ throw new IllegalArgumentException(
+ String.format("Cannot cast this TableValidationException implementation as %s", cls.getName()));
+ }
+
+ }
+
+ public static final class SurplusColumns extends TableValidationException {
+
+ private final List columns;
+
+ SurplusColumns(String table, List columns) {
+ super(ExceptionLocalization.buildMessage(
+ "schema_validation_table_surplus_columns", new String[] {table}),
+ table, Type.SURPLUS_COLUMNS);
+ this.columns = columns;
+ }
+
+ public List getColumns() {
+ return columns;
+ }
+
+ public T as(Class cls) {
+ if (cls == SurplusColumns.class) {
+ return cls.cast(this);
+ }
+ throw new IllegalArgumentException(
+ String.format("Cannot cast this TableValidationException implementation as %s", cls.getName()));
+ }
+
+ }
+
+ public static final class DifferentColumns extends TableValidationException {
+
+ private final List differences;
+
+ DifferentColumns(String table, List columns) {
+ super(ExceptionLocalization.buildMessage(
+ "schema_validation_table_surplus_columns", new String[] {table}),
+ table, TableValidationException.Type.SURPLUS_COLUMNS);
+ this.differences = columns;
+ }
+
+ public List getDifferences() {
+ return differences;
+ }
+
+ public T as(Class cls) {
+ if (cls == SurplusColumns.class) {
+ return cls.cast(this);
+ }
+ throw new IllegalArgumentException(
+ String.format("Cannot cast this TableValidationException implementation as %s", cls.getName()));
+ }
+
+ public static abstract class Difference {
+
+ private final String columnName;
+ private final Type type;
+
+ private Difference(String columnName, Type type) {
+ this.columnName = columnName;
+ this.type = type;
+ }
+
+ public abstract T as(Class type);
+
+ public String getColumnName() {
+ return columnName;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ }
+
+ public static class TypeDifference extends Difference {
+
+ private final String modelValue;
+ private final String dbValue;
+
+ public TypeDifference(String columnName, String modelValue, String dbValue) {
+ super(columnName, Type.TYPE_DIFFERENCE);
+ this.dbValue = dbValue;
+ this.modelValue = modelValue;
+ }
+
+ public T as(Class cls) {
+ if (cls == TypeDifference.class) {
+ return cls.cast(this);
+ }
+ throw new IllegalArgumentException(
+ String.format("Cannot cast this Difference implementation as %s", cls.getName()));
+ }
+
+ public String getDbValue() {
+ return dbValue;
+ }
+
+ public String getModelValue() {
+ return modelValue;
+ }
+
+ }
+
+ public enum Type {
+ /** Type difference. */
+ TYPE_DIFFERENCE;
+ }
+
+ }
+
+ public enum Type {
+ /** Missing table in the schema. */
+ MISSING_TABLE,
+ /** Table with missing columns in the schema. */
+ MISSING_COLUMNS,
+ /** Table with surplus columns in the schema. */
+ SURPLUS_COLUMNS;
+ }
+
+
+}
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/main/java/org/eclipse/persistence/testing/models/jpa/persistence32/Persistence32TableCreator.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/main/java/org/eclipse/persistence/testing/models/jpa/persistence32/Persistence32TableCreator.java
deleted file mode 100644
index a2753454dcd..00000000000
--- a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/main/java/org/eclipse/persistence/testing/models/jpa/persistence32/Persistence32TableCreator.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0,
- * or the Eclipse Distribution License v. 1.0 which is available at
- * http://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
- */
-package org.eclipse.persistence.testing.models.jpa.persistence32;
-
-import org.eclipse.persistence.testing.framework.TogglingFastTableCreator;
-import org.eclipse.persistence.tools.schemaframework.TableDefinition;
-
-public class Persistence32TableCreator extends TogglingFastTableCreator {
-
- public Persistence32TableCreator() {
- setName("Persistence32Project");
- addTableDefinition(buildTeamTable());
- addTableDefinition(buildTrainerTable());
- addTableDefinition(buildTypeTable());
- addTableDefinition(buildPokemonTable());
- addTableDefinition(buildPokemonTypeTable());
- addTableDefinition(buildSyntaxEntityTable());
- }
-
- public static TableDefinition buildTeamTable() {
- TableDefinition table = new TableDefinition();
- table.setName("PERSISTENCE32_TEAM");
- table.addField(createNumericPk("ID"));
- table.addField(createStringColumn("NAME", 64, false));
- return table;
- }
-
- public static TableDefinition buildTrainerTable() {
- TableDefinition table = new TableDefinition();
- table.setName("PERSISTENCE32_TRAINER");
- table.addField(createNumericPk("ID"));
- table.addField(createStringColumn("NAME", 64, false));
- table.addField(createNumericFk("TEAM_ID", 15,"PERSISTENCE32_TEAM.ID", false));
- return table;
- }
-
- public static TableDefinition buildTypeTable() {
- TableDefinition table = new TableDefinition();
- table.setName("PERSISTENCE32_TYPE");
- table.addField(createNumericPk("ID"));
- table.addField(createStringColumn("NAME", 64, false));
- return table;
- }
-
- public static TableDefinition buildPokemonTable() {
- TableDefinition table = new TableDefinition();
- table.setName("PERSISTENCE32_POKEMON");
- table.addField(createNumericPk("ID"));
- table.addField(createNumericFk("TRAINER_ID", 15,"PERSISTENCE32_TRAINER.ID", true));
- table.addField(createStringColumn("NAME", 64, false));
- return table;
- }
-
- public static TableDefinition buildPokemonTypeTable() {
- TableDefinition table = new TableDefinition();
- table.setName("PERSISTENCE32_POKEMON_TYPE");
- table.addField(createNumericFk("POKEMON_ID", "PERSISTENCE32_POKEMON.ID"));
- table.addField(createNumericFk("TYPE_ID", "PERSISTENCE32_TYPE.ID"));
- return table;
- }
-
- public static TableDefinition buildSyntaxEntityTable() {
- TableDefinition table = new TableDefinition();
- table.setName("PERSISTENCE32_SYNTAX_ENTITY");
- table.addField(createNumericPk("ID"));
- table.addField(createStringColumn("STR_VAL_1", 128, true));
- table.addField(createStringColumn("STR_VAL_2", 128, true));
- table.addField(createNumericColumn("INT_VAL", 15, true));
- table.addField(createTimeColumn("TIME_VAL", 3, true));
- table.addField(createDateColumn("DATE_VAL", 3, true));
- return table;
- }
-
-}
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/main/resources/META-INF/persistence.xml b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/main/resources/META-INF/persistence.xml
index 81008711a8a..a328f4fc450 100644
--- a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/main/resources/META-INF/persistence.xml
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/main/resources/META-INF/persistence.xml
@@ -11,11 +11,12 @@
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
-->
-
+ xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence
+ https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
+ version="3.0">
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/AbstractPokemon.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/AbstractPokemon.java
index c3a767ed9f8..34258ec09f8 100644
--- a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/AbstractPokemon.java
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/AbstractPokemon.java
@@ -15,19 +15,14 @@
import java.util.Map;
import jakarta.persistence.EntityManager;
-import junit.framework.TestSuite;
-import org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl;
-import org.eclipse.persistence.jpa.JpaEntityManagerFactory;
-import org.eclipse.persistence.testing.framework.jpa.junit.JUnitTestCase;
-import org.eclipse.persistence.testing.models.jpa.persistence32.Persistence32TableCreator;
import org.eclipse.persistence.testing.models.jpa.persistence32.Team;
import org.eclipse.persistence.testing.models.jpa.persistence32.Trainer;
import org.eclipse.persistence.testing.models.jpa.persistence32.Type;
/**
- * Abstract jUnit test suite with Pokemon model.
+ * {@link AbstractSuite} with Pokemon model.
*/
-public abstract class AbstractPokemon extends JUnitTestCase {
+public abstract class AbstractPokemon extends AbstractSuite {
// Trainer's teams
static final Team[] TEAMS = new Team[] {
@@ -68,30 +63,17 @@ public abstract class AbstractPokemon extends JUnitTestCase {
};
/**
- * Build test suite.
- * Adds model test setup as first and model test cleanup as last test
- * in the returned tests collection.
- *
- * @param name name of the suite
- * @param tests tests to add to the suite
- * @return collection of tests to execute
+ * Creates an instance of {@link AbstractPokemon}.
*/
- static TestSuite suite(String name, AbstractPokemon... tests) {
- TestSuite suite = new TestSuite();
- suite.setName(name);
- suite.addTest(new AbstractPokemon("testSetup") {});
- for (AbstractPokemon test : tests) {
- suite.addTest(test);
- }
- suite.addTest(new AbstractPokemon("testCleanup") {});
- return suite;
- }
-
- JpaEntityManagerFactory emf = null;
-
public AbstractPokemon() {
+ super();
}
+ /**
+ * Creates an instance of {@link AbstractPokemon} with custom test case name.
+ *
+ * @param name name of the test case
+ */
public AbstractPokemon(String name) {
super(name);
setPuName(getPersistenceUnitName());
@@ -102,33 +84,11 @@ public String getPersistenceUnitName() {
return "persistence32";
}
- @Override
- public void setUp() {
- super.setUp();
- emf = getEntityManagerFactory(getPersistenceUnitName())
- .unwrap(EntityManagerFactoryImpl.class);
- }
- /**
- * Return all pokemon types as ID {@link Map}.
- *
- * @param em {@link EntityManager} instance to execute the query
- * @return {@link Map} with pokemon types
- */
- Map pokemonTypes(EntityManager em) {
- Map types = new HashMap<>(TYPES.length);
- em.createNamedQuery("Type.all", Type.class)
- .getResultList()
- .forEach(type -> types.put(type.getId(), type));
- return types;
- }
-
- /**
- * The setup is done as a test, both to record its failure, and to allow
- * execution in the server.
- */
- public void testSetup() {
- new Persistence32TableCreator().replaceTables(JUnitTestCase.getServerSession(getPersistenceUnitName()));
+ // Initialize data
+ @Override
+ protected void suiteSetUp() {
+ super.suiteSetUp();
emf.runInTransaction(em -> {
for (int i = 1; i < TEAMS.length; i++) {
em.persist(TEAMS[i]);
@@ -144,23 +104,17 @@ public void testSetup() {
}
/**
- * The setup is done as a test, both to record its failure, and to allow
- * execution in the server.
+ * Return all pokemon types as ID {@link Map}.
+ *
+ * @param em {@link EntityManager} instance to execute the query
+ * @return {@link Map} with pokemon types
*/
- public void testCleanup() {
- emf.runInTransaction(em -> {
- em.createNamedQuery("Pokemon.deleteAllTypes").executeUpdate();
- em.createNamedQuery("Pokemon.deleteAll").executeUpdate();
- em.createNamedQuery("Type.deleteAll").executeUpdate();
- em.createNamedQuery("Trainer.deleteAll").executeUpdate();
- em.createNamedQuery("Team.deleteAll").executeUpdate();
- });
- }
-
- @Override
- public void clearCache() {
- emf.getCache().evictAll();
- super.clearCache();
+ Map pokemonTypes(EntityManager em) {
+ Map types = new HashMap<>(TYPES.length);
+ em.createNamedQuery("Type.all", Type.class)
+ .getResultList()
+ .forEach(type -> types.put(type.getId(), type));
+ return types;
}
}
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/AbstractSchemaManager.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/AbstractSchemaManager.java
new file mode 100644
index 00000000000..3fb633b06c1
--- /dev/null
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/AbstractSchemaManager.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 1998, 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.eclipse.persistence.testing.tests.jpa.persistence32;
+
+import java.util.Set;
+
+import junit.framework.TestSuite;
+import org.eclipse.persistence.exceptions.DatabaseException;
+import org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl;
+import org.eclipse.persistence.jpa.JpaEntityManagerFactory;
+import org.eclipse.persistence.testing.framework.ReflectionHelper;
+import org.eclipse.persistence.testing.framework.jpa.junit.JUnitTestCase;
+import org.eclipse.persistence.tools.schemaframework.TableCreator;
+
+/**
+ * Test {@link jakarta.persistence.SchemaManager} implementation in EclipseLink.
+ * This is an abstract class with all common code for child classes with individual tests.
+ * All those tests are based on database schema modifications, so they shall not run in parallel.
+ * Each child class shall contain just a single test.
+ */
+public abstract class AbstractSchemaManager extends JUnitTestCase {
+ private org.eclipse.persistence.tools.schemaframework.SchemaManager schemaManager;
+ JpaEntityManagerFactory emf = null;
+
+ public AbstractSchemaManager() {
+ }
+
+ public AbstractSchemaManager(String name) {
+ super(name);
+ setPuName(getPersistenceUnitName());
+ }
+
+ @Override
+ public String getPersistenceUnitName() {
+ return "persistence32";
+ }
+
+ @Override
+ public void setUp() {
+ super.setUp();
+ emf = getEntityManagerFactory(getPersistenceUnitName())
+ .unwrap(EntityManagerFactoryImpl.class);
+ schemaManager = new org.eclipse.persistence.tools.schemaframework.SchemaManager(emf.getDatabaseSession());
+ dropTables();
+ }
+
+ @Override
+ public void tearDown() {
+ dropTables();
+ }
+
+ /**
+ * Build test suite.
+ * Schema manager suites may contain just a single test.
+ *
+ * @param name name of the suite
+ * @param test test to add to the suite
+ * @return collection of tests to execute
+ */
+ static TestSuite suite(String name, AbstractSchemaManager test) {
+ TestSuite suite = new TestSuite();
+ suite.setName(name);
+ suite.addTest(test);
+ return suite;
+ }
+
+ TableCreator getDefaultTableCreator() {
+ // getDefaultTableCreator has protected access
+ try {
+ return ReflectionHelper.invokeMethod("getDefaultTableCreator",
+ schemaManager,
+ new Class>[] {boolean.class},
+ TableCreator.class,
+ new Object[] {Boolean.TRUE});
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException("Invocation of getDefaultTableCreator failed", e);
+ }
+ }
+
+ void dropTables() {
+ try {
+ schemaManager.dropDefaultTables();
+ } catch (DatabaseException de) {
+ emf.getDatabaseSession().logMessage(de.getLocalizedMessage());
+ }
+ }
+
+ void createTables() {
+ try {
+ schemaManager.createDefaultTables(true);
+ } catch (DatabaseException de) {
+ emf.getDatabaseSession().logMessage(de.getLocalizedMessage());
+ }
+ }
+
+ void checkMissingTable(Set initialMissingTablesSet, Set missingTablesSet, Set checked, String table) {
+ if (missingTablesSet.contains(table)) {
+ missingTablesSet.remove(table);
+ checked.add(table);
+ } else {
+ boolean first = true;
+ StringBuilder sb = new StringBuilder();
+ sb.append('[');
+ for (String missingTable : initialMissingTablesSet) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ sb.append(missingTable);
+ }
+ sb.append(']');
+ if (checked.contains(table)) {
+ fail(String.format("Duplicate table %s entry was found in expected tables Set %s", table, sb.toString()));
+ } else {
+ fail(String.format("Table %s was not found in expected tables Set %s", table, sb.toString()));
+ }
+ }
+ }
+
+}
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/AbstractSuite.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/AbstractSuite.java
new file mode 100644
index 00000000000..7382b11f17f
--- /dev/null
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/AbstractSuite.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.eclipse.persistence.testing.tests.jpa.persistence32;
+
+import jakarta.persistence.Persistence;
+import junit.framework.TestSuite;
+import org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl;
+import org.eclipse.persistence.jpa.JpaEntityManagerFactory;
+import org.eclipse.persistence.testing.framework.jpa.junit.JUnitTestCase;
+import org.eclipse.persistence.testing.framework.junit.JUnitTestCaseHelper;
+
+/**
+ * Abstract {@link JUnitTestCase} suite.
+ * Adds {@link #suiteSetUp()} and {@link #suiteTearDown()} methods executed before and after
+ * whole test suite execution.
+ * Contains suite wide {@link jakarta.persistence.EntityManagerFactory} instance initialized
+ * by {@link #getPersistenceUnitName()}.
+ * {@link jakarta.persistence.EntityManagerFactory} and database schema are initialized
+ * in {@link #suiteSetUp()} method. Database schema is dropped in {@link #suiteTearDown()} method.
+ *
+ */
+public abstract class AbstractSuite extends JUnitTestCase {
+
+ // Total number of tests in this suite
+ private static int TEST_COUNT = 0;
+ // Test counter (decreasing from TEST_COUNT to 0) to trigger suiteSetUp/suiteTearDown
+ private static int testCounter;
+
+ // EntityManagerFactory instance shared by the whole suite
+ static JpaEntityManagerFactory emf = null;
+
+ /**
+ * Build test suite.
+ * Adds model test setup as first and model test cleanup as last test
+ * in the returned tests collection.
+ * Using this metod is mandatory for suite creation.
+ *
+ * @param name name of the suite
+ * @param tests tests to add to the suite
+ * @return collection of tests to execute
+ */
+ static TestSuite suite(String name, AbstractSuite... tests) {
+ TestSuite suite = new TestSuite();
+ suite.setName(name);
+ for (AbstractSuite test : tests) {
+ suite.addTest(test);
+ }
+ testCounter = TEST_COUNT = suite.testCount();
+ return suite;
+ }
+
+ /**
+ * Creates an instance of {@link AbstractSuite}.
+ */
+ public AbstractSuite() {
+ super();
+ }
+
+ /**
+ * Creates an instance of {@link AbstractSuite} with custom test case name.
+ *
+ * @param name name of the test case
+ */
+ public AbstractSuite(String name) {
+ super(name);
+ setPuName(getPersistenceUnitName());
+ }
+
+ /**
+ * Initialize the test suite.
+ * This method is being executed before the whole test suite. This method initializes the suite wide
+ * {@link jakarta.persistence.EntityManagerFactory} instance and creates the database schema.
+ * Child class may overwrite this method to do additional initialization but should also call this method too.
+ */
+ protected void suiteSetUp() {
+ emf = Persistence.createEntityManagerFactory(
+ getPersistenceUnitName(),
+ JUnitTestCaseHelper.getDatabaseProperties(getPersistenceUnitName()))
+ .unwrap(EntityManagerFactoryImpl.class);
+ emf.getSchemaManager().create(true);
+ }
+
+ /**
+ * Clean up the test suite.
+ * This method is being executed after the whole test suite. This method drops the database schema
+ * and closes the suite wide {@link jakarta.persistence.EntityManagerFactory} instance.
+ * Child class may overwrite this method to do additional cleanup but should also call this method too.
+ */
+ protected void suiteTearDown() {
+ emf.getSchemaManager().drop(true);
+ emf.close();
+ }
+
+ /**
+ * This method is called before a test is executed.
+ * This method implements {@link #suiteSetUp()} call, so it shall be called by child class if overwritten.
+ */
+ @Override
+ public void setUp() {
+ super.setUp();
+ if (testCounter == TEST_COUNT) {
+ suiteSetUp();
+ }
+ testCounter--;
+ }
+
+ /**
+ * This method is called after a test is executed.
+ * This method implements {@link #suiteTearDown()} call, so it shall be called by child class if overwritten.
+ */
+ @Override
+ public void tearDown() {
+ super.tearDown();
+ if (testCounter == 0) {
+ suiteTearDown();
+ }
+ }
+
+ @Override
+ public void clearCache() {
+ emf.getCache().evictAll();
+ super.clearCache();
+ }
+
+}
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/CriteriaBuilderTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/CriteriaBuilderTest.java
index 6c0e1bf0b16..b08dc900998 100644
--- a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/CriteriaBuilderTest.java
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/CriteriaBuilderTest.java
@@ -30,14 +30,12 @@
import jakarta.persistence.criteria.ParameterExpression;
import jakarta.persistence.criteria.Root;
import junit.framework.Test;
-import junit.framework.TestSuite;
-import org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl;
-import org.eclipse.persistence.jpa.JpaEntityManagerFactory;
-import org.eclipse.persistence.testing.framework.jpa.junit.JUnitTestCase;
-import org.eclipse.persistence.testing.models.jpa.persistence32.Persistence32TableCreator;
import org.eclipse.persistence.testing.models.jpa.persistence32.SyntaxEntity;
-public class CriteriaBuilderTest extends JUnitTestCase {
+/**
+ * Verify jakarta.persistence 3.2 API changes in {@link CriteriaBuilder}.
+ */
+public class CriteriaBuilderTest extends AbstractSuite {
// SyntaxEntity instances, array index is equal to ID
// Values must be unique to get just single result and verify it by ID
@@ -50,47 +48,43 @@ public class CriteriaBuilderTest extends JUnitTestCase {
new SyntaxEntity(5L, null, null, null, null, LocalDate.of(1918, 9, 28), null)
};
- private static final int ENTITIES_COUNT = ENTITIES.length - 1;
-
- private JpaEntityManagerFactory emf = null;
-
public static Test suite() {
- TestSuite suite = new TestSuite();
- suite.setName("CriteriaBuilderTests");
- suite.addTest(new CriteriaBuilderTest("testSetup"));
- suite.addTest(new CriteriaBuilderTest("testAndPredicateAsListOf0"));
- suite.addTest(new CriteriaBuilderTest("testOrPredicateAsListOf0"));
- suite.addTest(new CriteriaBuilderTest("testAndPredicateAsListOf1"));
- suite.addTest(new CriteriaBuilderTest("testOrPredicateAsListOf1"));
- suite.addTest(new CriteriaBuilderTest("testAndPredicateAsListOf2"));
- suite.addTest(new CriteriaBuilderTest("testOrPredicateAsListOf2"));
- suite.addTest(new CriteriaBuilderTest("testAndPredicateAsListOfN"));
- suite.addTest(new CriteriaBuilderTest("testOrPredicateAsListOfN"));
- suite.addTest(new CriteriaBuilderTest("testLeftIntLen"));
- suite.addTest(new CriteriaBuilderTest("testLeftExprLen"));
- suite.addTest(new CriteriaBuilderTest("testRightIntLen"));
- suite.addTest(new CriteriaBuilderTest("testRightExprLen"));
- suite.addTest(new CriteriaBuilderTest("testReplaceExprExpr"));
- suite.addTest(new CriteriaBuilderTest("testReplaceExprStr"));
- suite.addTest(new CriteriaBuilderTest("testReplaceStrExpr"));
- suite.addTest(new CriteriaBuilderTest("testReplaceStrStr"));
- suite.addTest(new CriteriaBuilderTest("testExtractHourFromTime"));
- suite.addTest(new CriteriaBuilderTest("testExtractMinuteFromTime"));
- suite.addTest(new CriteriaBuilderTest("testExtractSecondFromTime"));
- suite.addTest(new CriteriaBuilderTest("testExtractYearFromDate"));
- suite.addTest(new CriteriaBuilderTest("testExtractMonthFromDate"));
- suite.addTest(new CriteriaBuilderTest("testExtractDayFromDate"));
- suite.addTest(new CriteriaBuilderTest("testExtractQuarterFromDate"));
- suite.addTest(new CriteriaBuilderTest("testExtractWeekFromDate"));
- suite.addTest(new CriteriaBuilderTest("testExpressionEqualToExpression"));
- suite.addTest(new CriteriaBuilderTest("testExpressionEqualToObject"));
- suite.addTest(new CriteriaBuilderTest("testExpressionNotEqualToExpression"));
- suite.addTest(new CriteriaBuilderTest("testExpressionNotEqualToObject"));
- suite.addTest(new CriteriaBuilderTest("testExpressionCast"));
- return suite;
+ return suite(
+ "CriteriaBuilderTests",
+ new CriteriaBuilderTest("testAndPredicateAsListOf0"),
+ new CriteriaBuilderTest("testOrPredicateAsListOf0"),
+ new CriteriaBuilderTest("testAndPredicateAsListOf1"),
+ new CriteriaBuilderTest("testOrPredicateAsListOf1"),
+ new CriteriaBuilderTest("testAndPredicateAsListOf2"),
+ new CriteriaBuilderTest("testOrPredicateAsListOf2"),
+ new CriteriaBuilderTest("testAndPredicateAsListOfN"),
+ new CriteriaBuilderTest("testOrPredicateAsListOfN"),
+ new CriteriaBuilderTest("testLeftIntLen"),
+ new CriteriaBuilderTest("testLeftExprLen"),
+ new CriteriaBuilderTest("testRightIntLen"),
+ new CriteriaBuilderTest("testRightExprLen"),
+ new CriteriaBuilderTest("testReplaceExprExpr"),
+ new CriteriaBuilderTest("testReplaceExprStr"),
+ new CriteriaBuilderTest("testReplaceStrExpr"),
+ new CriteriaBuilderTest("testReplaceStrStr"),
+ new CriteriaBuilderTest("testExtractHourFromTime"),
+ new CriteriaBuilderTest("testExtractMinuteFromTime"),
+ new CriteriaBuilderTest("testExtractSecondFromTime"),
+ new CriteriaBuilderTest("testExtractYearFromDate"),
+ new CriteriaBuilderTest("testExtractMonthFromDate"),
+ new CriteriaBuilderTest("testExtractDayFromDate"),
+ new CriteriaBuilderTest("testExtractQuarterFromDate"),
+ new CriteriaBuilderTest("testExtractWeekFromDate"),
+ new CriteriaBuilderTest("testExpressionEqualToExpression"),
+ new CriteriaBuilderTest("testExpressionEqualToObject"),
+ new CriteriaBuilderTest("testExpressionNotEqualToExpression"),
+ new CriteriaBuilderTest("testExpressionNotEqualToObject"),
+ new CriteriaBuilderTest("testExpressionCast")
+ );
}
public CriteriaBuilderTest() {
+ super();
}
public CriteriaBuilderTest(String name) {
@@ -104,18 +98,8 @@ public String getPersistenceUnitName() {
}
@Override
- public void setUp() {
- super.setUp();
- emf = getEntityManagerFactory(getPersistenceUnitName()).unwrap(EntityManagerFactoryImpl.class);
- }
-
- /**
- * The setup is done as a test, both to record its failure, and to allow
- * execution in the server.
- */
- public void testSetup() {
- new Persistence32TableCreator().replaceTables(JUnitTestCase.getServerSession(getPersistenceUnitName()));
- clearCache();
+ protected void suiteSetUp() {
+ super.suiteSetUp();
try (EntityManager em = emf.createEntityManager()) {
EntityTransaction et = em.getTransaction();
try {
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/EntityManagerFactoryTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/EntityManagerFactoryTest.java
index dd5e7f3925b..a04c11817d7 100644
--- a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/EntityManagerFactoryTest.java
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/EntityManagerFactoryTest.java
@@ -37,6 +37,9 @@
import org.eclipse.persistence.testing.models.jpa.persistence32.Trainer_;
import org.eclipse.persistence.testing.models.jpa.persistence32.Type;
+/**
+ * Verify jakarta.persistence 3.2 API changes in {@link jakarta.persistence.EntityManagerFactory}.
+ */
public class EntityManagerFactoryTest extends AbstractPokemon {
public static Test suite() {
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/QueryTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/QueryTest.java
index b4924dd83be..639fffa6963 100644
--- a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/QueryTest.java
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/QueryTest.java
@@ -20,6 +20,9 @@
import org.eclipse.persistence.testing.models.jpa.persistence32.Pokemon;
import org.junit.Assert;
+/**
+ * Verify jakarta.persistence 3.2 API changes in queries.
+ */
public class QueryTest extends AbstractPokemon {
// Pokemons. Array index is ID value.
@@ -35,7 +38,6 @@ public class QueryTest extends AbstractPokemon {
public static Test suite() {
return suite(
"QueryTest",
- new QueryTest("testSetup"),
new QueryTest("testGetSingleResultWithEmptyResult"),
new QueryTest("testGetSingleResultWithSingleResult"),
new QueryTest("testGetSingleResultWithMultipleResults"),
@@ -55,11 +57,10 @@ public QueryTest(String name) {
setPuName(getPersistenceUnitName());
}
- /**
- * The setup is done as a test, both to record its failure, and to allow
- * execution in the server.
- */
- public void testSetup() {
+ // Initialize data
+ @Override
+ protected void suiteSetUp() {
+ super.suiteSetUp();
emf.runInTransaction(em -> {
for (int i = 1; i < POKEMONS.length; i++) {
em.persist(POKEMONS[i]);
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerCreateTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerCreateTest.java
new file mode 100644
index 00000000000..42ff3570232
--- /dev/null
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerCreateTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.eclipse.persistence.testing.tests.jpa.persistence32;
+
+import jakarta.persistence.SchemaManager;
+import junit.framework.Test;
+
+import static org.eclipse.persistence.testing.tests.jpa.persistence32.AbstractPokemon.TEAMS;
+import static org.eclipse.persistence.testing.tests.jpa.persistence32.AbstractPokemon.TRAINERS;
+import static org.eclipse.persistence.testing.tests.jpa.persistence32.AbstractPokemon.TYPES;
+
+/**
+ * Verify jakarta.persistence 3.2 API changes in {@link SchemaManager}.
+ * Test {@link SchemaManager#create(boolean)} method on database with no schema.
+ */
+public class SchemaManagerCreateTest extends AbstractSchemaManager {
+
+ public static Test suite() {
+ return suite(
+ "SchemaManagerCreateTest",
+ new SchemaManagerCreateTest("testCreate")
+ );
+ }
+ public SchemaManagerCreateTest() {
+ }
+
+ public SchemaManagerCreateTest(String name) {
+ super(name);
+ }
+
+ // Test SchemaManager create method
+ public void testCreate() {
+ // Tables are always dropped in setUp() method
+ // Create the schema
+ SchemaManager schemaManager = emf.getSchemaManager();
+ schemaManager.create(true);
+ // Try to store data into the schema
+ emf.runInTransaction(em -> {
+ for (int i = 1; i < TEAMS.length; i++) {
+ em.persist(TEAMS[i]);
+ }
+ for (int i = 1; i < TRAINERS.length; i++) {
+ em.persist(TRAINERS[i]);
+ }
+ for (int i = 1; i < TYPES.length; i++) {
+ em.persist(TYPES[i]);
+ }
+ });
+ }
+
+
+
+}
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerDropTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerDropTest.java
new file mode 100644
index 00000000000..d594e3a0dc7
--- /dev/null
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerDropTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.eclipse.persistence.testing.tests.jpa.persistence32;
+
+import jakarta.persistence.PersistenceException;
+import jakarta.persistence.SchemaManager;
+import junit.framework.Test;
+import org.eclipse.persistence.logging.SessionLog;
+
+import static org.eclipse.persistence.testing.tests.jpa.persistence32.AbstractPokemon.TEAMS;
+import static org.eclipse.persistence.testing.tests.jpa.persistence32.AbstractPokemon.TRAINERS;
+import static org.eclipse.persistence.testing.tests.jpa.persistence32.AbstractPokemon.TYPES;
+
+/**
+ * Verify jakarta.persistence 3.2 API changes in {@link SchemaManager}.
+ * Test {@link SchemaManager#drop(boolean)} method on database with already existing schema.
+ */
+public class SchemaManagerDropTest extends AbstractSchemaManager {
+
+ public static Test suite() {
+ return suite(
+ "SchemaManagerDropTest",
+ new SchemaManagerDropTest("testDrop")
+ );
+ }
+ public SchemaManagerDropTest() {
+ }
+
+ public SchemaManagerDropTest(String name) {
+ super(name);
+ }
+
+ // Test SchemaManager drop method
+ public void testDrop() {
+ // Make sure that tables exist before being dropped
+ createTables();
+ // ...and persist call works
+ emf.runInTransaction(em -> {
+ for (int i = 1; i < TEAMS.length; i++) {
+ em.persist(TEAMS[i]);
+ }
+ for (int i = 1; i < TRAINERS.length; i++) {
+ em.persist(TRAINERS[i]);
+ }
+ for (int i = 1; i < TYPES.length; i++) {
+ em.persist(TYPES[i]);
+ }
+ });
+ // Drop the schema
+ SchemaManager schemaManager = emf.getSchemaManager();
+ schemaManager.drop(true);
+ // Turn off logging to suppress expected SQL errors warnings
+ int logLevel = emf.getDatabaseSession().getSessionLog().getLevel();
+ emf.getDatabaseSession().getSessionLog().setLevel(SessionLog.OFF);
+ // Verify that any attempt to store data throws an exception because of missing tables
+ // - Team entity
+ try {
+ emf.runInTransaction(em -> {
+ for (int i = 1; i < TEAMS.length; i++) {
+ em.persist(TEAMS[i]);
+ }
+ });
+ fail("Calling persist on entity after database schema was deleted shall throw an exception.");
+ } catch (PersistenceException pe) {
+ assertTrue(
+ "Unexpected exception message: " + pe.getLocalizedMessage(),
+ pe.getLocalizedMessage().contains("does not exist"));
+ }
+ // - Trainer entity
+ try {
+ emf.runInTransaction(em -> {
+ for (int i = 1; i < TRAINERS.length; i++) {
+ em.persist(TRAINERS[i]);
+ }
+ });
+ fail("Calling persist on entity after database schema was deleted shall throw an exception.");
+ } catch (PersistenceException pe) {
+ assertTrue(
+ "Unexpected exception message: " + pe.getLocalizedMessage(),
+ pe.getLocalizedMessage().contains("does not exist"));
+ }
+ // - Type entity
+ try {
+ emf.runInTransaction(em -> {
+ for (int i = 1; i < TYPES.length; i++) {
+ em.persist(TYPES[i]);
+ }
+ });
+ fail("Calling persist on entity after database schema was deleted shall throw an exception.");
+ } catch (PersistenceException pe) {
+ assertTrue(
+ "Unexpected exception message: " + pe.getLocalizedMessage(),
+ pe.getLocalizedMessage().contains("does not exist"));
+ }
+ emf.getDatabaseSession().getSessionLog().setLevel(logLevel);
+ }
+
+}
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerTruncateOnExistingTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerTruncateOnExistingTest.java
new file mode 100644
index 00000000000..4e45f696c07
--- /dev/null
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerTruncateOnExistingTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.eclipse.persistence.testing.tests.jpa.persistence32;
+
+import jakarta.persistence.SchemaManager;
+import junit.framework.Test;
+
+import static org.eclipse.persistence.testing.tests.jpa.persistence32.AbstractPokemon.TEAMS;
+import static org.eclipse.persistence.testing.tests.jpa.persistence32.AbstractPokemon.TRAINERS;
+import static org.eclipse.persistence.testing.tests.jpa.persistence32.AbstractPokemon.TYPES;
+
+/**
+ * Verify jakarta.persistence 3.2 API changes in {@link SchemaManager}.
+ * Test {@link SchemaManager#truncate()} method on database with already existing schema and data.
+ */
+public class SchemaManagerTruncateOnExistingTest extends AbstractSchemaManager {
+
+ public static Test suite() {
+ return suite(
+ "SchemaManagerTruncateOnExistingTest",
+ new SchemaManagerTruncateOnExistingTest("testTruncateOnExistingSchema")
+ );
+ }
+ public SchemaManagerTruncateOnExistingTest() {
+ }
+
+ public SchemaManagerTruncateOnExistingTest(String name) {
+ super(name);
+ }
+
+ // Test SchemaManager truncate method
+ public void testTruncateOnExistingSchema() {
+ // Tables are always dropped in setUp() method
+ // Make sure that tables exist and contain data
+ createTables();
+ emf.runInTransaction(em -> {
+ for (int i = 1; i < TEAMS.length; i++) {
+ em.persist(TEAMS[i]);
+ }
+ for (int i = 1; i < TRAINERS.length; i++) {
+ em.persist(TRAINERS[i]);
+ }
+ for (int i = 1; i < TYPES.length; i++) {
+ em.persist(TYPES[i]);
+ }
+ });
+ // Truncate the schema
+ SchemaManager schemaManager = emf.getSchemaManager();
+ schemaManager.truncate();
+ // Verify that tables still exist but are empty
+ // - Team count shall be 0
+ int teamCount = emf.callInTransaction(
+ em -> em.createQuery("SELECT count(t) FROM Team t", Integer.class).getFirstResult());
+ assertEquals(teamCount, 0);
+ // - Trainer count shall be 0
+ int trainerCount = emf.callInTransaction(
+ em -> em.createQuery("SELECT count(t) FROM Trainer t", Integer.class).getFirstResult());
+ assertEquals(trainerCount, 0);
+ // - Type count shall be 0
+ int typeCount = emf.callInTransaction(
+ em -> em.createQuery("SELECT count(t) FROM Type t", Integer.class).getFirstResult());
+ assertEquals(typeCount, 0);
+ }
+
+}
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnMissingColumnTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnMissingColumnTest.java
new file mode 100644
index 00000000000..f6fc6a3a9b5
--- /dev/null
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnMissingColumnTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.eclipse.persistence.testing.tests.jpa.persistence32;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import jakarta.persistence.SchemaManager;
+import jakarta.persistence.SchemaValidationException;
+import junit.framework.Test;
+import org.eclipse.persistence.tools.schemaframework.TableCreator;
+import org.eclipse.persistence.tools.schemaframework.TableDefinition;
+import org.eclipse.persistence.tools.schemaframework.TableValidationException;
+
+/**
+ * Verify jakarta.persistence 3.2 API changes in {@link SchemaManager}.
+ * Test {@link jakarta.persistence.SchemaManager#validate()} method on database with existing but modified schema.
+ */
+public class SchemaManagerValidateOnMissingColumnTest extends AbstractSchemaManager {
+
+ public static Test suite() {
+ return suite(
+ "SchemaManagerValidateOnMissingColumnTest",
+ new SchemaManagerValidateOnMissingColumnTest("testValidateOnMissingColumn")
+ );
+ }
+ public SchemaManagerValidateOnMissingColumnTest() {
+ }
+
+ public SchemaManagerValidateOnMissingColumnTest(String name) {
+ super(name);
+ }
+
+ // Test SchemaManager validate method on existing valid schema
+ public void testValidateOnMissingColumn() {
+ // Make sure that tables exist
+ createTables();
+ // Modify current schema
+ TableCreator tableCreator = getDefaultTableCreator();
+ Map tableDefinitions = new HashMap<>(tableCreator.getTableDefinitions().size());
+ for (TableDefinition tableDefinition : tableCreator.getTableDefinitions()) {
+ String tableName = tableDefinition.getTable() == null
+ ? tableDefinition.getName()
+ : tableDefinition.getTable().getName();
+ tableDefinitions.put(tableName, tableDefinition);
+ }
+ // Remove "NAME" field from "PERSISTENCE32_TEAM"
+ TableDefinition team = tableDefinitions.get("PERSISTENCE32_TEAM");
+ team.dropFieldOnDatabase(emf.getDatabaseSession(), "NAME");
+ // Do the validation
+ SchemaManager schemaManager = emf.getSchemaManager();
+ try {
+ // Test validation
+ schemaManager.validate();
+ } catch (SchemaValidationException sve) {
+ // Validation is expected to fail and return all missing columns
+ Exception[] exceptions = sve.getFailures();
+ Map> missingColumns = Map.of("PERSISTENCE32_TEAM", Set.of("NAME"));
+ for (TableValidationException exception : (TableValidationException[]) exceptions) {
+ if (!(exception instanceof TableValidationException.MissingColumns)) {
+ fail("Exception is not an instance of TableValidationException.MissingColumns");
+ }
+ if (exception.getType() == TableValidationException.Type.MISSING_COLUMNS) {
+ assertEquals("PERSISTENCE32_TEAM", exception.getTable());
+
+ } else {
+ fail("Exception type is not MISSING_COLUMNS");
+ }
+ checkMissingColumns(missingColumns,
+ exception.getTable(),
+ exception.as(TableValidationException.MissingColumns.class)
+ .getColumns()
+ .stream()
+ .map(String::toUpperCase)
+ .toList());
+ }
+ }
+ }
+
+ private void checkMissingColumns(Map> missingColumns, String table, List columns) {
+ Set columnsToCheck = new HashSet<>(missingColumns.get(table));
+ Set checked = new HashSet<>(columnsToCheck.size());
+ if (columnsToCheck.isEmpty()) {
+ fail(String.format("Table %s is not expected to have missing columns", table));
+ }
+ for (String column : columns) {
+ if (columnsToCheck.contains(column)) {
+ columnsToCheck.remove(column);
+ checked.add(column);
+ } else {
+ if (checked.contains(column)) {
+ fail(String.format("Duplicate missing %s column entry for table %s", column, table));
+ } else {
+ fail(String.format("Missing column %s was not reported for table %s", column, table));
+ }
+ }
+ }
+ }
+
+}
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnMissingSchemaTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnMissingSchemaTest.java
new file mode 100644
index 00000000000..4731783649f
--- /dev/null
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnMissingSchemaTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.eclipse.persistence.testing.tests.jpa.persistence32;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import jakarta.persistence.SchemaManager;
+import jakarta.persistence.SchemaValidationException;
+import junit.framework.Test;
+import org.eclipse.persistence.tools.schemaframework.TableValidationException;
+
+/**
+ * Verify jakarta.persistence 3.2 API changes in {@link SchemaManager}.
+ * Test {@link SchemaManager#validate()} method on database with missing schema.
+ */
+public class SchemaManagerValidateOnMissingSchemaTest extends AbstractSchemaManager {
+
+ public static Test suite() {
+ return suite(
+ "SchemaManagerValidateOnMissingSchemaTest",
+ new SchemaManagerValidateOnMissingSchemaTest("testValidateOnMissingSchema")
+ );
+ }
+ public SchemaManagerValidateOnMissingSchemaTest() {
+ }
+
+ public SchemaManagerValidateOnMissingSchemaTest(String name) {
+ super(name);
+ }
+
+ // Test SchemaManager validate method on missing schema
+ public void testValidateOnMissingSchema() {
+ // Tables are always dropped in setUp() method
+ SchemaManager schemaManager = emf.getSchemaManager();
+ try {
+ // Test validation
+ schemaManager.validate();
+ fail("Schema validation shall throw an exception on missing schema");
+ } catch (SchemaValidationException sve) {
+ // Validation is expected to fail and return all tables as missing
+ Exception[] exceptions = sve.getFailures();
+ String[] missingTables = new String[] {
+ "PERSISTENCE32_TEAM",
+ "PERSISTENCE32_TRAINER",
+ "PERSISTENCE32_TYPE",
+ "PERSISTENCE32_POKEMON",
+ "PERSISTENCE32_POKEMON_TYPE",
+ "PERSISTENCE32_SYNTAX_ENTITY",
+ "PERSISTENCE32_SE_COLTABLE"
+ };
+ Set missingTablesSet = new HashSet<>(Arrays.asList(missingTables));
+ Set initialMissingTablesSet = Set.copyOf(missingTablesSet);
+ Set checked = new HashSet<>(initialMissingTablesSet.size());
+ for (TableValidationException exception : (TableValidationException[]) exceptions) {
+ if (!(exception instanceof TableValidationException.MissingTable)) {
+ fail("Exception is not an instance of TableValidationException.MissingTable");
+ }
+ if (exception.getType() != TableValidationException.Type.MISSING_TABLE) {
+ fail("Exception type is not MISSING_TABLE");
+ }
+ checkMissingTable(initialMissingTablesSet, missingTablesSet, checked, exception.getTable());
+ }
+ }
+ }
+
+}
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnModifiedColumnTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnModifiedColumnTest.java
new file mode 100644
index 00000000000..c93e756da4c
--- /dev/null
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnModifiedColumnTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.eclipse.persistence.testing.tests.jpa.persistence32;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import jakarta.persistence.SchemaManager;
+import jakarta.persistence.SchemaValidationException;
+import junit.framework.Test;
+import org.eclipse.persistence.tools.schemaframework.FieldDefinition;
+import org.eclipse.persistence.tools.schemaframework.TableCreator;
+import org.eclipse.persistence.tools.schemaframework.TableDefinition;
+
+/**
+ * Verify jakarta.persistence 3.2 API changes in {@link SchemaManager}.
+ * Test {@link jakarta.persistence.SchemaManager#validate()} method on database with existing but modified schema.
+ */
+public class SchemaManagerValidateOnModifiedColumnTest extends AbstractSchemaManager {
+
+ public static Test suite() {
+ return suite(
+ "SchemaManagerValidateOnModifiedColumnTest",
+ new SchemaManagerValidateOnModifiedColumnTest("testValidateOnModifiedSchema")
+ );
+ }
+ public SchemaManagerValidateOnModifiedColumnTest() {
+ }
+
+ public SchemaManagerValidateOnModifiedColumnTest(String name) {
+ super(name);
+ }
+
+ // Test SchemaManager validate method on existing valid schema
+ public void testValidateOnModifiedSchema() {
+ // Make sure that tables exist
+ createTables();
+ // Modify current schema
+ TableCreator tableCreator = getDefaultTableCreator();
+ Map tableDefinitions = new HashMap<>(tableCreator.getTableDefinitions().size());
+ for (TableDefinition tableDefinition : tableCreator.getTableDefinitions()) {
+ String tableName = tableDefinition.getTable() == null
+ ? tableDefinition.getName()
+ : tableDefinition.getTable().getName();
+ tableDefinitions.put(tableName, tableDefinition);
+ }
+ // Modify "NAME" field in "PERSISTENCE32_TRAINER"
+ TableDefinition trainer = tableDefinitions.get("PERSISTENCE32_TRAINER");
+ FieldDefinition nameField = trainer.getField("NAME");
+ nameField.setSize(nameField.getSize()+5);
+ nameField.setShouldAllowNull(true);
+ trainer.dropFieldOnDatabase(emf.getDatabaseSession(), "NAME");
+ FieldDefinition newNameField = new FieldDefinition();
+ newNameField.setName("NAME");
+ newNameField.setTypeName("NUMERIC");
+ newNameField.setSize(15);
+ newNameField.setShouldAllowNull(true);
+ newNameField.setIsPrimaryKey(false);
+ newNameField.setUnique(false);
+ newNameField.setIsIdentity(false);
+ trainer.addFieldOnDatabase(emf.getDatabaseSession(), newNameField);
+ // Do the validation
+ SchemaManager schemaManager = emf.getSchemaManager();
+ try {
+ // Test validation
+ schemaManager.validate();
+ } catch (SchemaValidationException sve) {
+ //fail(sve.getLocalizedMessage());
+ }
+ }
+
+}
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnSurplusColumnTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnSurplusColumnTest.java
new file mode 100644
index 00000000000..f26365fdda1
--- /dev/null
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnSurplusColumnTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.eclipse.persistence.testing.tests.jpa.persistence32;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import jakarta.persistence.SchemaManager;
+import jakarta.persistence.SchemaValidationException;
+import junit.framework.Test;
+import org.eclipse.persistence.tools.schemaframework.FieldDefinition;
+import org.eclipse.persistence.tools.schemaframework.TableCreator;
+import org.eclipse.persistence.tools.schemaframework.TableDefinition;
+import org.eclipse.persistence.tools.schemaframework.TableValidationException;
+
+/**
+ * Verify jakarta.persistence 3.2 API changes in {@link SchemaManager}.
+ * Test {@link jakarta.persistence.SchemaManager#validate()} method on database with existing but modified schema.
+ */
+public class SchemaManagerValidateOnSurplusColumnTest extends AbstractSchemaManager {
+
+ public static Test suite() {
+ return suite(
+ "SchemaManagerValidateOnSurplusColumnTest",
+ new SchemaManagerValidateOnSurplusColumnTest("testValidateOnModifiedSchema")
+ );
+ }
+ public SchemaManagerValidateOnSurplusColumnTest() {
+ }
+
+ public SchemaManagerValidateOnSurplusColumnTest(String name) {
+ super(name);
+ }
+
+ // Test SchemaManager validate method on existing valid schema
+ public void testValidateOnModifiedSchema() {
+ // Make sure that tables exist
+ createTables();
+ // Modify current schema
+ TableCreator tableCreator = getDefaultTableCreator();
+ Map tableDefinitions = new HashMap<>(tableCreator.getTableDefinitions().size());
+ for (TableDefinition tableDefinition : tableCreator.getTableDefinitions()) {
+ String tableName = tableDefinition.getTable() == null
+ ? tableDefinition.getName()
+ : tableDefinition.getTable().getName();
+ tableDefinitions.put(tableName, tableDefinition);
+ }
+ // Extend PERSISTENCE32_TRAINER with age field
+ TableDefinition trainer = tableDefinitions.get("PERSISTENCE32_TRAINER");
+ FieldDefinition ageField = new FieldDefinition();
+ ageField.setName("age");
+ ageField.setTypeName("NUMERIC");
+ ageField.setSize(15);
+ ageField.setShouldAllowNull(true);
+ ageField.setIsPrimaryKey(false);
+ ageField.setUnique(false);
+ ageField.setIsIdentity(false);
+ trainer.addFieldOnDatabase(emf.getDatabaseSession(), ageField);
+ // Do the validation
+ SchemaManager schemaManager = emf.getSchemaManager();
+ try {
+ // Test validation
+ schemaManager.validate();
+ } catch (SchemaValidationException sve) {
+ // Validation is expected to fail and return all missing columns
+ Exception[] exceptions = sve.getFailures();
+ Map> surplusColumns = Map.of("PERSISTENCE32_TRAINER", Set.of("AGE"));
+ for (TableValidationException exception : (TableValidationException[]) exceptions) {
+ if (!(exception instanceof TableValidationException.SurplusColumns)) {
+ fail("Exception is not an instance of TableValidationException.SurplusColumns");
+ }
+ if (exception.getType() == TableValidationException.Type.SURPLUS_COLUMNS) {
+ assertEquals("PERSISTENCE32_TRAINER", exception.getTable());
+ } else {
+ fail("Exception type is not SURPLUS_COLUMNS");
+ }
+ checkSurplusColumns(surplusColumns,
+ exception.getTable(),
+ exception.as(TableValidationException.SurplusColumns.class)
+ .getColumns()
+ .stream()
+ .map(String::toUpperCase)
+ .toList());
+ }
+ }
+ }
+
+ private void checkSurplusColumns(Map> surplusColumns, String table, List columns) {
+ Set columnsToCheck = new HashSet<>(surplusColumns.get(table));
+ Set checked = new HashSet<>(columnsToCheck.size());
+ if (columnsToCheck.isEmpty()) {
+ fail(String.format("Table %s is not expected to have missing columns", table));
+ }
+ for (String column : columns) {
+ if (columnsToCheck.contains(column)) {
+ columnsToCheck.remove(column);
+ checked.add(column);
+ } else {
+ if (checked.contains(column)) {
+ fail(String.format("Duplicate missing %s column entry for table %s", column, table));
+ } else {
+ fail(String.format("Missing column %s was not reported for table %s", column, table));
+ }
+ }
+ }
+ }
+
+}
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnValidSchemaTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnValidSchemaTest.java
new file mode 100644
index 00000000000..f5573e7396b
--- /dev/null
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/SchemaManagerValidateOnValidSchemaTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.eclipse.persistence.testing.tests.jpa.persistence32;
+
+import jakarta.persistence.SchemaManager;
+import jakarta.persistence.SchemaValidationException;
+import junit.framework.Test;
+
+/**
+ * Verify jakarta.persistence 3.2 API changes in {@link SchemaManager}.
+ * Test {@link SchemaManager#validate()} method on database with existing and valid schema.
+ */
+public class SchemaManagerValidateOnValidSchemaTest extends AbstractSchemaManager {
+
+ public static Test suite() {
+ return suite(
+ "SchemaManagerValidateOnValidSchemaTest",
+ new SchemaManagerValidateOnValidSchemaTest("testValidateOnValidSchema")
+ );
+ }
+ public SchemaManagerValidateOnValidSchemaTest() {
+ }
+
+ public SchemaManagerValidateOnValidSchemaTest(String name) {
+ super(name);
+ }
+
+ // Test SchemaManager validate method on existing valid schema
+ public void testValidateOnValidSchema() {
+ // Make sure that tables exist
+ createTables();
+ SchemaManager schemaManager = emf.getSchemaManager();
+ try {
+ // Test validation
+ schemaManager.validate();
+ } catch (SchemaValidationException sve) {
+ fail(sve.getLocalizedMessage());
+ }
+ }
+
+}
diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/UnionCriteriaQueryTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/UnionCriteriaQueryTest.java
index 96bd487c21c..6739fb2bd35 100644
--- a/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/UnionCriteriaQueryTest.java
+++ b/jpa/eclipselink.jpa.testapps/jpa.test.persistence32/src/test/java/org/eclipse/persistence/testing/tests/jpa/persistence32/UnionCriteriaQueryTest.java
@@ -30,6 +30,9 @@
import org.eclipse.persistence.testing.models.jpa.persistence32.Pokemon;
import org.eclipse.persistence.testing.models.jpa.persistence32.Trainer;
+/**
+ * Verify jakarta.persistence 3.2 API changes in {@link CriteriaBuilder} for union expressions.
+ */
public class UnionCriteriaQueryTest extends AbstractPokemon {
// Pokemons. Array index is ID value.
@@ -50,7 +53,6 @@ public class UnionCriteriaQueryTest extends AbstractPokemon {
public static Test suite() {
return suite(
"QueryTest",
- new UnionCriteriaQueryTest("testSetup"),
new UnionCriteriaQueryTest("testUnionWithNoSelection"),
new UnionCriteriaQueryTest("testUnionAllWithNoSelection"),
new UnionCriteriaQueryTest("testIntersectWithNoSelection"),
@@ -72,11 +74,10 @@ public UnionCriteriaQueryTest(String name) {
setPuName(getPersistenceUnitName());
}
- /**
- * The setup is done as a test, both to record its failure, and to allow
- * execution in the server.
- */
- public void testSetup() {
+ // Initialize data
+ @Override
+ protected void suiteSetUp() {
+ super.suiteSetUp();
emf.runInTransaction(em -> {
for (int i = 1; i < POKEMONS.length; i++) {
em.persist(POKEMONS[i]);
@@ -84,22 +85,6 @@ public void testSetup() {
});
}
- private static void verifyValuesOnce(Set expected, List queryResult) {
- Set check = new HashSet<>(expected);
- for (Pokemon pokemon : queryResult) {
- assertTrue(String.format("Pokemon %d:%s was not found in Set %s", pokemon.getId(), pokemon.getName(), expected),
- check.contains(pokemon));
- check.remove(pokemon);
- }
- }
-
- private static void verifyValuesMultiple(Set expected, List queryResult) {
- for (Pokemon pokemon : queryResult) {
- assertTrue(String.format("Pokemon %d:%s was not found in Set %s", pokemon.getId(), pokemon.getName(), expected),
- expected.contains(pokemon));
- }
- }
-
// Test UNION: Shall return distinct values from both queries: 1x Pokemon[1..3]
// Pokemon[1] matches WHERE clause in both selects
public void testUnionWithNoSelection() {
@@ -479,4 +464,20 @@ public void testUnionWithMultiselectEntityInSelection() {
}
}
+ private static void verifyValuesOnce(Set expected, List queryResult) {
+ Set check = new HashSet<>(expected);
+ for (Pokemon pokemon : queryResult) {
+ assertTrue(String.format("Pokemon %d:%s was not found in Set %s", pokemon.getId(), pokemon.getName(), expected),
+ check.contains(pokemon));
+ check.remove(pokemon);
+ }
+ }
+
+ private static void verifyValuesMultiple(Set expected, List queryResult) {
+ for (Pokemon pokemon : queryResult) {
+ assertTrue(String.format("Pokemon %d:%s was not found in Set %s", pokemon.getId(), pokemon.getName(), expected),
+ expected.contains(pokemon));
+ }
+ }
+
}
diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EntityManagerFactoryDelegate.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EntityManagerFactoryDelegate.java
index ecf6e031a0f..1b1e986983f 100644
--- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EntityManagerFactoryDelegate.java
+++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EntityManagerFactoryDelegate.java
@@ -159,6 +159,9 @@ public class EntityManagerFactoryDelegate implements EntityManagerFactory, Persi
/** Pointer to the EntityManagerFactoryImpl that created me */
protected JpaEntityManagerFactory owner = null;
+ /** Persistence unit schema manager. */
+ private SchemaManagerImpl schemaManager = null;
+
/**
* Will return an instance of the Factory. Should only be called by
* EclipseLink.
@@ -564,10 +567,12 @@ public PersistenceUnitTransactionType getTransactionType() {
};
}
- // TODO-API-3.2
@Override
public SchemaManager getSchemaManager() {
- throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet");
+ if (schemaManager == null) {
+ schemaManager = new SchemaManagerImpl(getDatabaseSession());
+ }
+ return schemaManager;
}
/**
diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/SchemaManagerImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/SchemaManagerImpl.java
new file mode 100644
index 00000000000..906933ace17
--- /dev/null
+++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/SchemaManagerImpl.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 1998, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2023 IBM Corporation. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+// Contributors:
+// 11/28/2023: Tomas Kraus
+// - New Jakarta Persistence 3.2 Features
+package org.eclipse.persistence.internal.jpa;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+import jakarta.persistence.SchemaManager;
+import jakarta.persistence.SchemaValidationException;
+import org.eclipse.persistence.internal.localization.ExceptionLocalization;
+import org.eclipse.persistence.internal.sessions.DatabaseSessionImpl;
+import org.eclipse.persistence.tools.schemaframework.TableValidationException;
+
+public class SchemaManagerImpl implements SchemaManager {
+
+ private final org.eclipse.persistence.tools.schemaframework.SchemaManager schemaManager;
+
+ SchemaManagerImpl(DatabaseSessionImpl session) {
+ schemaManager = new org.eclipse.persistence.tools.schemaframework.SchemaManager(session);
+ }
+
+ @Override
+ public void create(boolean createSchemas) {
+ if (createSchemas) {
+ schemaManager.createDefaultTables(true);
+ }
+ }
+
+ @Override
+ public void drop(boolean dropSchemas) {
+ if (dropSchemas) {
+ schemaManager.dropDefaultTables();
+ }
+ }
+
+ // TODO-API-3.2
+ @Override
+ public void validate() throws SchemaValidationException {
+ ValidationFailure failures = new ValidationFailure();
+ if (!schemaManager.validateDefaultTables(failures, true)) {
+ throw new SchemaValidationException(
+ ExceptionLocalization.buildMessage("schema_validation_failed"),
+ failures.result().toArray(new TableValidationException[failures.result().size()]));
+ }
+ }
+
+ @Override
+ public void truncate() {
+ schemaManager.truncateDefaultTables(false);
+ }
+
+ private static final class ValidationFailure implements Consumer> {
+
+ private static List validationResult;
+
+ private ValidationFailure() {
+ validationResult = null;
+ }
+
+ @Override
+ public void accept(List failures) {
+ validationResult = failures;
+ }
+
+ private List result() {
+ return validationResult;
+ }
+
+ }
+
+}