| /* |
| * 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.tools.history; |
| |
| import java.util.*; |
| |
| import org.eclipse.persistence.history.*; |
| import org.eclipse.persistence.internal.databaseaccess.DatasourcePlatform; |
| import org.eclipse.persistence.internal.helper.*; |
| import org.eclipse.persistence.internal.history.*; |
| import org.eclipse.persistence.mappings.*; |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.sessions.*; |
| import org.eclipse.persistence.queries.*; |
| import org.eclipse.persistence.sessions.broker.*; |
| import org.eclipse.persistence.tools.schemaframework.*; |
| import org.eclipse.persistence.sessions.server.*; |
| |
| /** |
| * <b>Purpose:</b>One stop shopping for all your history needs. |
| * |
| * @author Stephen McRitchie |
| * @since Oracle 10g AS |
| */ |
| public class HistoryFacade { |
| |
| private static Map<Session, Long> timeOffsetsMap = new IdentityHashMap<>(); |
| |
| protected HistoryFacade() { |
| } |
| |
| protected static void buildHistoricalTableDefinition(HistoryPolicy policy, |
| String name, |
| TableDefinition def, |
| TableCreator creator) { |
| |
| if (def == null) { |
| return; |
| } |
| |
| TableDefinition histDef = (TableDefinition)def.clone(); |
| histDef.setName(name); |
| FieldDefinition fieldDef = new FieldDefinition(); |
| fieldDef.setName(policy.getStartFieldName()); |
| fieldDef.setType(ClassConstants.TIMESTAMP); |
| fieldDef.setSize(6); |
| histDef.addField(fieldDef); |
| fieldDef = new FieldDefinition(); |
| fieldDef.setName(policy.getEndFieldName()); |
| fieldDef.setType(ClassConstants.TIMESTAMP); |
| fieldDef.setSize(6); |
| histDef.addField(fieldDef); |
| histDef.setForeignKeys(new Vector<>()); |
| |
| for (FieldDefinition fieldDef2 : histDef.getFields()) { |
| // For now foreign key constraints are not supported, because shallow inserts are not... |
| fieldDef2.setForeignKeyFieldName(null); |
| if (fieldDef2.getName().equals("ROW_START") && |
| (!histDef.getPrimaryKeyFieldNames().isEmpty())) { |
| fieldDef2.setIsPrimaryKey(true); |
| } |
| if (fieldDef2.getName().equals("VERSION") && |
| (!histDef.getPrimaryKeyFieldNames().isEmpty())) { |
| fieldDef2.setIsPrimaryKey(true); |
| } |
| } |
| creator.addTableDefinition(histDef); |
| } |
| |
| public static long currentDatabaseTimeMillis(org.eclipse.persistence.sessions.Session session) { |
| return currentDatabaseTimeMillis(session, null); |
| } |
| |
| /** |
| * PUBLIC: |
| */ |
| public static long currentDatabaseTimeMillis(org.eclipse.persistence.sessions.Session session, |
| Class<?> domainClass) { |
| Session rootSession = session; |
| while (rootSession.isUnitOfWork() || rootSession.isClientSession() || |
| rootSession instanceof HistoricalSession || |
| rootSession.isSessionBroker()) { |
| if (rootSession.isUnitOfWork()) { |
| rootSession = ((UnitOfWork)rootSession).getParent(); |
| } else if (rootSession.isClientSession()) { |
| rootSession = ((ClientSession)rootSession).getParent(); |
| } else if (rootSession instanceof HistoricalSession) { |
| rootSession = ((HistoricalSession)rootSession).getParent(); |
| } else { |
| SessionBroker broker = (SessionBroker)rootSession; |
| rootSession = broker.getSessionForClass(domainClass); |
| if (rootSession == broker) { |
| break; |
| } |
| } |
| } |
| if (timeOffsetsMap.containsKey(rootSession)) { |
| Long offset = timeOffsetsMap.get(rootSession); |
| return System.currentTimeMillis() + offset; |
| } else { |
| DatabaseQuery query = |
| rootSession.getPlatform().getTimestampQuery(); |
| long startTime = System.currentTimeMillis(); |
| java.sql.Timestamp databaseTime = |
| (java.sql.Timestamp)rootSession.executeQuery(query); |
| long endTime = System.currentTimeMillis(); |
| long jvmTime = (endTime - startTime) / 2 + startTime; |
| long offset = databaseTime.getTime() - jvmTime; |
| timeOffsetsMap.put(rootSession, offset); |
| return jvmTime + offset; |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Generates a mirroring historical schema given a |
| * conventional schema and a session with HistoryPolicies set on |
| * the descriptors. |
| */ |
| public static void generateHistoricalTableDefinitions(TableCreator creator, Session session) { |
| |
| // First add all table definitions to a hashtable. |
| Map<String, TableDefinition> tableDefinitions = new HashMap<>(creator.getTableDefinitions().size()); |
| for (TableDefinition def : creator.getTableDefinitions()) { |
| tableDefinitions.put(def.getFullName(), def); |
| } |
| Set<String> generatedTables = new HashSet<>(); |
| for (ClassDescriptor descriptor : session.getDescriptors().values()) { |
| HistoryPolicy policy = descriptor.getHistoryPolicy(); |
| if (policy != null) { |
| List<String> names = policy.getHistoryTableNames(); |
| for (int i = 0; i < descriptor.getTableNames().size(); i++) { |
| String name = (String)descriptor.getTableNames().get(i); |
| String histName = names.get(i); |
| if (!generatedTables.contains(histName)) { |
| generatedTables.add(histName); |
| TableDefinition def = tableDefinitions.get(name); |
| buildHistoricalTableDefinition(policy, histName, def, creator); |
| } |
| } |
| } |
| for (DatabaseMapping mapping : descriptor.getMappings()) { |
| if (mapping.isManyToManyMapping()) { |
| ManyToManyMapping m2mMapping = (ManyToManyMapping)mapping; |
| policy = m2mMapping.getHistoryPolicy(); |
| if (policy != null) { |
| String name = m2mMapping.getRelationTableName(); |
| String histName = policy.getHistoryTableNames().get(0); |
| if (!generatedTables.contains(histName)) { |
| generatedTables.add(histName); |
| TableDefinition def = tableDefinitions.get(name); |
| buildHistoricalTableDefinition(policy, histName, def, creator); |
| } |
| } |
| } else if (mapping.isDirectCollectionMapping()) { |
| DirectCollectionMapping dcMapping = (DirectCollectionMapping)mapping; |
| policy = dcMapping.getHistoryPolicy(); |
| if (policy != null) { |
| String name = dcMapping.getReferenceTableName(); |
| String histName = policy.getHistoryTableNames().get(0); |
| if (!generatedTables.contains(histName)) { |
| generatedTables.add(histName); |
| TableDefinition def = tableDefinitions.get(name); |
| buildHistoricalTableDefinition(policy, histName, def, creator); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Generates HistoryPolicies for a given object model. The policies |
| * are of the recommended form, with START/END, and the history table name |
| * being the current table name with a _HIST suffix. Hence Employee would |
| * become Employee_Hist. |
| */ |
| public static void generateHistoryPolicies(Iterator<ClassDescriptor> descriptors, DatasourcePlatform platform) { |
| HistoryPolicy basePolicy = new HistoryPolicy(); |
| basePolicy.addStartFieldName("ROW_START"); |
| basePolicy.addEndFieldName("ROW_END"); |
| |
| HistoryPolicy policy = null; |
| |
| while (descriptors.hasNext()) { |
| ClassDescriptor descriptor = descriptors.next(); |
| policy = (HistoryPolicy)basePolicy.clone(); |
| List<DatabaseTable> tables = descriptor.getTables(); |
| int size = tables.size(); |
| if (size == 0) { |
| continue; |
| } |
| for (int i = 0; i < size; i++) { |
| DatabaseTable table = tables.get(i); |
| String name = table.getQualifiedNameDelimited(platform); |
| String historicalName; |
| if(table.shouldUseDelimiters()) { |
| historicalName = name.substring(0, name.length() - 1) + "_HIST" + Helper.getDefaultEndDatabaseDelimiter(); |
| } else { |
| historicalName = name + "_HIST"; |
| } |
| policy.addHistoryTableName(name, historicalName); |
| } |
| descriptor.setHistoryPolicy(policy); |
| |
| for (Enumeration<DatabaseMapping> mappings = descriptor.getMappings().elements(); |
| mappings.hasMoreElements(); ) { |
| DatabaseMapping mapping = |
| mappings.nextElement(); |
| if (mapping instanceof ManyToManyMapping) { |
| ManyToManyMapping m2mMapping = (ManyToManyMapping)mapping; |
| policy = (HistoryPolicy)basePolicy.clone(); |
| policy.addHistoryTableName(m2mMapping.getRelationTableName() + |
| "_HIST"); |
| m2mMapping.setHistoryPolicy(policy); |
| } else if (mapping instanceof DirectCollectionMapping) { |
| DirectCollectionMapping dcMapping = |
| (DirectCollectionMapping)mapping; |
| policy = (HistoryPolicy)basePolicy.clone(); |
| policy.addHistoryTableName(dcMapping.getReferenceTableName() + |
| "_HIST"); |
| dcMapping.setHistoryPolicy(policy); |
| } |
| } |
| } |
| } |
| |
| public static void generateHistoryPolicies(org.eclipse.persistence.sessions.Project project) { |
| generateHistoryPolicies(project.getDescriptors().values().iterator(), project.getDatasourceLogin().getPlatform()); |
| } |
| |
| public static void generateHistoryPolicies(org.eclipse.persistence.sessions.Session session) { |
| generateHistoryPolicies(session.getDescriptors().values().iterator(), session.getPlatform()); |
| } |
| } |