/******************************************************************************* * Copyright (c) 1998, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * 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; } public boolean isOriginalSetupRequired() { return true; } 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); } } } } 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(); } } }