Package org.datanucleus.store.mapped.query

Source Code of org.datanucleus.store.mapped.query.DiscriminatorIteratorStatement

/**********************************************************************
Copyright (c) 2005 Andy Jefferson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


Contributors:
    ...
**********************************************************************/
package org.datanucleus.store.mapped.query;

import java.util.Iterator;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.DiscriminatorMetaData;
import org.datanucleus.metadata.DiscriminatorStrategy;
import org.datanucleus.store.StoreManager;
import org.datanucleus.store.mapped.DatastoreContainerObject;
import org.datanucleus.store.mapped.DatastoreIdentifier;
import org.datanucleus.store.mapped.expression.BooleanExpression;
import org.datanucleus.store.mapped.expression.LogicSetExpression;
import org.datanucleus.store.mapped.expression.NullLiteral;
import org.datanucleus.store.mapped.expression.QueryExpression;
import org.datanucleus.store.mapped.expression.ScalarExpression;
import org.datanucleus.store.mapped.mapping.JavaTypeMapping;

/**
* Class to generate a QueryStatement that can be used for iterating through instances
* in a single table using a discriminator to distinguish between the classes.
* There are two modes of operation supported :-
*
* <H3>Generated statement - candidate type</H3>
* Let's assume that we have class A which is the candidate and this has subclasses A1, A2
* that are stored in the same table (with different values of the discriminator).
* We want to find all objects of the candidate type and optionally its subclasses
* and we want information about what type the object is (A or A1 or A2).
* The query will be of the form
* <PRE>
* SELECT [THIS.DISCRIMINATOR]
* FROM A THIS
* [WHERE (THIS.DISCRIMINATOR = value1 || THIS.DISCRIMINATOR = value2 || THIS_DISCRIMINATOR = value3)]
* </PRE>
* The SELECT of the discriminator column is optional.
* The WHERE clause is added where we don't want to include all possible classes
* that are stored in that table. You can omit the WHERE clause by specifying restrictDiscriminator as false.
*
* <H3>Generated statement - candidate type with select table</H3>
* Let's assume that we have a 1-N relation with a join table, and we want to
* find all elements of the candidate type (B) and optionally its subclasses (B1, B2).
* We need to select the join table, yet have a different candidate type.
* The query will be of the form
* <PRE>
* SELECT [`ELEMENT`.DISCRIMINATOR]
* FROM SELECTTABLE THIS INNER JOIN B `ELEMENT` ON `ELEMENT`.B_ID = THIS.B_ID_EID
* [WHERE (`ELEMENT`.DISCRIMINATOR = value1 OR `ELEMENT`.DISCRIMINATOR = value2 || `ELEMENT`.DISCRIMINATOR = value3)]
* </PRE>
* The SELECT of the discriminator column is optional.
* The WHERE clause is added where we don't want to include all possible classes
* that are stored in that table. You can omit the WHERE clause by specifying restrictDiscriminator as false.
*
* <H3>Selecting columns</H3>
* It should be noted that the "SELECT" columns are not added here (with the
* exception of THIS.DISCRIMINATOR, which is an optional addition). They are added
* after creating this statement. This statement simply creates the basic form of the
* select statement.
*/
public abstract class DiscriminatorIteratorStatement extends AbstractIteratorStatement
{
    /** Whether to select the discriminator column. */
    private boolean selectDiscriminator;

    /** Whether to restrict the discriminator values returned. */
    private boolean restrictDiscriminator = true;

    /** Whether to allow for nulls when joining a select table to candidate table. */
    private boolean allowNulls = false;

    /** Table to select (only when we aren't selecting the candidate table). */
    private DatastoreContainerObject selectTable;

    /** Mapping in the select table to join to the id of the candidate table (only when selectTable is not null). */
    private JavaTypeMapping selectCandidateMapping;

    /** Identifier to use for the candidate table (only when the selectTable is not null). */
    private DatastoreIdentifier candidateTableIdentifier;

    /** Candidate types to query. Normally we have a single candidate, but for interfaces we have multiple implementations. **/
    private Class[] candidateTypes;

    /**
     * Constructor, for a candidate type select.
     * @param clr The ClassLoaderResolver
     * @param candidateTypes Base object types that we are looking for
     * @param includeSubclasses Should we include subclasses of this candidate?
     * @param storeMgr Manager for the store
     * @param selectDiscriminator Whether to select the discriminator
     **/
    public DiscriminatorIteratorStatement(ClassLoaderResolver clr,
                                          Class[] candidateTypes,
                                          boolean includeSubclasses,
                                          StoreManager storeMgr,
                                          boolean selectDiscriminator)
    {
        super(candidateTypes[0], clr, includeSubclasses, storeMgr);
        this.selectDiscriminator = selectDiscriminator;
        this.candidateTypes = candidateTypes;
    }

