/* * Copyright (c) 2005-2016 Vincent Vandenschrick. All rights reserved. * * This file is part of the Jspresso framework. * * Jspresso is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Jspresso is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Jspresso. If not, see <http://www.gnu.org/licenses/>. */ package org.jspresso.framework.model.descriptor.basic; import org.jspresso.framework.model.descriptor.IRelationshipEndPropertyDescriptor; /** * This is the abstract base descriptor for all relationship properties. * relationship properties include : * <ul> * <li><i>reference</i> properties, i.e. "N to 1" or "1 to * 1" properties</li> * <li><i>collection</i> properties, i.e. "1 to N" or "N to * N" properties</li> * </ul> * Other type of properties are named <i>scalar</i> properties. * * @author Vincent Vandenschrick */ public abstract class BasicRelationshipEndPropertyDescriptor extends BasicPropertyDescriptor implements IRelationshipEndPropertyDescriptor { private Boolean composition; private String fkName; private boolean leadingPersistence = true; private IRelationshipEndPropertyDescriptor reverseRelationEnd; private IRelationshipEndPropertyDescriptor tempReverseRelationEnd; private EFetchType fetchType = EFetchType.SELECT; private Integer batchSize; /** * {@inheritDoc} */ @Override public BasicRelationshipEndPropertyDescriptor clone() { BasicRelationshipEndPropertyDescriptor clonedDescriptor = (BasicRelationshipEndPropertyDescriptor) super .clone(); return clonedDescriptor; } /** * {@inheritDoc} */ @Override public BasicRelationshipEndPropertyDescriptor createQueryDescriptor() { BasicRelationshipEndPropertyDescriptor queryDescriptor = (BasicRelationshipEndPropertyDescriptor) super .createQueryDescriptor(); queryDescriptor.reverseRelationEnd = null; return queryDescriptor; } /** * Gets the fkName. * * @return the fkName. */ public String getFkName() { return fkName; } /** * {@inheritDoc} */ @Override public IRelationshipEndPropertyDescriptor getReverseRelationEnd() { return reverseRelationEnd; } /** * {@inheritDoc} */ @Override public boolean isComposition() { if (composition != null) { return composition; } return getDefaultComposition(); } /** * Instructs the framework that this property has to be treated as a * <i>composition</i>, in the UML terminology. This implies that reachable * entities that are referenced by this property follow the owning entity * lifecycle. For instance, when the owning entity is deleted, the referenced * entities in composition properties are also deleted. * <p> * Whenever this property is not explicitly set by the developer, Jspresso * uses sensible defaults : * <ul> * <li><i>collection properties</i> are compositions <b>unless</b> they are * bidirectional "N to N"</li> * <li><i>reference properties</i> are not composition</li> * </ul> * <p> * This property is strictly behavioural and does not impact the domain state * itself. * * @param composition * the composition to set. */ public void setComposition(boolean composition) { this.composition = composition; } /** * Gives the developer the opportunity to customize the generated foreign key * (if any) name. * * @param fkName * the fkName to set. */ public void setFkName(String fkName) { this.fkName = fkName; } /** * Allows to make a relationship bi-directional. By default, when a * relationship end is defined, it is only navigable from the owning component * to the described end (default value is {@code null}). Assigning a * reverse relationship ends instructs the framework that the relationship is * bi-directional. This implies several complementary features : * <ul> * <li>When one of the relationship ends is updated, the other side is * automatically maintained by Jspresso, i.e. you never have to worry about * reverse state. For instance, considering a {@code Invoice} - * {@code InvoiceLine} bi-directional relationship, * {@code InvoiceLine.setInvoice(Invoice)} and * {@code Invoice.addToInvoiceLines(InvoiceLine)} are strictly * equivalent.</li> * <li>You can qualify a "N-N" relationship (thus creating an * association table in the data store behind the scene) by assigning 2 * collection property descriptors as reverse relation ends of each other.</li> * <li>You can qualify a "1-1" relationship (thus enforcing some * unicity constraint in the data store behind the scene) by assigning 2 * reference property descriptors as reverse relation ends of each other.</li> * </ul> * Setting the reverse relation end operation is commutative so that it * automatically assigns bot ends as reverse, i.e. you only have to set the * property on one side of the relationship. * * @param reverseRelationEnd * the reverseRelationEnd to set. */ @Override public void setReverseRelationEnd( IRelationshipEndPropertyDescriptor reverseRelationEnd) { if (getName() == null) { // store it until we have a name available. tempReverseRelationEnd = reverseRelationEnd; return; } // We only ant to actually update reverse relation end if it is an 'actual' // property descriptor, e.g. not a compound one used only for the view. if (this.reverseRelationEnd != reverseRelationEnd) { if (!getName().contains(".")) { if (this.reverseRelationEnd != null) { this.reverseRelationEnd.setReverseRelationEnd(null); } } if (reverseRelationEnd == null) { // it is set uni-directional. leadingPersistence = true; } else { leadingPersistence = reverseRelationEnd.getReverseRelationEnd() != this; } this.reverseRelationEnd = reverseRelationEnd; if (!getName().contains(".")) { if (this.reverseRelationEnd != null) { this.reverseRelationEnd.setReverseRelationEnd(this); } } } } /** * Assign reverse temp relation end if set. * <p> * {@inheritDoc} */ @Override public void setName(String name) { super.setName(name); if (tempReverseRelationEnd != null) { setReverseRelationEnd(tempReverseRelationEnd); tempReverseRelationEnd = null; } } /** * Gets the leadingPersistence. * * @return the leadingPersistence. */ public boolean isLeadingPersistence() { return leadingPersistence; } /** * Gets the default composition of a relationship end. * * @return the default composition of a relationship end. */ protected abstract boolean getDefaultComposition(); /** * Gets the fetchType. * * @return the fetchType. */ public EFetchType getFetchType() { return fetchType; } /** * This property allows to finely tune fetching strategy of the ORM on this * relationship end. This is either a value of the {@code EFetchType} * enum or its equivalent string representation : * <ul> * <li>{@code SELECT} for default 2nd select strategy (lazy)</li> * <li>{@code SUBSELECT} for default 2nd select strategy (lazy) using an * IN clause</li> * <li>{@code JOIN} for a join select strategy (not lazy)</li> * </ul> * <p> * Default value is {@code EFetchType.SELECT}, i.e. 2nd select strategy. * * @param fetchType * the fetchType to set. */ public void setFetchType(EFetchType fetchType) { this.fetchType = fetchType; } /** * Gets the batchSize. * * @return the batchSize. */ public Integer getBatchSize() { return batchSize; } /** * This property allows to finely tune batching strategy of the ORM on this * relationship end. Whenever possible, the ORM will use a IN clause in order * to fetch multiple instances relationships at once. The batch size * determines the size of th IN clause. * * @param batchSize * the batchSize to set. */ public void setBatchSize(Integer batchSize) { this.batchSize = batchSize; } }