| /* |
| * Copyright (c) 1998, 2021 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: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.testing.tests.returning; |
| |
| import java.util.*; |
| |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.sequencing.NativeSequence; |
| import org.eclipse.persistence.sessions.*; |
| import org.eclipse.persistence.descriptors.ReturningPolicy; |
| import org.eclipse.persistence.queries.DataModifyQuery; |
| import org.eclipse.persistence.tools.schemaframework.SequenceObjectDefinition; |
| import org.eclipse.persistence.testing.framework.*; |
| import org.eclipse.persistence.tools.schemaframework.SchemaManager; |
| |
| /** |
| * This adapter creates INSERT trigger for the original model's tables |
| * that return a generated sequence value, and modifies the descriptors |
| * to use a ReturningPolicy to assign this sequence value. |
| */ |
| public class SubstituteSequencingWithReturningPolicyAdapter implements ProjectAndDatabaseAdapter { |
| |
| public SubstituteSequencingWithReturningPolicyAdapter() { |
| this(true); |
| } |
| |
| /** |
| * If isReadOnly parameter is false, then the PK value will not be generated by the trigger if a non-null |
| * value is passed in insert, otherwise the passed value is ignored. |
| */ |
| public SubstituteSequencingWithReturningPolicyAdapter(boolean isReadOnly) { |
| this(isReadOnly, true); |
| } |
| |
| /** |
| * useExistingSequenceName parameter indicates whether to use the sequence name used by the project |
| * or just create sequence name using table name. |
| * For instance, for Employee class mapped to EMPLOYEE table and with sequenceNumberName==EMP_SEQ, |
| * useExistingSequenceName==true causes EMP_SEQ to be used |
| * as an Oracle sequence name, otherwise EMPLOYEE_SEQ name is used (tableName +"_SEQ"). |
| */ |
| public SubstituteSequencingWithReturningPolicyAdapter(boolean isReadOnly, boolean useExistingSequenceName) { |
| this.isReadOnly = isReadOnly; |
| tableToField = new Hashtable(); |
| if (useExistingSequenceName) { |
| tableToSequence = new Hashtable(); |
| } |
| } |
| |
| protected Hashtable tableToField; |
| protected Hashtable tableToSequence; |
| protected boolean isReadOnly; |
| |
| public boolean usesExistingSequenceNames() { |
| return tableToSequence != null; |
| } |
| |
| @Override |
| public boolean isOriginalSetupRequired() { |
| return true; |
| } |
| |
| @Override |
| public void updateProject(Project project, Session session) { |
| Iterator it = project.getDescriptors().values().iterator(); |
| while (it.hasNext()) { |
| ClassDescriptor desc = (ClassDescriptor)it.next(); |
| if (desc.isAggregateDescriptor()) { |
| continue; |
| } |
| String sequenceName = desc.getSequenceNumberName(); |
| String fieldName = desc.getSequenceNumberFieldName(); |
| if (fieldName != null && sequenceName != null) { |
| String tableName = desc.getTableName(); |
| int indexOfDot = fieldName.indexOf('.'); |
| if (indexOfDot != -1) { |
| fieldName = fieldName.substring(indexOfDot + 1); |
| } |
| tableToField.put(tableName, fieldName); |
| if (tableToSequence != null) { |
| tableToSequence.put(tableName, sequenceName); |
| } |
| // remove sequencing from descriptor |
| desc.setSequenceNumberFieldName(null); |
| desc.setSequenceNumberName(null); |
| // add ReturningPolicy |
| String fieldQualifiedName = tableName + '.' + fieldName; |
| desc.setReturningPolicy(new ReturningPolicy()); |
| if (isReadOnly) { |
| desc.getReturningPolicy().addFieldForInsertReturnOnly(fieldQualifiedName); |
| } else { |
| desc.getReturningPolicy().addFieldForInsert(fieldQualifiedName); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void updateDatabase(Session session) { |
| try { |
| createSequences(session); |
| replaceOrCreateTriggers(session); |
| } finally { |
| clear(); |
| } |
| } |
| |
| public void createSequences(Session session) { |
| if (!session.getPlatform().supportsSequenceObjects()) { |
| throw new TestWarningException("Requires database platform that supports sequence objects (like Oracle) - they will be used by triggers"); |
| } |
| SchemaManager schemaManager = new SchemaManager((DatabaseSession)session); |
| Hashtable sequenceNameToDefinition = new Hashtable(); |
| Enumeration tableNames = tableToField.keys(); |
| while (tableNames.hasMoreElements()) { |
| String tableName = (String)tableNames.nextElement(); |
| String sequenceName = getSequenceNameFromTableName(tableName); |
| if (!sequenceNameToDefinition.containsKey(sequenceName)) { |
| SequenceObjectDefinition definition = new SequenceObjectDefinition(new NativeSequence(sequenceName, 1, false)); |
| sequenceNameToDefinition.put(sequenceName, definition); |
| schemaManager.createObject(definition); |
| } |
| } |
| } |
| |
| public void replaceOrCreateTriggers(Session session) { |
| if (!session.getPlatform().isOracle()) { |
| throw new TestWarningException("Currently supports Oracle platform only"); |
| } |
| Enumeration tableNames = tableToField.keys(); |
| while (tableNames.hasMoreElements()) { |
| String tableName = (String)tableNames.nextElement(); |
| String fieldName = (String)tableToField.get(tableName); |
| String sequenceName = getSequenceNameFromTableName(tableName); |
| String triggerName = getTriggerNameFromTableName(tableName); |
| String strCommand = "CREATE OR REPLACE TRIGGER " + triggerName + " BEFORE INSERT ON " + tableName + " FOR EACH ROW "; |
| String strBegin = "BEGIN\n"; |
| String strIf = " IF (:new." + fieldName + " IS NULL) OR (:new." + fieldName + " = 0) THEN\n "; |
| String strSeq = " SELECT " + sequenceName + ".NEXTVAL INTO :new." + fieldName + " FROM DUAL;\n"; |
| String strEndIf = " END IF;\n"; |
| String strEnd = "END;"; |
| String str; |
| if (isReadOnly) { |
| str = strCommand + strBegin + strSeq + strEnd; |
| } else { |
| str = strCommand + strBegin + strIf + strSeq + strEndIf + strEnd; |
| } |
| execute(session, str, true); |
| } |
| } |
| |
| public Map getTableToField() { |
| return this.tableToField; |
| } |
| |
| public Map getTableToSequence() { |
| return this.tableToSequence; |
| } |
| |
| protected String getSequenceNameFromTableName(String tableName) { |
| String sequenceName; |
| if (tableToSequence != null) { |
| sequenceName = (String)tableToSequence.get(tableName); |
| } else { |
| sequenceName = tableName + "_SEQ"; |
| } |
| return sequenceName; |
| } |
| |
| protected String getTriggerNameFromTableName(String tableName) { |
| return tableName + "_TRIGGER"; |
| } |
| |
| protected void execute(Session session, String str, boolean shouldThrowException) { |
| try { |
| DataModifyQuery query = new DataModifyQuery(str); |
| query.setShouldBindAllParameters(false); |
| session.executeQuery(query); |
| } catch (Exception e) { |
| if (shouldThrowException) { |
| throw new TestErrorException("FAILED: " + str, e); |
| } |
| } |
| } |
| |
| protected void clear() { |
| tableToField.clear(); |
| if (tableToSequence != null) { |
| tableToSequence.clear(); |
| } |
| } |
| } |