/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/*
* ForeignFieldDesc.java
*
* Created on March 3, 2000
*
*/
package com.sun.jdo.spi.persistence.support.sqlstore.model;
import org.netbeans.modules.dbschema.ColumnElement;
import org.netbeans.modules.dbschema.TableElement;
import com.sun.jdo.api.persistence.model.jdo.RelationshipElement;
import com.sun.jdo.api.persistence.support.JDOFatalInternalException;
import com.sun.jdo.spi.persistence.support.sqlstore.SQLStateManager;
import org.glassfish.persistence.common.I18NHelper;
import com.sun.jdo.spi.persistence.utility.logging.Logger;
import java.lang.reflect.Field;
import java.util.ArrayList;
/**
*
*/
public class ForeignFieldDesc extends FieldDesc {
// Values for deleteAction
/**
* When the parent object is deleted from db,
* delete the relationship object described by this object also.
*/
public static final int ACT_CASCADE = RelationshipElement.CASCADE_ACTION;
/**
* When the parent object is deleted,
* no action is required for the relationship object described by this object.
*/
public static final int ACT_NONE = RelationshipElement.NONE_ACTION;
/** Currently runtime code does not interprete this action. */
public static final int ACT_NULLIFY = RelationshipElement.NULLIFY_ACTION;
/** Currently runtime code does not interprete this action. */
public static final int ACT_RESTRICT = RelationshipElement.RESTRICT_ACTION;
/** Currently runtime code does not interprete this action. */
public static final int ACT_AGGREGATE = RelationshipElement.AGGREGATE_ACTION;
/** Class descriptor for the class of this relationship field. */
public ClassDesc foreignConfig;
public int cardinalityLWB;
public int cardinalityUPB;
// Takes one of the following values.
public int deleteAction;
/** Array of LocalFieldDesc. */
public ArrayList foreignFields;
/** Array of ColumnElement. */
public ArrayList foreignColumns;
/** Array of LocalFieldDesc. */
public ArrayList localFields;
/** Array of ColumnElement. */
public ArrayList localColumns;
/** Array of LocalFieldDesc. */
public ArrayList assocForeignFields;
/** Array of ColumnElement. */
public ArrayList assocForeignColumns;
/** Array of LocalFieldDesc. */
public ArrayList assocLocalFields;
/** Array of ColumnElement. */
public ArrayList assocLocalColumns;
/**
* If inverseRelationshipField is not null, it means this field is
* under managed relationship. Otherwise, this is a one-way relationship.
*/
private ForeignFieldDesc inverseRelationshipField;
/**
* True, if the relationship is mapped to primary key fields
* <b>on the other relationship side</b>.
*/
private boolean isMappedToPk;
ForeignFieldDesc(ClassDesc config) {
super(config);
}
/**
* Returns true.
*/
public boolean isRelationshipField() {
return true;
}
/**
* Returns true, if the relationship is mapped to a join table.
*
* @return True, if the relationship is mapped to a join table, false otherwise.
*/
public boolean useJoinTable() {
return (assocLocalColumns != null && assocLocalColumns.size() > 0);
}
/**
* Checks the conditions that guarantee, that we have the foreign
* key on this side.
*
* @return True, if this relationship is mapped to foreign keys and the
* foreign key is on the local side. False otherwise.
*/
public boolean hasForeignKey() {
boolean result = false;
// RESOLVE: Can't check PROP_REF_INTEGRITY_UPDATES for 1 way mappings.
// See #checkReferentialIntegrityUpdatesForObjectField.
if (inverseRelationshipField != null) {
result = cardinalityUPB == 1 && !useJoinTable() &&
(sqlProperties & FieldDesc.PROP_REF_INTEGRITY_UPDATES) > 0;
}
return result;
}
/**
* Returns true, if the relationship is mapped to primary key fields
* <b>on the other relationship side</b>.
*
* @return True, if the relationship is mapped to primary key fields
* <b>on the other relationship side</b>, false otherwise.
* @see #initializeIsMappedToPk
*/
public boolean isMappedToPk() {
return isMappedToPk;
}
public ArrayList getLocalFields() {
if (localFields == null) {
localFields = new ArrayList();
}
return localFields;
}
public ArrayList getForeignFields() {
if (foreignFields == null) {
foreignFields = new ArrayList();
}
return foreignFields;
}
public ArrayList getAssocLocalFields() {
// Only create assocLocalFields if there is a corresponding
// assocLocalColumns to save space.
if (assocLocalFields == null && assocLocalColumns != null) {
assocLocalFields = new ArrayList();
}
return assocLocalFields;
}
public ArrayList getAssocForeignFields() {
// Only create assocForeignFields if there is a corresponding
// assocForeignColumns to save space.
if (assocForeignFields == null && assocForeignColumns != null) {
assocForeignFields = new ArrayList();
}
return assocForeignFields;
}
public ForeignFieldDesc getInverseRelationshipField() {
return inverseRelationshipField;
}
/**
* Constructs the oid of a related instance. If called by {@link
* SQLStateManager#updateTrackedFields}, the new value for the
* local field <code>fieldDesc</code> is not yet set and passed as
* parameter <code>value</code>. If called for navigation by
* {@link SQLStateManager#populateForeignField}, values of updated
* local fields must be retrieved from the before image. In both
* cases, the actual call to this method is in {@link
* SQLStateManager#getObjectById}.<p>
* For tracked field usage, see
* {@link SQLStateManager#setForeignKey} and
* {@link SQLStateManager#updateTrackedFields}.
* For navigation usage, see
* {@link SQLStateManager#realizeForeignField}.
*
* @param sm State manager on the local side.
* @param fieldDesc Local field being set.
* @param value New value of the field being set.
* @return The object id for the related instance. This id is used
* to lookup the instance from the persistence manager cache. The
* construced oid is invalid, if one of the oid fields is
* null. In this case the returned value is null.
*/
public Object createObjectId(SQLStateManager sm, LocalFieldDesc fieldDesc, Object value) {
assert isMappedToPk();
Class oidClass = foreignConfig.getOidClass();
Object oid = null;
try {
oid = oidClass.newInstance();
} catch (Exception e) {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.statemanager.cantnewoid", oidClass.getName()), e); // NOI18N
}
Field keyFields[] = foreignConfig.getKeyFields();
String keyFieldNames[] = foreignConfig.getKeyFieldNames();
for (int i = 0; i < keyFields.length && oid != null; i++) {
Field keyField = keyFields[i];
for (int j = 0; j < foreignFields.size() && oid != null; j++) {
LocalFieldDesc fa = (LocalFieldDesc) foreignFields.get(j);
if (fa.getName().compareTo(keyFieldNames[i]) == 0) {
LocalFieldDesc la = (LocalFieldDesc) localFields.get(j);
Object keyFieldValue = null;
if (la == fieldDesc) {
keyFieldValue = value;
} else if (sm.getSetMaskBit(la.absoluteID) && !sm.getSetMaskBit(absoluteID)) {
keyFieldValue = la.getValue(sm.getBeforeImage());
} else {
keyFieldValue = la.getValue(sm);
}
if (keyFieldValue != null) {
try {
// We need to convert the keyFieldValue to the proper type before
// setting it.
keyField.set(oid, fa.convertValue(keyFieldValue, sm));
} catch (IllegalAccessException e) {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.statemanager.cantsetkeyfield", keyField.getName()), e); // NOI18N
}
} else {
oid = null;
}
}
}
}
return oid;
}
//
// ------------ Initialisation methods ------------
//
// TODO: Should be removed, once computeTrackeRelationshipFields is removed.
private void setInverseRelationshipField(ForeignFieldDesc f) {
inverseRelationshipField = f;
}
void computeTrackedRelationshipFields() {
// If the field is a ForeignFieldDesc, we only need
// to compare against other ForeignFieldDesc. The reason
// is that ForeignFieldDesc implicitly tracks a LocalFieldDesc
// (foreign key field) via relationship updates.
ForeignFieldDesc inverseField = getInverseRelationshipField();
for (int k = 0; k < classDesc.foreignFields.size(); k++) {
ForeignFieldDesc tf = (ForeignFieldDesc) classDesc.foreignFields.get(k);
if ((this != tf) && (getType() == tf.getType()) && (compareColumns(this, tf) == true)) {
if ((inverseField != null) &&
(tf.getInverseRelationshipField() == null)) {
tf.setInverseRelationshipField(inverseField);
}
// Mark the relationship field tracking the foreign key as primary.
if ((sqlProperties & FieldDesc.PROP_SECONDARY_TRACKED_FIELD) == 0) {
sqlProperties |= FieldDesc.PROP_PRIMARY_TRACKED_FIELD;
}
if ((tf.sqlProperties & FieldDesc.PROP_PRIMARY_TRACKED_FIELD) == 0) {
tf.sqlProperties |= FieldDesc.PROP_SECONDARY_TRACKED_FIELD;
}
addTrackedField(tf);
}
}
}
/**
* Initializes relationship field information.
*
* @param foreignConfig Class descriptor of the foreign class.
* @param inverseField The inverse relationship field.
*/
void fixupForeignReference(ClassDesc foreignConfig,
ForeignFieldDesc inverseField) {
registerForeignConfig(foreignConfig, inverseField);
initializeFieldLists();
initializeIsMappedToPk();
addForeignKeyFieldsToDFG();
}
/**
* Registers the relationship information about the foreign class.
*
* @param foreignConfig Class descriptor of the foreign class.
* @param inverseField The inverse relationship field.
*/
private void registerForeignConfig(ClassDesc foreignConfig,
ForeignFieldDesc inverseField) {
boolean debug = logger.isLoggable(Logger.FINEST);
if (debug) {
Object[] items = new Object[] {classDesc, this, foreignConfig};
logger.finest("sqlstore.model.classdesc.general", items); // NOI18N
}
// Remember the class descriptor for the foreign class.
this.foreignConfig = foreignConfig;
if (debug && inverseField != null) {
logger.finest("sqlstore.model.classdesc.assocrelatedfield", inverseField); //NOI18N
}
setInverseRelationshipField(inverseField);
}
/**
* Initialize the field lists based on column list information.
*/
private void initializeFieldLists() {
ClassDesc theConfig = classDesc;
for (int i = 0; i < 4; i++) {
ArrayList fields = null;
ArrayList columns = null;
switch (i) {
case 0:
columns = localColumns;
fields = getLocalFields();
break;
case 1:
columns = assocLocalColumns;
fields = getAssocLocalFields();
break;
case 2:
columns = assocForeignColumns;
fields = getAssocForeignFields();
break;
case 3:
columns = foreignColumns;
fields = getForeignFields();
theConfig = foreignConfig;
break;
}
if (columns == null) continue;
for (int j = 0; j < columns.size(); j++) {
ColumnElement ce = (ColumnElement) columns.get(j);
TableElement te = ce.getDeclaringTable();
if (te == null) {
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
"core.configuration.columnnotable")); // NOI18N
}
fields.add(theConfig.getLocalFieldDesc(ce));
}
}
}
/**
* Checks, if the relationship is mapped to primary key fields
* <b>on the other relationship side</b> and sets the property
* <code>isMappedToPk</code>.
*/
private void initializeIsMappedToPk() {
int count = foreignFields.size();
isMappedToPk = !useJoinTable() &&
foreignConfig.getKeyFields().length == count;
for (int i = 0; i < count && isMappedToPk; i++) {
isMappedToPk = ((LocalFieldDesc) foreignFields.get(i)).isKeyField();
}
}
/**
* Silently adding hidden (local foreign key) fields to the DFG.
*/
private void addForeignKeyFieldsToDFG() {
for (int i = 0; i < localFields.size(); i++) {
LocalFieldDesc lf = (LocalFieldDesc) localFields.get(i);
if (lf.absoluteID < 0 && !useJoinTable()) {
classDesc.getFetchGroup(GROUP_DEFAULT).add(lf);
}
}
}
/**
* Determines the relationship side to be updated. Foreign key relationships
* must always be updated on the side having the foreign key. Jointable
* relationships can be handled from either side. To have unified
* dependency management for foreign key _and_ jointable relationships,
* it's essential that we apply the same rules defining the updated side
* for foreign key and jointable relationships. We also need to always
* update the same relationship side.
*/
void fixupFieldProperties() {
boolean refIntegrityUpdate = true;
if (cardinalityUPB > 1) {
// Collection side
if (!(refIntegrityUpdate = checkReferentialIntegrityUpdatesForCollectionField())) {
unsetReferentialIntegrityUpdateProperty();
// We also unset the IN_CONCURRENCY_CHECK property because we can't
// detect concurrency violation for changes made to a Collection
sqlProperties &= ~(FieldDesc.PROP_IN_CONCURRENCY_CHECK);
}
} else {
// Object side
if (!(refIntegrityUpdate = checkReferentialIntegrityUpdatesForObjectField())) {
unsetReferentialIntegrityUpdateProperty();
sqlProperties &= ~(FieldDesc.PROP_IN_CONCURRENCY_CHECK);
} else if (!useJoinTable()) {
// This side will write relationship updates to the database.
// Mark the local fields as part of the foreign key.
for (int i = 0; i < localFields.size(); i++) {
((LocalFieldDesc)localFields.get(i)).sqlProperties |= FieldDesc.PROP_FOREIGN_KEY_FIELD;
}
}
}
if (!refIntegrityUpdate) {
unsetConcurrencyCheckProperty();
}
}
/**
* Checks, if datastore updates will be scheduled locally or on
* the opposite relationship side.
*
* @return True, if datastore updates for this relationship will
* be scheduled locally, false otherwise.
*/
private boolean checkReferentialIntegrityUpdatesForCollectionField() {
boolean refIntegrityUpdate;
ForeignFieldDesc inverseFieldDesc = getInverseRelationshipField();
if (inverseFieldDesc == null) {
refIntegrityUpdate = defineUpdatedSideXToM();
} else {
if (inverseFieldDesc.cardinalityUPB <= 1) {
// For 1:N relationships, we always update the relationship side
// which includes jointables. We indicate this by unsetting the
// REF_INTEGRITY_UPDATES property which means that integrity updates
// cannot be done locally.
refIntegrityUpdate = false;
} else {
// For N:M relationships, we choose the updated relationship side
// depending on the alphabethical order of the related class names.
// N:M relationships must be mapped to a jointable. As jointable
// entries can be created from each side, we just define the side.
refIntegrityUpdate = defineUpdatedSideNToM(inverseFieldDesc);
}
}
return refIntegrityUpdate;
}
/**
* Checks, if datastore updates will be scheduled locally or on
* the opposite relationship side.
*
* @return True, if datastore updates for this relationship will
* be scheduled locally, false otherwise.
*/
private boolean checkReferentialIntegrityUpdatesForObjectField() {
boolean refIntegrityUpdate;
ForeignFieldDesc inverseFieldDesc = getInverseRelationshipField();
if (inverseFieldDesc == null) {
// Update the local side for one-way relationships.
refIntegrityUpdate = true;
} else {
if (inverseFieldDesc.cardinalityUPB > 1) {
// For 1:N relationships, we always update the local side
// which includes jointables. We indicate this by setting the
// REF_INTEGRITY_UPDATES property which means that integrity updates
// are done locally.
refIntegrityUpdate = true;
} else {
// For 1:1 relationships, we choose the updated relationship side
// depending on the side having the foreign key. If the relationship
// is mapped to primary key fields only, we consider if one side
// is marked for cascade delete. Otherwise, we choose the updatable
// side depending on the alphabethical order of the related class names.
refIntegrityUpdate = defineUpdatedSide1To1(inverseFieldDesc);
}
}
return refIntegrityUpdate;
}
/**
* Defines the updated side for a collection relationship
* mapped one-way.
*
* @return True, if datastore updates for this relationship will
* be scheduled locally, false otherwise.
*/
private boolean defineUpdatedSideXToM() {
boolean refIntegrityUpdate;
if (!useJoinTable()) {
// As this is a foreign key relationship, the other side must
// be the one side. Foreign key relationships can be updated
// from either side, even if the inverse side is unknown.
refIntegrityUpdate = false;
} else {
// Update the local side for one-way relationships mapped to jointables.
refIntegrityUpdate = true;
}
return refIntegrityUpdate;
}
/**
* Defines the updated side for many-to-many relationships. As jointable
* entries can be created from either side, we just define the side. The updated
* side is chosen based on the alphabethical order of the related class names.
*
* @param inverseFieldDesc Inverse relationship field.
* Is guaranteed to be not null!
* @return True, if datastore updates for this relationship will
* be scheduled locally, false otherwise.
*/
private boolean defineUpdatedSideNToM(ForeignFieldDesc inverseFieldDesc) {
boolean refIntegrityUpdate;
final boolean updateOtherSide = (inverseFieldDesc.sqlProperties & FieldDesc.PROP_REF_INTEGRITY_UPDATES) > 0;
if (!updateOtherSide) {
// The opposite side has already been identified not to be updated.
refIntegrityUpdate = true;
} else {
refIntegrityUpdate = chooseUpdatedSide(inverseFieldDesc);
}
return refIntegrityUpdate;
}
/**
* Defines the updated side for one-to-one relationships. The updated side
* is either defined by:
*
* <ol>
* <li>The relationship side mapped to non-key columns.</li>
* <li>The relationship side is identified as dependent side, or</li>
* <li><ul>
* <li>Mark both sides updatable for foreign key relationships.</li>
* <li>Choose a side for jointable relationships.</li>
* </ul></li>
* </ol>
*
* @param inverseFieldDesc Inverse relationship field.
* Is guaranteed to be not null!
* @return True, if datastore updates for this relationship will
* be scheduled locally, false otherwise.
*/
private boolean defineUpdatedSide1To1(ForeignFieldDesc inverseFieldDesc) {
boolean refIntegrityUpdate;
final boolean updateOtherSide = (inverseFieldDesc.sqlProperties & FieldDesc.PROP_REF_INTEGRITY_UPDATES) > 0;
if (!updateOtherSide) {
// The opposite side has already been identified not to be updated.
refIntegrityUpdate = true;
} else if (!useJoinTable()) {
// Check foreign key constraints and the dependent side.
refIntegrityUpdate = checkForeignKeysAndDependentSide(inverseFieldDesc);
} else {
// Just check the dependent side.
refIntegrityUpdate = checkDependentSide(inverseFieldDesc);
}
if (!refIntegrityUpdate && cardinalityLWB == 1) {
// Lower bound should not be 1 in this case.
// We silently set it to 0 for now.
// RESOLVE: Shall we throw an exception here?
cardinalityLWB = 0;
}
return refIntegrityUpdate;
}
/**
* Checks, if one relationship side isn't mapped to primary key fields
* (i.e. foreign key side). Based on the assumption, that jointable
* relationships are always mapped to the primary key columns,
* this method is called for foreign relationships only.
*
* @param inverseFieldDesc Inverse relationship field.
* Is guaranteed to be not null!
* @return True, if datastore updates for this relationship will
* be scheduled locally, false otherwise.
*/
private boolean checkForeignKeysAndDependentSide(ForeignFieldDesc inverseFieldDesc) {
boolean refIntegrityUpdate;
// Check the foreign keys.
if (checkForeignKey(getLocalFields())) {
// The foreign key is on the local side.
refIntegrityUpdate = true;
} else if (checkForeignKey(getForeignFields())) {
// The foreign key is on the other side.
refIntegrityUpdate = false;
} else {
// The relationship is mapped to primary key columns on either side.
refIntegrityUpdate = checkDependentSide(inverseFieldDesc);
}
return refIntegrityUpdate;
}
/**
* Marks the dependent side as identified by jdo meta-data for update.
* If no side is marked dependent, the following rules apply:
*
* <ul>
* <li>Foreign key relationships can be handled from both sides,
* because it must be an 1:1 relationship mapped to primary key
* fields.</li>
* <li>Jointable relationships need to be updated from exactly one
* side to implement unified dependency management. It's important to
* check the dependency on both relationship sides before the responsible
* side is chosen.</li>
* </ul>
*
* @param inverseFieldDesc Inverse relationship field.
* Is guaranteed to be not null!
* @return True, if datastore updates for this relationship will
* be scheduled locally, false otherwise.
* @see #isDependentOn
* @see SQLStateManager#manageDependencyForObjectField
*/
private boolean checkDependentSide(ForeignFieldDesc inverseFieldDesc) {
boolean refIntegrityUpdate;
// Check if meta data identifies the dependent relationship side.
if (this.isDependentOn(inverseFieldDesc)) {
// This side is marked dependent and will be updated.
refIntegrityUpdate = true;
} else if (inverseFieldDesc.isDependentOn(this)) {
// This side is marked as primary, the other side will be updated.
refIntegrityUpdate = false;
} else {
if (!useJoinTable()) {
// No information about the dependent side can be obtained and the
// relationship is mapped to the primary key fields on both sides.
// Relationship updates can be done from either side, but only during
// instance creation/deletion.
refIntegrityUpdate = true;
} else {
// No information about the dependent side can be obtained. If
// the Employee-Insurance relationship is mapped to a jointable,
// the dependent side can't be determinated. Identifying the
// updated side is essential to provide unified dependency
// management for the database updates, see
// SQLStateManager#manageDependencyForObjectField
refIntegrityUpdate = chooseUpdatedSide(inverseFieldDesc);
}
}
return refIntegrityUpdate;
}
/**
* Checks if at least one of the fields in <code>fieldList</code>
* is updatable. In this case <code>fieldList</code> makes up a
* foreign key. This is based on the assumption, that key fields
* referred in relationships must not be updated. Not updatable fields
* have property REF_INTEGRITY_UPDATES unset.
*
* @param fieldList Fields corresponding to datastore columns.
* The fields are either a <code>ForeignFieldDesc</code>'s local or foreign fields.
* @return True, if <code>fieldList</code> is a foreign key, false otherwise.
*/
private static boolean checkForeignKey(ArrayList fieldList) {
for (int i = 0; i < fieldList.size(); i++) {
FieldDesc lf = (FieldDesc) fieldList.get(i);
if ((lf.sqlProperties & FieldDesc.PROP_REF_INTEGRITY_UPDATES) > 0) {
// Based on the assumption, that referred key fields
// have property REF_INTEGRITY_UPDATES unset, at least
// one of the fields is not part of the key.
return true;
}
}
return false;
}
/**
* Checks, if <code>this</code> relationship side is dependent
* on the inverse side <code>inverseFieldDesc</code>. The
* dependent side can be identified by the following criteria:
*
* <ul>
* <li>This side has cardinalityLWB == 1.</li>
* <li>The inverse side is marked for cascade delete.</li>
* </ul>
*
* @param inverseFieldDesc Inverse relationship field.
* Is guaranteed to be not null!
* @return true, if <code>this</code> relationship side is dependent
* on <code>inverseFieldDesc</code>, false otherwise.
*/
private boolean isDependentOn(ForeignFieldDesc inverseFieldDesc) {
return (this.cardinalityLWB == 1 ||
inverseFieldDesc.deleteAction == ForeignFieldDesc.ACT_CASCADE);
}
/**
* Choose the updated relationship side based on the alphabethical
* order of the related class names. For self relationships, the
* field names itself are compared, too.
*
* @param inverseFieldDesc Inverse relationship field.
* Is guaranteed to be not null!
* @return This method is guaranteed to identify the relationship
* side that will not be updated.
*/
private boolean chooseUpdatedSide(ForeignFieldDesc inverseFieldDesc) {
int comparison = classDesc.getName().compareTo(foreignConfig.getName());
if (comparison == 0) {
comparison = getName().compareTo(inverseFieldDesc.getName());
}
return comparison < 0;
}
/**
* Unsets the REF_INTEGRITY_UPDATES property for this relationship field.
* Datastore updates will be scheduled on the opposite relationship side.
*/
private void unsetReferentialIntegrityUpdateProperty() {
if (logger.isLoggable(Logger.FINEST)) {
logger.finest("sqlstore.model.classdesc.unsetrefintegrityupdate", getName()); // NOI18N
}
sqlProperties &= ~(FieldDesc.PROP_REF_INTEGRITY_UPDATES);
}
/**
* Unsets the IN_CONCURRENCY_CHECK property for all the
* (hidden) local fields involved in this relationship.
*/
private void unsetConcurrencyCheckProperty() {
// Copy the field list temporarily.
ArrayList fieldList = (ArrayList) getLocalFields().clone();
if (useJoinTable()) {
fieldList.addAll(getAssocLocalFields());
}
for (int j = 0; j < fieldList.size(); j++) {
FieldDesc lf = (FieldDesc) fieldList.get(j);
if (lf.absoluteID < 0) {
if (logger.isLoggable(Logger.FINEST)) {
logger.finest("sqlstore.model.classdesc.unsetconcurrencychk", lf.getName()); // NOI18N
}
lf.sqlProperties &= ~(FieldDesc.PROP_IN_CONCURRENCY_CHECK);
}
}
}
}