| /* |
| * 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.sql; |
| |
| import java.sql.SQLException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.openjpa.jdbc.schema.Table; |
| import org.apache.openjpa.kernel.OpenJPAStateManager; |
| import org.apache.openjpa.util.InternalException; |
| |
| /** |
| * Manages SQL rows during an insert/update/delete process. |
| * |
| * @author Abe White |
| */ |
| public class RowManagerImpl |
| implements RowManager { |
| |
| private Map<Key, PrimaryRow> _inserts = null; |
| private Map<Key, PrimaryRow> _updates = null; |
| private Map<Key, PrimaryRow> _deletes = null; |
| private Collection<SecondaryRow> _secondaryUpdates = null; |
| private Collection<SecondaryRow> _secondaryDeletes = null; |
| private Collection<Row> _allRowUpdates = null; |
| private Collection<Row> _allRowDeletes = null; |
| |
| // we maintain a list of the order of all primary rows if the user |
| // wants to be able to fetch them in order |
| private final List<PrimaryRow> _primaryOrder; |
| |
| // track whether we're dealing with any auto-inc columns |
| private boolean _auto = false; |
| |
| // cache the last key and primary row; when looping over |
| // all the field mappings of a class each one will probably ask for the |
| // same key, so avoid the key creation and row lookup when possible |
| private Key _key = null; |
| private PrimaryRow _row = null; |
| |
| /** |
| * Constructor. |
| * |
| * @param order whether to keep track of the order in which rows are added |
| */ |
| public RowManagerImpl(boolean order) { |
| _primaryOrder = (order) ? new ArrayList<>() : null; |
| } |
| |
| /** |
| * Whether any primary rows have auto-assign constraints. |
| */ |
| public boolean hasAutoAssignConstraints() { |
| return _auto; |
| } |
| |
| /** |
| * Return the ordered primary rows. Only available if ordering requested |
| * on construction. |
| */ |
| public List<PrimaryRow> getOrdered() { |
| if(_primaryOrder == null ) { |
| return Collections.emptyList(); |
| } |
| else { |
| return _primaryOrder; |
| } |
| } |
| |
| /** |
| * Return all inserted primary rows. |
| */ |
| public Collection<PrimaryRow> getInserts() { |
| if(_inserts == null ) { |
| return Collections.emptyList(); |
| } |
| else { |
| return _inserts.values(); |
| } |
| } |
| |
| /** |
| * Return all updated primary rows. |
| */ |
| public Collection<PrimaryRow> getUpdates() { |
| if(_updates == null ){ |
| return Collections.emptyList(); |
| } |
| else { |
| return _updates.values(); |
| } |
| } |
| |
| /** |
| * Return all deleted primary rows. |
| */ |
| public Collection<PrimaryRow> getDeletes() { |
| if(_deletes == null) { |
| return Collections.emptyList(); |
| } |
| else { |
| return _deletes.values(); |
| } |
| } |
| |
| /** |
| * Return all inserted and updated secondary rows. |
| */ |
| public Collection<SecondaryRow> getSecondaryUpdates() { |
| if(_secondaryUpdates == null) { |
| return Collections.emptyList(); |
| } |
| else { |
| return _secondaryUpdates; |
| } |
| } |
| |
| /** |
| * Return all deleted secondary rows. |
| */ |
| public Collection<SecondaryRow> getSecondaryDeletes() { |
| if(_secondaryDeletes == null) { |
| return Collections.emptyList(); |
| } |
| else { |
| return _secondaryDeletes; |
| } |
| } |
| |
| /** |
| * Return any 'all row' updates. |
| */ |
| public Collection<Row> getAllRowUpdates() { |
| if(_allRowUpdates == null) { |
| return Collections.emptyList(); |
| } |
| else { |
| return _allRowUpdates; |
| } |
| } |
| |
| /** |
| * Return any 'all row' deletes. |
| */ |
| public Collection<Row> getAllRowDeletes() { |
| if(_allRowDeletes == null) { |
| return Collections.emptyList(); |
| } |
| else { |
| return _allRowDeletes; |
| } |
| |
| } |
| |
| @Override |
| public Row getSecondaryRow(Table table, int action) { |
| return new SecondaryRow(table, action); |
| } |
| |
| @Override |
| public void flushSecondaryRow(Row row) |
| throws SQLException { |
| if (!row.isValid()) |
| return; |
| |
| SecondaryRow srow = (SecondaryRow) row; |
| if (srow.getAction() == Row.ACTION_DELETE) { |
| if (_secondaryDeletes == null) |
| _secondaryDeletes = new ArrayList<>(); |
| _secondaryDeletes.add((SecondaryRow) srow.clone()); |
| } else { |
| if (_secondaryUpdates == null) |
| _secondaryUpdates = new ArrayList<>(); |
| _secondaryUpdates.add((SecondaryRow) srow.clone()); |
| } |
| } |
| |
| @Override |
| public Row getAllRows(Table table, int action) { |
| return new RowImpl(table, action); |
| } |
| |
| @Override |
| public void flushAllRows(Row row) { |
| if (!row.isValid()) |
| return; |
| |
| switch (row.getAction()) { |
| case Row.ACTION_UPDATE: |
| if (_allRowUpdates == null) |
| _allRowUpdates = new ArrayList<>(); |
| _allRowUpdates.add(row); |
| break; |
| case Row.ACTION_DELETE: |
| if (_allRowDeletes == null) |
| _allRowDeletes = new ArrayList<>(); |
| _allRowDeletes.add(row); |
| break; |
| default: |
| throw new InternalException("action = " + row.getAction()); |
| } |
| } |
| |
| @Override |
| public Row getRow(Table table, int action, OpenJPAStateManager sm, |
| boolean create) { |
| if (sm == null) |
| return null; |
| |
| // check if request matches cached version |
| if (_key != null && _key.table == table && _key.sm == sm |
| && _row != null && _row.getAction() == action) |
| return _row; |
| |
| Map<Key, PrimaryRow> map; |
| if (action == Row.ACTION_DELETE) { |
| if (_deletes == null && create) |
| _deletes = new LinkedHashMap<>(); |
| map = _deletes; |
| } else if (action == Row.ACTION_INSERT) { |
| if (_inserts == null && create) |
| _inserts = new LinkedHashMap<>(); |
| map = _inserts; |
| } else { |
| if (_updates == null && create) |
| _updates = new LinkedHashMap<>(); |
| map = _updates; |
| } |
| if (map == null) |
| return null; |
| |
| _key = new Key(table, sm); |
| _row = map.get(_key); |
| |
| if (_row == null && create) { |
| _row = new PrimaryRow(table, action, sm); |
| map.put(_key, _row); |
| if (_primaryOrder != null) { |
| _row.setIndex(_primaryOrder.size()); |
| _primaryOrder.add(_row); |
| } |
| |
| if (!_auto && action == Row.ACTION_INSERT) |
| _auto = table.getAutoAssignedColumns().length > 0; |
| } |
| |
| if (_row != null) |
| _row.setFailedObject(sm.getManagedInstance()); |
| return _row; |
| } |
| |
| /** |
| * Key for hashing virtual rows. |
| */ |
| private static class Key { |
| |
| public final Table table; |
| public final OpenJPAStateManager sm; |
| |
| public Key(Table table, OpenJPAStateManager sm) { |
| this.table = table; |
| this.sm = sm; |
| } |
| |
| @Override |
| public int hashCode() { |
| return ((table == null) ? 0 : table.hashCode()) + ((sm == null) ? 0 : sm.hashCode()) % Integer.MAX_VALUE; |
| } |
| |
| @Override |
| public boolean equals(Object other) { |
| if (other == null) |
| return false; |
| if (other == this) |
| return true; |
| |
| Key key = (Key) other; |
| return table == key.table && sm == key.sm; |
| } |
| } |
| } |