Package org.apache.openjpa.jdbc.meta.strats

Source Code of org.apache.openjpa.jdbc.meta.strats.ColumnVersionStrategy

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.   
*/
package org.apache.openjpa.jdbc.meta.strats;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.Comparator;

import org.apache.openjpa.jdbc.identifier.DBIdentifier;
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
import org.apache.openjpa.jdbc.kernel.JDBCStore;
import org.apache.openjpa.jdbc.meta.ClassMapping;
import org.apache.openjpa.jdbc.meta.VersionMappingInfo;
import org.apache.openjpa.jdbc.meta.strats.AbstractVersionStrategy;
import org.apache.openjpa.jdbc.schema.Column;
import org.apache.openjpa.jdbc.schema.ColumnIO;
import org.apache.openjpa.jdbc.schema.ForeignKey;
import org.apache.openjpa.jdbc.schema.Index;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.Joins;
import org.apache.openjpa.jdbc.sql.Result;
import org.apache.openjpa.jdbc.sql.Row;
import org.apache.openjpa.jdbc.sql.RowManager;
import org.apache.openjpa.jdbc.sql.Select;
import org.apache.openjpa.kernel.MixedLockLevels;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.kernel.StoreManager;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.MetaDataException;

/**
* Uses a one or more column(s) and corresponding version object.
*
* @author Marc Prud'hommeaux
* @author Pinaki Poddar
*/
public abstract class ColumnVersionStrategy
    extends AbstractVersionStrategy {

    private static final Localizer _loc = Localizer.forPackage
        (ColumnVersionStrategy.class);

    /**
     * Return the code from {@link JavaTypes} for the version values this
     * strategy uses. This method is only used during mapping installation.
     */
    protected abstract int getJavaType();
   
    /**
     * Return the code from {@link JavaTypes} for the version value this given
     * column index uses. Only used if the version strategy employs more than
     * one column.
     */
    protected int getJavaType(int i) {
        throw new AbstractMethodError(_loc.get(
                "multi-column-version-unsupported",getAlias()).toString());
    }
   
    /**
     * Return the next version given the current one, which may be null.
     */
    protected abstract Object nextVersion(Object version);

    /**
     * Compare the two versions. Defaults to assuming the version objects
     * implement {@link Comparable}.
     *
     * @see Comparator#compare
     */
    protected int compare(Object v1, Object v2) {
        if (v1 == v2)
            return 0;
        if (v1 == null)
            return -1;
        if (v2 == null)
            return 1;
       
        if (v1.getClass().isArray()) {
          if (!v2.getClass().isArray())
            throw new InternalException();
          return compare((Object[])v1, (Object[])v2);
        }
        if (v1.getClass() != v2.getClass()) {
            if (v1 instanceof Number && !(v1 instanceof BigDecimal))
                v1 = new BigDecimal(((Number) v1).doubleValue());

            if (v2 instanceof Number && !(v2 instanceof BigDecimal))
                v2 = new BigDecimal(((Number) v2).doubleValue());
        }

        return ((Comparable) v1).compareTo(v2);
    }


  /**
   * Compare each element of the given arrays that must be of equal size.
     * The given array values represent version values and the result designate
   * whether first version is earlier, same or later than the second one.
   *
   * @return If any element of a1 is later than corresponding element of
     * a2 then returns 1 i.e. the first version is later than the second
     * version. If each element of a1 is equal to corresponding element of a2
     * then return 0 i.e. the first version is same as the second version.
   * else return a negative number i.e. the first version is earlier than
   * the second version.
   */
  protected int compare(Object[] a1, Object[] a2) {
    if (a1.length != a2.length)
        throw new InternalException();
    int total = 0;
    for (int i = 0; i < a1.length; i++) {
      int c =  compare(a1[i], a2[i]);
      if (c > 0)
        return 1;
      total += c;
    }
    return total;
  }
 
    public void map(boolean adapt) {
        ClassMapping cls = vers.getClassMapping();
        if (cls.getJoinablePCSuperclassMapping() != null
            || cls.getEmbeddingMetaData() != null)
            throw new MetaDataException(_loc.get("not-base-vers", cls));

        VersionMappingInfo info = vers.getMappingInfo();
        info.assertNoJoin(vers, true);
        info.assertNoForeignKey(vers, !adapt);
        info.assertNoUnique(vers, false);
        if (info.getColumns().size() > 1) {
          Column[] templates = new Column[info.getColumns().size()];
          for (int i = 0; i < info.getColumns().size(); i++) {
                templates[i] = new Column();
            Column infoColumn = (Column)info.getColumns().get(i);
            templates[i].setTableIdentifier(infoColumn.getTableIdentifier());
            templates[i].setType(infoColumn.getType());
            templates[i].setSize(infoColumn.getSize());
                templates[i].setDecimalDigits(infoColumn.getDecimalDigits());
            templates[i].setJavaType(getJavaType(i));
            templates[i].setIdentifier(infoColumn.getIdentifier());
          }
          Column[] cols = info.getColumns(vers, templates, adapt);
          for (int i = 0; i < cols.length; i++)
            cols[i].setVersionStrategy(this);
          vers.setColumns(cols);
          vers.setColumnIO(info.getColumnIO());
        } else {
           Column tmplate = new Column();
           tmplate.setJavaType(getJavaType());
           DBDictionary dict = vers.getMappingRepository().getDBDictionary();
           DBIdentifier versName = DBIdentifier.newColumn("versn", dict != null ? dict.delimitAll() : false);
           tmplate.setIdentifier(versName);

           Column[] cols = info.getColumns(vers, new Column[]{ tmplate },
                   adapt);
           cols[0].setVersionStrategy(this);
           vers.setColumns(cols);
           vers.setColumnIO(info.getColumnIO());

           Index idx = info.getIndex(vers, cols, adapt);
           vers.setIndex(idx);
        }
    }

    public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
        throws SQLException {
        Column[] cols = vers.getColumns();
        ColumnIO io = vers.getColumnIO();
        Object initial = nextVersion(null);
        for (int i = 0; i < cols.length; i++) {
            Row row = rm.getRow(cols[i].getTable(), Row.ACTION_INSERT, sm,
                    true);
            if (io.isInsertable(i, initial == null))
                row.setObject(cols[i], getColumnValue(initial, i));
        }
        // set initial version into state manager
        Object nextVersion;
        nextVersion = initial;
        sm.setNextVersion(nextVersion);
    }

    public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
        throws SQLException {
        Column[] cols = vers.getColumns();
        if (cols == null || cols.length == 0 ||
            !sm.isDirty() && !sm.isVersionUpdateRequired())
            return;

        Object curVersion = sm.getVersion();
        Object nextVersion = nextVersion(curVersion);


        // set where and update conditions on row
        for (int i = 0; i < cols.length; i++) {
            Row row = rm.getRow(cols[i].getTable(), Row.ACTION_UPDATE, sm,
                    true);
            row.setFailedObject(sm.getManagedInstance());
            if (curVersion != null && sm.isVersionCheckRequired()) {
                row.whereObject(cols[i], getColumnValue(curVersion, i));
                if (isSecondaryColumn(cols[i], sm)) {
                  ForeignKey[] fks = cols[i].getTable().getForeignKeys();
                  for (ForeignKey fk : fks) {
                    row.whereForeignKey(fk, sm);
                  }
                }
            }
            if (vers.getColumnIO().isUpdatable(i, nextVersion == null))
                row.setObject(cols[i], getColumnValue(nextVersion, i));
        }

        if (nextVersion != null)
            sm.setNextVersion(nextVersion);
    }

    public void delete(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
        throws SQLException {
        Column[] cols = vers.getColumns();

        Object curVersion = sm.getVersion();
        Object cur;
        for (int i = 0; i < cols.length; i++) {
            Row row = rm.getRow(cols[i].getTable(),
              Row.ACTION_DELETE, sm, true);
            row.setFailedObject(sm.getManagedInstance());
            cur = getColumnValue(curVersion, i);
            // set where and update conditions on row
            if (cur != null) {
                row.whereObject(cols[i], cur);
                if (isSecondaryColumn(cols[i], sm)) {
                  ForeignKey[] fks = cols[i].getTable().getForeignKeys();
                  for (ForeignKey fk : fks) {
                    row.whereForeignKey(fk, sm);
                  }
                }
            }
        }
    }

    public boolean select(Select sel, ClassMapping mapping) {
        sel.select(vers.getColumns());
        return true;
    }
   
    public Object load(OpenJPAStateManager sm, JDBCStore store, Result res)
        throws SQLException {
        return this.load(sm, store, res, null);
    }

    public Object load(OpenJPAStateManager sm, JDBCStore store, Result res, Joins joins)
        throws SQLException {
        // typically if one version column is in the result, they all are, so
        // optimize by checking for the first one before doing any real work
        Column[] cols = vers.getColumns();
        if (!res.contains(cols[0], joins)) {
            return null;
        }

        Object version = populateFromResult(res, joins);
       
        // OPENJPA-662 Allow a null StateManager because this method may just be
        // invoked to get the result of projection query
        if (sm != null) {
          sm.setVersion(version);
        }
        return version;
    }

    public boolean checkVersion(OpenJPAStateManager sm, JDBCStore store,
        boolean updateVersion)
        throws SQLException {
        Column[] cols = vers.getColumns();
        Select sel = store.getSQLFactory().newSelect();
        sel.select(cols);
        sel.wherePrimaryKey(sm.getObjectId(), vers.getClassMapping(), store);

        // No need to lock version field (i.e. optimistic), except when version update is required (e.g. refresh)
        JDBCFetchConfiguration fetch = store.getFetchConfiguration();
        if (!updateVersion && fetch.getReadLockLevel() >= MixedLockLevels.LOCK_PESSIMISTIC_READ) {
            fetch = (JDBCFetchConfiguration) fetch.clone();
            fetch.setReadLockLevel(MixedLockLevels.LOCK_NONE);
        }
        Result res = sel.execute(store, fetch);
        try {
            if (!res.next())
                return false;

            Object memVersion = sm.getVersion();
            Object dbVersion  = populateFromResult(res, null);
            boolean refresh   = compare(memVersion, dbVersion) < 0;

            if (updateVersion)
                sm.setVersion(dbVersion);
            return !refresh;
        } finally {
            res.close();
        }
    }

    public int compareVersion(Object v1, Object v2) {
        if (v1 == v2)
            return StoreManager.VERSION_SAME;
        if (v1 == null || v2 == null)
            return StoreManager.VERSION_DIFFERENT;

        int cmp = compare(v1, v2);
        if (cmp < 0)
            return StoreManager.VERSION_EARLIER;
        if (cmp > 0)
            return StoreManager.VERSION_LATER;
        return StoreManager.VERSION_SAME;
    }
       
    /**
     * Populate values of a version object from the given result.
     *
     * @return a single Object or an array depending on whether using a single
     * or multiple columns being used for representation.
    */
    Object populateFromResult(Result res, Joins joins) throws SQLException {
        if (res == null)
     return null;
     
        Column[] cols = vers.getColumns();
        Object[] values = new Object[cols.length];
        for (int i = 0; i < cols.length; i++) {
            values[i] = res.getObject(cols[i], null, joins);
        }
        return (cols.length == 1) ? values[0] : values;
    }
   
    Object getColumnValue(Object o, int idx) {
      if (o == null)
        return null;
      if (o.getClass().isArray())
        return Array.get(o, idx);
      return o;
    }
   
    boolean isSecondaryColumn(Column col, OpenJPAStateManager sm) {
      ClassMapping mapping = (ClassMapping)sm.getMetaData();
      while (mapping != null) {
        if (mapping.getTable() == col.getTable())
          return false;
        else
          mapping = mapping.getPCSuperclassMapping();
      }
      return true;
    }
}
TOP

Related Classes of org.apache.openjpa.jdbc.meta.strats.ColumnVersionStrategy

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.