| /* |
| * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 2010, 2015 IBM Corporation. |
| * Copyright (c) 2010, 2015 Dies Koper (Fujitsu). |
| * |
| * 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: |
| // Created Feb 19, 2010 - Dies Koper (Fujitsu) |
| // bug 288715: Drop Table Restrictions: "table locked" errors when dropping |
| // tables in several Core and many JPA LRG tests on Symfoware. |
| // Jul 19, 2014 - Tomas Kraus (Oracle) |
| // bug 437578: Added few helper methods to simplify table builder methods. |
| // Jan 06, 2015 - Dalia Abo Sheasha (IBM) |
| // bug 454917: Moved a few helper methods from child class to be visible to all table creators. |
| package org.eclipse.persistence.testing.framework; |
| |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.Vector; |
| |
| import org.eclipse.persistence.exceptions.DatabaseException; |
| import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform; |
| import org.eclipse.persistence.internal.helper.DatabaseField; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.logging.AbstractSessionLog; |
| import org.eclipse.persistence.logging.SessionLog; |
| import org.eclipse.persistence.sessions.DatabaseSession; |
| import org.eclipse.persistence.tools.schemaframework.FieldDefinition; |
| import org.eclipse.persistence.tools.schemaframework.ForeignKeyConstraint; |
| import org.eclipse.persistence.tools.schemaframework.SchemaManager; |
| import org.eclipse.persistence.tools.schemaframework.TableCreator; |
| import org.eclipse.persistence.tools.schemaframework.TableDefinition; |
| |
| /** |
| * Many JPA and a few Core tests use the same tables names, so at the start of |
| * the tests tables are dropped and recreated. If the tables were created in a |
| * previous test and the connection used to create it is still open, Symfoware |
| * complains that the tables are locked when it tries to drop them. |
| * <p> |
| * |
| * This class sets a flag before the subsequent recreation of the tables to |
| * delete the rows instead. The first time it is called it does not set the |
| * flag, to allow the tables to be initially created. |
| * <p> |
| * |
| * To enable this functionality, the system property |
| * "eclipselink.test.toggle-fast-table-creator" needs to be set to "true". |
| * <p> |
| * |
| * This class should be positioned between the test's table creator class and |
| * TableCreator by making the's test table creator extend this class. |
| * |
| * @author Dies Koper, Tomas Kraus |
| * |
| */ |
| public class TogglingFastTableCreator extends TableCreator { |
| |
| /** Character to separate table name and column name in full column identifier. */ |
| protected static final char TABLE_FIELD_SEPARATOR = '.'; |
| |
| protected static Set fastTableCreators = new HashSet(); |
| protected static boolean useFastTableCreatorAfterInitialCreate = Boolean |
| .getBoolean("eclipselink.test.toggle-fast-table-creator"); |
| |
| /** |
| * Delegates to super's constructor. |
| */ |
| public TogglingFastTableCreator() { |
| super(); |
| } |
| |
| /** |
| * Delegates to super's constructor. |
| */ |
| public TogglingFastTableCreator(Vector tableDefinitions) { |
| super(tableDefinitions); |
| } |
| |
| @Override |
| public void replaceTables(DatabaseSession session) { |
| // on Symfoware, to avoid table locking issues only the first invocation |
| // of an instance of this class (drops & re-)creates the tables |
| // if the system property is set. |
| session.getSessionLog().log(SessionLog.FINEST, "TogglingFastTableCreator: useFastTableCreatorAfterInitialCreate: " |
| + useFastTableCreatorAfterInitialCreate); |
| |
| boolean isFirstCreate = !isFastTableCreator(); |
| session.getSessionLog().log(SessionLog.FINEST, "TogglingFastTableCreator: " + getTableCreatorName() |
| + " - isFirstCreate: " + isFirstCreate); |
| session.getSessionLog().log(SessionLog.FINEST, "TogglingFastTableCreator: Current fastTableCreators: " |
| + fastTableCreators); |
| |
| if (useFastTableCreatorAfterInitialCreate && !isFirstCreate) { |
| session.getSessionLog().log(SessionLog.FINEST, "TogglingFastTableCreator: " + getTableCreatorName() |
| + " - toggling true"); |
| String sequenceTableName = getSequenceTableName(session); |
| List<TableDefinition> tables = getTableDefinitions(); |
| for (TableDefinition table : tables) { |
| if (!table.getName().equals(sequenceTableName)) { |
| SchemaManager schemaManager = new SchemaManager(session); |
| AbstractSession abstarctSession = schemaManager.getSession(); |
| try { |
| abstarctSession.priviledgedExecuteNonSelectingCall(new org.eclipse.persistence.queries.SQLCall("DELETE FROM " + table.getFullName())); |
| } catch (DatabaseException ex) { |
| //Ignore database exception. eg. If there is no table to delete, it gives database exception. |
| } |
| } |
| } |
| } else { |
| super.replaceTables(session); |
| } |
| |
| // next time just delete the rows instead. |
| if (useFastTableCreatorAfterInitialCreate) { |
| setFastTableCreator(); |
| session.getSessionLog().log(SessionLog.FINEST, "TogglingFastTableCreator: " + getTableCreatorName() |
| + " added to fastTableCreators"); |
| } |
| } |
| |
| public boolean resetFastTableCreator() { |
| AbstractSessionLog.getLog().log(SessionLog.FINEST, "TogglingFastTableCreator: removing table creator: " |
| + getTableCreatorName()); |
| return fastTableCreators.remove(getTableCreatorName()); |
| } |
| |
| public boolean setFastTableCreator() { |
| AbstractSessionLog.getLog().log(SessionLog.FINEST, "TogglingFastTableCreator: adding table creator: " |
| + getTableCreatorName()); |
| return fastTableCreators.add(getTableCreatorName()); |
| } |
| |
| public boolean isFastTableCreator() { |
| return fastTableCreators.contains(getTableCreatorName()); |
| } |
| |
| public String getTableCreatorName() { |
| return this.getClass().getName(); |
| } |
| |
| /** |
| * Helper method to concatenate table name and column name into full column name. |
| * @param tableName Table name. |
| * @param columnName Column name. |
| * @return Full column name (prefixed with table name). |
| */ |
| protected static String buildFullColumnName( |
| final String tableName, final String columnName) { |
| final StringBuilder sb = new StringBuilder(tableName.length() + columnName.length() + 1); |
| sb.append(tableName).append(TABLE_FIELD_SEPARATOR).append(columnName); |
| return sb.toString(); |
| |
| } |
| |
| /** |
| * Helper method to create {@link TableDefinition} instance with given table name. |
| * @param name Table name. |
| * @return {@link TableDefinition} instance with name set. |
| */ |
| protected static TableDefinition createTable(final String name) { |
| final TableDefinition table = new TableDefinition(); |
| table.setName(name); |
| return table; |
| } |
| |
| /** |
| * Helper method to create {@link FieldDefinition} instance for unique |
| * numeric primary key with given name and size. |
| * @param name Column name. |
| * @param size Column numeric type size. |
| * @return Initialized {@link FieldDefinition} instance. |
| */ |
| protected static FieldDefinition createNumericPk( |
| final String name, final int size) { |
| final FieldDefinition field = new FieldDefinition(); |
| field.setName(name); |
| field.setTypeName("NUMERIC"); |
| field.setSize(size); |
| field.setShouldAllowNull(false); |
| field.setIsPrimaryKey(true); |
| field.setUnique(true); |
| field.setIsIdentity(true); |
| return field; |
| } |
| |
| /** |
| * Helper method to create {@link FieldDefinition} instance for unique |
| * numeric primary key with given name and default size of {@code 15}. |
| * @param name Column name. |
| * @return Initialized {@link FieldDefinition} instance. |
| */ |
| protected static FieldDefinition createNumericPk(final String name) { |
| return createNumericPk(name, 15); |
| } |
| |
| /** |
| * Helper method to create {@link FieldDefinition} instance for |
| * numeric foreign key with given name, size and foreign key name. |
| * @param name Column name. |
| * @param size Column numeric type size. |
| * @param fkName Foreign key name (e.g. {@code "MY_TABLE.ID"}. |
| * @return Initialized {@link FieldDefinition} instance. |
| */ |
| protected static FieldDefinition createNumericFk( |
| final String name, final int size, final String fkName) { |
| final FieldDefinition field = new FieldDefinition(); |
| field.setName(name); |
| field.setTypeName("NUMERIC"); |
| field.setSize(size); |
| field.setShouldAllowNull(false); |
| field.setIsPrimaryKey(false); |
| field.setUnique(false); |
| field.setIsIdentity(false); |
| field.setForeignKeyFieldName(fkName); |
| return field; |
| } |
| |
| /** |
| * Helper method to create {@link FieldDefinition} instance for |
| * numeric foreign key with given name, foreign key name and default size |
| * of {@code 15}. |
| * @param name Column name. |
| * @return Initialized {@link FieldDefinition} instance. |
| */ |
| protected static FieldDefinition createNumericFk( |
| final String name, final String fkName) { |
| return createNumericFk(name, 15, fkName); |
| } |
| |
| /** |
| * Helper method to create {@link FieldDefinition} instance for numeric column |
| * with given name and size and without any additional constraints. |
| * @param name Column name. |
| * @param size Column numeric type size. |
| * @param allowNull Allow {@code null} values for column. |
| * @return Initialized {@link FieldDefinition} instance. |
| */ |
| protected static FieldDefinition createNumericColumn( |
| final String name, final int size, final boolean allowNull) { |
| final FieldDefinition field = new FieldDefinition(); |
| field.setName(name); |
| field.setTypeName("NUMERIC"); |
| field.setSize(size); |
| field.setShouldAllowNull(allowNull); |
| field.setIsPrimaryKey(false); |
| field.setUnique(false); |
| field.setIsIdentity(false); |
| return field; |
| } |
| |
| /** |
| * Helper method to create {@link FieldDefinition} instance for numeric column |
| * with given name, size of {@code 15}, with {@code null} value allowed and |
| * without any additional constraints. |
| * @param name Column name. |
| * @return Initialized {@link FieldDefinition} instance. |
| */ |
| protected static FieldDefinition createNumericColumn( |
| final String name) { |
| return createNumericColumn(name, 15, true); |
| } |
| |
| /** |
| * Helper method to create {@link FieldDefinition} instance for <code>DTYPE</code> |
| * column used for inheritance in model. |
| * @return Initialized {@link FieldDefinition} instance. |
| */ |
| protected static FieldDefinition createDTypeColumn() { |
| final FieldDefinition field = new FieldDefinition(); |
| field.setName("DTYPE"); |
| field.setTypeName("VARCHAR2"); |
| field.setSize(15); |
| field.setSubSize(0); |
| field.setIsPrimaryKey(false); |
| field.setIsIdentity(false); |
| field.setUnique(false); |
| field.setShouldAllowNull(true); |
| return field; |
| } |
| |
| /** |
| * Helper method to create {@link FieldDefinition} instance for {@link String} column |
| * with given name and size and without any additional constraints. |
| * @param name Column name. |
| * @param size Column numeric type size. |
| * @param allowNull Allow {@code null} values for column. |
| * @return Initialized {@link FieldDefinition} instance. |
| */ |
| protected static FieldDefinition createStringColumn( |
| final String name, final int size, final boolean allowNull) { |
| final FieldDefinition field = new FieldDefinition(); |
| field.setName(name); |
| field.setTypeName("VARCHAR"); |
| field.setSize(size); |
| field.setShouldAllowNull(allowNull); |
| field.setIsPrimaryKey(false); |
| field.setUnique(false); |
| field.setIsIdentity(false); |
| return field; |
| } |
| |
| /** |
| * Helper method to create {@link FieldDefinition} instance for {@link String} column |
| * with given name size of {@code 32}, with {@code null} value allowed and |
| * without any additional constraints. |
| * @param name Column name. |
| * @return Initialized {@link FieldDefinition} instance. |
| */ |
| protected static FieldDefinition createStringColumn( |
| final String name) { |
| return createStringColumn(name, 32, true); |
| } |
| |
| /** |
| * Helper method to create {@link FieldDefinition} instance |
| * for {@link java.util.Date} column with given name and size and without |
| * any additional constraints. |
| * @param name Column name. |
| * @param size Column date size. |
| * @param allowNull Allow {@code null} values for column. |
| * @return Initialized {@link FieldDefinition} instance. |
| */ |
| protected static FieldDefinition createDateColumn( |
| final String name, final int size, final boolean allowNull) { |
| final FieldDefinition field = new FieldDefinition(); |
| field.setName(name); |
| field.setTypeName("DATE"); |
| field.setSize(size); |
| field.setShouldAllowNull(allowNull); |
| field.setIsPrimaryKey(false); |
| field.setUnique(false); |
| field.setIsIdentity(false); |
| return field; |
| } |
| |
| /** |
| * Helper method to create {@link FieldDefinition} instance |
| * for {@link java.util.Date} column with given name size of {@code 23}, |
| * with {@code null} value allowed and without any additional constraints. |
| * @param name Column name. |
| * @return Initialized {@link FieldDefinition} instance. |
| */ |
| protected static FieldDefinition createDateColumn( |
| final String name) { |
| return createDateColumn(name, 23, true); |
| } |
| |
| protected void adjustForeignKeyFieldTypes(DatabaseSession session) { |
| for (TableDefinition sourceTableDefinition : getTableDefinitions() ) { |
| for (FieldDefinition sourceFieldDefinition : sourceTableDefinition.getFields()) { // see TableDefinition, l.789 |
| if (sourceFieldDefinition.getForeignKeyFieldName() != null) { |
| // We need to build each foreign key constraint on the fly, because TableDefinition.buildFieldTypes() has not been called yet |
| ForeignKeyConstraint foreignKeyConstraint = buildForeignKeyConstraint(sourceFieldDefinition, session.getPlatform()); |
| // Assume only one of each (as in Field Defintion) |
| String sourceFieldName = foreignKeyConstraint.getSourceFields().get(0); |
| String targetFieldName = foreignKeyConstraint.getTargetFields().get(0); |
| |
| // Find the target table and the target field |
| TableDefinition targetTableDefinition = getTableDefinition(foreignKeyConstraint.getTargetTable()); |
| // session.getSessionLog().log(SessionLog.FINEST, "AdvancedTableCreator: Found target table " + foreignKeyConstraint.getTargetTable() + " for foreign key " + foreignKeyConstraint.getName()); |
| FieldDefinition targetFieldDefinition = getFieldDefinition(targetTableDefinition, targetFieldName); |
| // session.getSessionLog().log(SessionLog.FINEST, "AdvancedTableCreator: Found target field " + targetFieldDefinition.getName() + " in table " + targetTableDefinition.getName()); |
| // Only change source column if target is identity |
| String qualifiedName = targetTableDefinition.getFullName() + '.' + targetFieldDefinition.getName(); |
| if (targetFieldDefinition.isIdentity() && session.getPlatform().shouldPrintFieldIdentityClause((AbstractSession)session, qualifiedName)) { |
| session.getSessionLog().log(SessionLog.FINEST, "AdvancedTableCreator.adjustForeignKeyFieldTypes(): Changing data type of source field " + sourceFieldDefinition.getName() + "to INTEGER in table " + sourceTableDefinition.getName()); |
| sourceFieldDefinition.setTypeName("INTEGER"); |
| sourceFieldDefinition.setSize(0); |
| } |
| } |
| } |
| } |
| } |
| |
| // Helper methods for adjustForeignKeyFieldTypes() |
| |
| private FieldDefinition getFieldDefinition(TableDefinition tableDefinition, String fieldName) { |
| for (FieldDefinition targetField : tableDefinition.getFields()) { // see TableDefinition, l.351 |
| if (targetField.getName().equals(fieldName)) { |
| return targetField; |
| } |
| } |
| return null; |
| } |
| |
| private TableDefinition getTableDefinition(String tableName) { |
| for (TableDefinition targetTable : getTableDefinitions()) { // see TableCreator, l.87 |
| if (targetTable.getName().equals(tableName)) { |
| return targetTable; |
| } |
| } |
| return null; |
| } |
| |
| // Mostly cloned from TableDefinition.buildForeignKeyConstraint() |
| private ForeignKeyConstraint buildForeignKeyConstraint(FieldDefinition field, DatabasePlatform platform) { |
| Vector sourceFields = new Vector(); |
| Vector targetFields = new Vector(); |
| ForeignKeyConstraint fkConstraint = new ForeignKeyConstraint(); |
| DatabaseField tempTargetField = new DatabaseField(field.getForeignKeyFieldName()); |
| DatabaseField tempSourceField = new DatabaseField(field.getName()); |
| |
| sourceFields.add(tempSourceField.getName()); |
| targetFields.add(tempTargetField.getName()); |
| |
| fkConstraint.setSourceFields(sourceFields); |
| fkConstraint.setTargetFields(targetFields); |
| fkConstraint.setTargetTable(tempTargetField.getTable().getQualifiedNameDelimited(platform)); |
| |
| return fkConstraint; |
| } |
| } |