| /* |
| * 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; |
| |
| import java.sql.SQLException; |
| import java.util.Map; |
| |
| import org.apache.openjpa.jdbc.kernel.JDBCStore; |
| import org.apache.openjpa.jdbc.schema.Column; |
| import org.apache.openjpa.jdbc.schema.ColumnIO; |
| import org.apache.openjpa.jdbc.schema.Index; |
| import org.apache.openjpa.jdbc.schema.Schemas; |
| import org.apache.openjpa.jdbc.sql.Joins; |
| import org.apache.openjpa.jdbc.sql.Result; |
| import org.apache.openjpa.jdbc.sql.RowManager; |
| import org.apache.openjpa.jdbc.sql.Select; |
| import org.apache.openjpa.kernel.OpenJPAStateManager; |
| import org.apache.openjpa.lib.log.Log; |
| import org.apache.openjpa.lib.util.Localizer; |
| import org.apache.openjpa.meta.MetaDataContext; |
| import org.apache.openjpa.meta.MetaDataModes; |
| import org.apache.openjpa.meta.MetaDataRepository; |
| import org.apache.openjpa.util.InternalException; |
| |
| /** |
| * Handles optimistic lock versioning for a class. |
| * |
| * @author Abe White |
| */ |
| public class Version |
| implements VersionStrategy, MetaDataContext, MetaDataModes { |
| |
| |
| private static final long serialVersionUID = 1L; |
| |
| private static final Localizer _loc = Localizer.forPackage(Version.class); |
| |
| private final ClassMapping _mapping; |
| private final VersionMappingInfo _info; |
| private VersionStrategy _strategy = null; |
| private int _resMode = MODE_NONE; |
| |
| private Column[] _cols = Schemas.EMPTY_COLUMNS; |
| private ColumnIO _io = null; |
| private Index _idx = null; |
| |
| /** |
| * Constructor. Supply owning mapping. |
| */ |
| public Version(ClassMapping mapping) { |
| _mapping = mapping; |
| _info = getMappingRepository().newMappingInfo(this); |
| } |
| |
| @Override |
| public MetaDataRepository getRepository() { |
| return _mapping.getRepository(); |
| } |
| |
| public MappingRepository getMappingRepository() { |
| return _mapping.getMappingRepository(); |
| } |
| |
| /** |
| * Return the owning mapping. |
| */ |
| public ClassMapping getClassMapping() { |
| return _mapping; |
| } |
| |
| /** |
| * The strategy used for versioning. |
| */ |
| public VersionStrategy getStrategy() { |
| return _strategy; |
| } |
| |
| /** |
| * The strategy used for versioning. The <code>adapt</code> |
| * parameter determines whether to adapt when mapping the strategy; |
| * use null if the strategy should not be mapped. |
| */ |
| public void setStrategy(VersionStrategy strategy, Boolean adapt) { |
| // set strategy first so we can access it during mapping |
| VersionStrategy orig = _strategy; |
| _strategy = strategy; |
| if (strategy != null) { |
| try { |
| strategy.setVersion(this); |
| if (adapt != null) |
| strategy.map(adapt); |
| } catch (RuntimeException re) { |
| // reset strategy |
| _strategy = orig; |
| throw re; |
| } |
| } |
| } |
| |
| /** |
| * Raw mapping data. |
| */ |
| public VersionMappingInfo getMappingInfo() { |
| return _info; |
| } |
| |
| /** |
| * Columns used for versioning. |
| */ |
| public Column[] getColumns() { |
| return _cols; |
| } |
| |
| /** |
| * Columns used for versioning. |
| */ |
| public void setColumns(Column[] cols) { |
| if (cols == null) |
| cols = Schemas.EMPTY_COLUMNS; |
| _cols = cols; |
| } |
| |
| /** |
| * I/O information on the version columns. |
| */ |
| public ColumnIO getColumnIO() { |
| return (_io == null) ? ColumnIO.UNRESTRICTED : _io; |
| } |
| |
| /** |
| * I/O information on the version columns. |
| */ |
| public void setColumnIO(ColumnIO io) { |
| _io = io; |
| } |
| |
| /** |
| * Index on the version columns, or null if none. |
| */ |
| public Index getIndex() { |
| return _idx; |
| } |
| |
| /** |
| * Index on the version columns, or null if none. |
| */ |
| public void setIndex(Index idx) { |
| _idx = idx; |
| } |
| |
| /** |
| * Increment the reference count of used schema components. |
| */ |
| public void refSchemaComponents() { |
| for (int i = 0; i < _cols.length; i++) |
| _cols[i].ref(); |
| } |
| |
| /** |
| * Clear mapping information, including strategy. |
| */ |
| public void clearMapping() { |
| _strategy = null; |
| _cols = Schemas.EMPTY_COLUMNS; |
| _idx = null; |
| _info.clear(); |
| setResolve(MODE_MAPPING | MODE_MAPPING_INIT, false); |
| } |
| |
| /** |
| * Update {@link MappingInfo} with our current mapping information. |
| */ |
| public void syncMappingInfo() { |
| _info.syncWith(this); |
| |
| // setup the version field with our mapping info |
| FieldMapping fm = _mapping.getVersionFieldMapping(); |
| if (fm != null) { |
| // erase explicit strategy if it is standard version strategy for |
| // field type |
| if (_info.getStrategy() != null |
| && _info.getStrategy().equals(getMappingRepository(). |
| defaultStrategy(this, fm).getAlias())) |
| _info.setStrategy(null); |
| |
| fm.getMappingInfo().clear(); |
| fm.getValueInfo().clear(); |
| fm.getKeyMapping().getValueInfo().clear(); |
| fm.getElementMapping().getValueInfo().clear(); |
| fm.getValueInfo().copy(_info); |
| _info.clear(); |
| } |
| } |
| |
| /** |
| * Resolve mode. |
| */ |
| public int getResolve() { |
| return _resMode; |
| } |
| |
| /** |
| * Resolve mode. |
| */ |
| public void setResolve(int mode) { |
| _resMode = mode; |
| } |
| |
| /** |
| * Resolve mode. |
| */ |
| public void setResolve(int mode, boolean on) { |
| if (mode == MODE_NONE) |
| _resMode = mode; |
| else if (on) |
| _resMode |= mode; |
| else |
| _resMode &= ~mode; |
| } |
| |
| /** |
| * Resolve mapping information. |
| */ |
| public boolean resolve(int mode) { |
| if ((_resMode & mode) == mode) |
| return true; |
| int cur = _resMode; |
| _resMode |= mode; |
| if ((mode & MODE_MAPPING) != 0 && (cur & MODE_MAPPING) == 0) |
| resolveMapping(); |
| if ((mode & MODE_MAPPING_INIT) != 0 && (cur & MODE_MAPPING_INIT) == 0) |
| _strategy.initialize(); |
| return false; |
| } |
| |
| /** |
| * Setup mapping. |
| */ |
| private void resolveMapping() { |
| // if there is a version field, copy mapping info from it |
| FieldMapping fm = _mapping.getVersionFieldMapping(); |
| if (fm != null) |
| _info.copy(fm.getValueInfo()); |
| |
| // map strategy |
| MappingRepository repos = getMappingRepository(); |
| if (_strategy == null) |
| repos.getStrategyInstaller().installStrategy(this); |
| Log log = repos.getLog(); |
| if (log.isTraceEnabled()) |
| log.trace(_loc.get("strategy", this, _strategy.getAlias())); |
| |
| // mark columns as mapped |
| Column[] cols = getColumns(); |
| ColumnIO io = getColumnIO(); |
| for (int i = 0; i < cols.length; i++) { |
| if (io.isInsertable(i, false)) |
| cols[i].setFlag(Column.FLAG_DIRECT_INSERT, true); |
| if (io.isUpdatable(i, false)) |
| cols[i].setFlag(Column.FLAG_DIRECT_UPDATE, true); |
| } |
| } |
| |
| ////////////////////////////////// |
| // VersionStrategy implementation |
| ////////////////////////////////// |
| |
| @Override |
| public String getAlias() { |
| return assertStrategy().getAlias(); |
| } |
| |
| @Override |
| public void map(boolean adapt) { |
| assertStrategy().map(adapt); |
| } |
| |
| @Override |
| public void initialize() { |
| assertStrategy().initialize(); |
| } |
| |
| @Override |
| public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm) |
| throws SQLException { |
| assertStrategy().insert(sm, store, rm); |
| } |
| |
| @Override |
| public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm) |
| throws SQLException { |
| assertStrategy().update(sm, store, rm); |
| } |
| |
| @Override |
| public void delete(OpenJPAStateManager sm, JDBCStore store, RowManager rm) |
| throws SQLException { |
| assertStrategy().delete(sm, store, rm); |
| } |
| |
| @Override |
| public Boolean isCustomInsert(OpenJPAStateManager sm, JDBCStore store) { |
| return assertStrategy().isCustomInsert(sm, store); |
| } |
| |
| @Override |
| public Boolean isCustomUpdate(OpenJPAStateManager sm, JDBCStore store) { |
| return assertStrategy().isCustomUpdate(sm, store); |
| } |
| |
| @Override |
| public Boolean isCustomDelete(OpenJPAStateManager sm, JDBCStore store) { |
| return assertStrategy().isCustomDelete(sm, store); |
| } |
| |
| @Override |
| public void customInsert(OpenJPAStateManager sm, JDBCStore store) |
| throws SQLException { |
| assertStrategy().customInsert(sm, store); |
| } |
| |
| @Override |
| public void customUpdate(OpenJPAStateManager sm, JDBCStore store) |
| throws SQLException { |
| assertStrategy().customUpdate(sm, store); |
| } |
| |
| @Override |
| public void customDelete(OpenJPAStateManager sm, JDBCStore store) |
| throws SQLException { |
| assertStrategy().customDelete(sm, store); |
| } |
| |
| @Override |
| public void setVersion(Version owner) { |
| assertStrategy().setVersion(owner); |
| } |
| |
| @Override |
| public boolean select(Select sel, ClassMapping mapping) { |
| return assertStrategy().select(sel, mapping); |
| } |
| |
| @Override |
| public Object load(OpenJPAStateManager sm, JDBCStore store, Result res) |
| throws SQLException { |
| return assertStrategy().load(sm, store, res); |
| } |
| |
| @Override |
| public Object load(OpenJPAStateManager sm, JDBCStore store, Result res, Joins joins) |
| throws SQLException { |
| return assertStrategy().load(sm, store, res, joins); |
| } |
| |
| @Override |
| public void afterLoad(OpenJPAStateManager sm, JDBCStore store) { |
| assertStrategy().afterLoad(sm, store); |
| } |
| |
| @Override |
| public boolean checkVersion(OpenJPAStateManager sm, JDBCStore store, |
| boolean updateVersion) |
| throws SQLException { |
| return assertStrategy().checkVersion(sm, store, updateVersion); |
| } |
| |
| @Override |
| public int compareVersion(Object v1, Object v2) { |
| return assertStrategy().compareVersion(v1, v2); |
| } |
| |
| private VersionStrategy assertStrategy() { |
| if (_strategy == null) |
| throw new InternalException(); |
| return _strategy; |
| } |
| |
| @Override |
| public String toString() { |
| return _mapping + "<version>"; |
| } |
| |
| /** |
| * @return a Map<Column,Object> specifying how to update each version |
| * column in this instance during a bulk update. |
| * |
| * @since 1.0.0 |
| */ |
| @Override |
| public Map<Column,?> getBulkUpdateValues() { |
| return _strategy.getBulkUpdateValues(); |
| } |
| } |