    /**
     * Constructor, for a candidate type with a select table.
     * @param clr The ClassLoaderResolver
     * @param candidateType Base object type that we are looking for
     * @param includeSubclasses Should we include subclasses of this candidate?
     * @param storeMgr Manager for the store
     * @param selectDiscriminator Whether to select the discriminator
     * @param allowNulls Whether to allow nulls in the select table (and associated join to candidate table)
     * @param selectTable Table to select
     * @param selectCandidateMapping Mapping in the select table to join with the id of the candidate table
     * @param candidateTableIdentifier Identifier to use for the candidate table when using a select table
     **/
    public DiscriminatorIteratorStatement(ClassLoaderResolver clr,
                                          Class[] candidateType,
                                          boolean includeSubclasses,
                                          StoreManager storeMgr,
                                          boolean selectDiscriminator,
                                          boolean allowNulls,
                                          DatastoreContainerObject selectTable,
                                          JavaTypeMapping selectCandidateMapping,
                                          DatastoreIdentifier candidateTableIdentifier)
    {
        this(clr, candidateType, includeSubclasses, storeMgr, selectDiscriminator);
        this.selectTable = selectTable;
        this.selectCandidateMapping = selectCandidateMapping;
        this.candidateTableIdentifier = candidateTableIdentifier;
        this.allowNulls = allowNulls;
    }

    /**
     * Mutator for whether to restrict the discriminator values in the query to just those
     * of the candidate class (and subclasses where specified). By default the values of the
     * discriminator will be restricted in the query.
     * <B>Must be set before calling getQueryStatement() if it is required</B>
     * @param restrict the valid discrim values in the query.
     */
    public void setRestrictDiscriminator(boolean restrict)
    {
        this.restrictDiscriminator = restrict;
    }

