/******************************************************************************* | |
* Copyright (c) 1998, 2013 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.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. | |
* <p> | |
* @author Stephen McRitchie | |
* @since Oracle 10g AS | |
*/ | |
public class HistoryFacade { | |
private static Map 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); | |
histDef.addField(fieldDef); | |
fieldDef = new FieldDefinition(); | |
fieldDef.setName(policy.getEndFieldName()); | |
fieldDef.setType(ClassConstants.TIMESTAMP); | |
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 = (Long)timeOffsetsMap.get(rootSession); | |
return System.currentTimeMillis() + offset.longValue(); | |
} 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, new Long(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<String>(); | |
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 = (String)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 = (String)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 descriptors, DatasourcePlatform platform) { | |
HistoryPolicy basePolicy = new HistoryPolicy(); | |
basePolicy.addStartFieldName("ROW_START"); | |
basePolicy.addEndFieldName("ROW_END"); | |
HistoryPolicy policy = null; | |
while (descriptors.hasNext()) { | |
ClassDescriptor descriptor = (ClassDescriptor)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 mappings = descriptor.getMappings().elements(); | |
mappings.hasMoreElements(); ) { | |
DatabaseMapping mapping = | |
(DatabaseMapping)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()); | |
} | |
} |