org.eclipse.persistence.descriptors.SingleTableMultitenantPolicy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 2011, 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:
// 09/09/2011-2.3.1 Guy Pelletier
// - 356197: Add new VPD type to MultitenantType
// 11/10/2011-2.4 Guy Pelletier
// - 357474: Address primaryKey option from tenant discriminator column
// 14/05/2012-2.4 Guy Pelletier
// - 376603: Provide for table per tenant support for multitenant applications
package org.eclipse.persistence.descriptors;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.tools.schemaframework.TableDefinition;
/**
* A single table "striped" multitenant policy.
*
* @author Guy Pelletier
* @since EclipseLink 2.3.1
*/
public class SingleTableMultitenantPolicy implements MultitenantPolicy {
protected boolean includeTenantCriteria;
protected ClassDescriptor descriptor;
protected Map tenantDiscriminatorFields;
protected Map> tenantDiscriminatorFieldsKeyedOnContext;
public SingleTableMultitenantPolicy(ClassDescriptor desc) {
descriptor = desc;
includeTenantCriteria = true;
tenantDiscriminatorFields = new HashMap<>(5);
tenantDiscriminatorFieldsKeyedOnContext = new HashMap<>(5);
}
/**
* INTERNAL:
* Add the tenant discriminator fields to the row.
*/
@Override
public void addFieldsToRow(AbstractRecord row, AbstractSession session) {
for (DatabaseField discriminatorField : tenantDiscriminatorFields.keySet()) {
String property = tenantDiscriminatorFields.get(discriminatorField);
Object propertyValue = session.getProperty(property);
row.put(discriminatorField, propertyValue);
}
}
/**
* INTERNAL:
*/
@Override
public void addToTableDefinition(TableDefinition tableDefinition) {
// Does nothing at this level.
}
/**
* INTERNAL:
*/
@Override
public MultitenantPolicy clone(ClassDescriptor descriptor) {
SingleTableMultitenantPolicy clonedPolicy = new SingleTableMultitenantPolicy(descriptor);
clonedPolicy.includeTenantCriteria = includeTenantCriteria;
clonedPolicy.tenantDiscriminatorFields = tenantDiscriminatorFields;
return clonedPolicy;
}
/**
* INTERNAL:
*/
public ClassDescriptor getDescriptor() {
return descriptor;
}
/**
* INTERNAL:
* Add a tenant discriminator field to the policy.
*/
public void addTenantDiscriminatorField(String property, DatabaseField field) {
if (tenantDiscriminatorFields.containsKey(field)) {
String currentProperty = tenantDiscriminatorFields.get(field);
if (! currentProperty.equals(property)) {
// Adding a different property for the same field is not
// allowed. If it is the same we'll just ignore it.
throw ValidationException.multipleContextPropertiesForSameTenantDiscriminatorFieldSpecified(getDescriptor().getJavaClassName(), field.getQualifiedName(), currentProperty, property);
}
} else {
tenantDiscriminatorFields.put(field, property);
if (! tenantDiscriminatorFieldsKeyedOnContext.containsKey(property)) {
tenantDiscriminatorFieldsKeyedOnContext.put(property, new ArrayList<>());
}
tenantDiscriminatorFieldsKeyedOnContext.get(property).add(field);
}
}
/**
* INTERNAL:
*/
public Map getTenantDiscriminatorFields() {
return tenantDiscriminatorFields;
}
/**
* INTERNAL:
*/
public Map> getTenantDiscriminatorFieldsKeyedOnContext() {
return tenantDiscriminatorFieldsKeyedOnContext;
}
/**
* INTERNAL:
* Return if this descriptor has specified some tenant discriminator fields.
*/
public boolean hasTenantDiscriminatorFields() {
return ! tenantDiscriminatorFields.isEmpty();
}
/**
* INTERNAL:
* Initialize the mappings as a separate step.
* This is done as a separate step to ensure that inheritance has been first resolved.
*/
@Override
public void initialize(AbstractSession session) throws DescriptorException {
if (hasTenantDiscriminatorFields()) {
for (DatabaseField discriminatorField : tenantDiscriminatorFields.keySet()) {
DatabaseMapping mapping = getDescriptor().getObjectBuilder().getMappingForField(discriminatorField);
if (mapping != null && ! mapping.isReadOnly() && ! mapping.isMultitenantPrimaryKeyMapping()) {
throw ValidationException.nonReadOnlyMappedTenantDiscriminatorField(getDescriptor().getJavaClassName(), discriminatorField.getQualifiedName());
}
// Add the context property to the session set.
session.addMultitenantContextProperty(tenantDiscriminatorFields.get(discriminatorField));
}
}
}
/**
* INTERNAL:
*/
@Override
public boolean isSingleTableMultitenantPolicy() {
return true;
}
/**
* INTERNAL:
*/
@Override
public boolean isTablePerMultitenantPolicy() {
return false;
}
/**
* INTERNAL:
*/
@Override
public boolean isSchemaPerMultitenantPolicy() {
return false;
}
/**
* INTERNAL:
* Subclasses that need to add field to an expresison should override this method.
*/
@Override
public void postInitialize(AbstractSession session) {
if (includeTenantCriteria) {
Expression expression = getDescriptor().getQueryManager().getAdditionalJoinExpression();
ExpressionBuilder builder = (expression == null) ? new ExpressionBuilder() : expression.getBuilder();
for (DatabaseField discriminatorField : tenantDiscriminatorFields.keySet()) {
String property = tenantDiscriminatorFields.get(discriminatorField);
// Add the tenant discriminator field context property as the parameter.
// Do not initialize the database field with the property as it could be tenant.id
// and we do not want to de-qualify it.
DatabaseField newField = new DatabaseField();
newField.setName(property, session.getPlatform());
Expression tenantIdExpression = builder.and(builder.getField(discriminatorField).equal(builder.getProperty(newField)));
if (expression == null) {
expression = tenantIdExpression;
} else {
expression = expression.and(tenantIdExpression);
}
}
getDescriptor().getQueryManager().setAdditionalJoinExpression(expression);
}
}
/**
* INTERNAL:
* Allow the descriptor to initialize any dependencies on this session.
*/
@Override
public void preInitialize(AbstractSession session) throws DescriptorException {
for (DatabaseField discriminatorField : tenantDiscriminatorFields.keySet()) {
DatabaseField field = getDescriptor().buildField(discriminatorField);
field.setKeepInRow(true);
getDescriptor().getFields().add(field);
}
}
/**
* INTERNAL:
*/
public void setDescriptor(ClassDescriptor descriptor) {
this.descriptor = descriptor;
}
/**
* ADVANCED:
* Boolean used to indicate if the database requires the tenant criteria to
* be added to the SELECT, UPDATE, and DELETE queries. By default this is
* done but when set to false the queries will not be modified and it will
* be up to the application or database to ensure that the correct criteria
* is applied to all queries.
*
* @see org.eclipse.persistence.annotations.Multitenant
*/
public void setIncludeTenantCriteria(boolean includeTenantCriteria) {
this.includeTenantCriteria = includeTenantCriteria;
}
/**
* INTERNAL:
*/
public void setTenantDiscriminatorFields(Map tenantDiscriminatorFields) {
this.tenantDiscriminatorFields = tenantDiscriminatorFields;
}
}