    /**
     * Accessor for the Query Statement.
     * @param candidateAlias Alias for the candidate
     * @return The Query Statement for iterating through objects with a discriminator column
     */
    public QueryExpression getQueryStatement(DatastoreIdentifier candidateAlias)
    {
        QueryExpression stmt = null;
        DiscriminatorMetaData dismd = candidateTable.getDiscriminatorMetaData();
        JavaTypeMapping discriminatorMapping = candidateTable.getDiscriminatorMapping(true);
        if (discriminatorMapping != null)
        {
            // Use discriminator metadata from the place where the discriminator mapping is defined
            dismd = discriminatorMapping.getDatastoreContainer().getDiscriminatorMetaData();
        }
        boolean hasDiscriminator = (discriminatorMapping != null && dismd.getStrategy() != DiscriminatorStrategy.NONE);

        LogicSetExpression discrimTableExpr = null;
        if (selectTable != null)
        {
            // Select the required "selectTable"
            stmt = dba.newQueryStatement(selectTable, candidateAlias, clr);

            // Join from the "selectTable" to the table of our candidate
            ScalarExpression selectExpression = selectCandidateMapping.newScalarExpression(stmt, stmt.getMainTableExpression());
            LogicSetExpression candidateTableExpression = stmt.newTableExpression(candidateTable, candidateTableIdentifier);
            ScalarExpression candidateExpression = candidateTable.getIDMapping().newScalarExpression(stmt, candidateTableExpression);
            if (allowNulls)
            {
                // Do LEFT OUTER JOIN since we need to allow nulls in the results
                stmt.leftOuterJoin(selectExpression, candidateExpression, candidateTableExpression, true);
            }
            else
            {
                // Do INNER JOIN since we don't allow nulls in the results
                stmt.innerJoin(selectExpression, candidateExpression, candidateTableExpression, true);
            }
            discrimTableExpr = stmt.getTableExpression(candidateTableIdentifier);

            if (hasDiscriminator && selectDiscriminator)
            {
                // Select the discriminator column so we can process the ResultSet
                stmt.selectScalarExpression(candidateTable.getDiscriminatorMapping(true).newScalarExpression(stmt, candidateTableExpression));
            }
        }
        else
        {
            // Select the candidate table
            stmt = dba.newQueryStatement(candidateTable, candidateAlias, clr);
            discrimTableExpr = stmt.getMainTableExpression();

            if (hasDiscriminator && selectDiscriminator)
            {
                // Select the discriminator column so we can process the ResultSet
                stmt.select(discriminatorMapping);
            }
        }

        // Check if we can omit the discriminator restriction
        if (includeSubclasses && hasDiscriminator && candidateTable.getDiscriminatorMapping(false) != null &&
            !storeMgr.getOMFContext().getMetaDataManager().isPersistentDefinitionImplementation(candidateFullClassName))
        {
            String[] managedClasses = getCandidateTableManagedClasses();
            if (managedClasses.length == 1)
            {
                // Only the candidate managed by this table and the discrim is here and we're including subclasses
                // in the SELECT so don't apply any restriction on the discriminator value
                // Note : we omit the persistent interface impl case from here for now
                restrictDiscriminator = false;
            }
        }

        if (hasDiscriminator && restrictDiscriminator)
        {
            // Add the discriminator required values to the WHERE clause
            // Loop through each possible candidate type and add the discrim values for each branch
            boolean multipleDiscriminatorValues = candidateTypes.length > 1 ? true: false;
            BooleanExpression discExpr = null;
            for (int i=0; i<candidateTypes.length; i++)
            {
                // For this candidate type, go through the possible classes persistable in this table and add an OR to the WHERE clause
                String candidateName = candidateTypes[i].getName();
                BooleanExpression discExprCand = getExpressionForDiscriminatorForClass(stmt, candidateName,
                    dismd, discriminatorMapping, discrimTableExpr, storeMgr);
                if (discExpr != null)
                {
                    discExpr = discExpr.ior(discExprCand);
                }
                else
                {
                    discExpr = discExprCand;
                }
                if (includeSubclasses)
                {
                    Iterator iterator = storeMgr.getSubClassesForClass(candidateName, true, clr).iterator();
                    while (iterator.hasNext())
                    {
                        String subCandidateType = (String)iterator.next();
                        BooleanExpression discExprSub = getExpressionForDiscriminatorForClass(stmt,
                            subCandidateType, dismd, discriminatorMapping, discrimTableExpr, storeMgr);
                        discExpr = discExpr.ior(discExprSub);
                        if (!multipleDiscriminatorValues)
                        {
                            multipleDiscriminatorValues = true;
                        }
                    }
                }
            }

            if (allowNulls)
            {
                // Allow for null value of discriminator
                ScalarExpression expr = discriminatorMapping.newScalarExpression(stmt, discrimTableExpr);
                ScalarExpression val = new NullLiteral(stmt);
                BooleanExpression nullDiscExpr = expr.eq(val);
                discExpr = discExpr.ior(nullDiscExpr);
                if (!multipleDiscriminatorValues)
                {
                    multipleDiscriminatorValues = true;
                }
            }

            // Apply the discriminator to the query statement
            if (multipleDiscriminatorValues)
            {
                discExpr.encloseWithInParentheses();
            }
            stmt.andCondition(discExpr);
        }

        return stmt;
    }

    /**
     * Convenience method to generate a BooleanExpression for the associated discriminator value for
     * the specified class.
     * @param stmt The Query Statement to be updated
     * @param className The class name
     * @param dismd MetaData for the discriminator (root)
     * @param discriminatorMapping Mapping for the discriminator
     * @param discrimTableExpr Table expression for the discriminator
     * @return Boolean expression for this discriminator value
     */
    private static BooleanExpression getExpressionForDiscriminatorForClass(QueryExpression stmt,
            String className, DiscriminatorMetaData dismd, JavaTypeMapping discriminatorMapping,
            LogicSetExpression discrimTableExpr, StoreManager storeMgr)
    {
        String discriminatorValue = className; // Default to the "class-name" discriminator strategy
        if (dismd.getStrategy() == DiscriminatorStrategy.VALUE_MAP)
        {
            // Get the MetaData for the target class since that holds the "value"
            AbstractClassMetaData targetCmd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForClass(className, stmt.getClassLoaderResolver());
            discriminatorValue = targetCmd.getInheritanceMetaData().getDiscriminatorMetaData().getValue();
        }

        ScalarExpression discrExpr = discriminatorMapping.newScalarExpression(stmt, discrimTableExpr);
        ScalarExpression discrVal = discriminatorMapping.newLiteral(stmt, discriminatorValue);
        return discrExpr.eq(discrVal);
    }

    /**
     * Provides the names of the classes managed by the candidate table.
     */
    protected abstract String[] getCandidateTableManagedClasses();
}
TOP

Related Classes of org.datanucleus.store.mapped.query.DiscriminatorIteratorStatement

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.