blob: ea48ec5fd226de97c7868e178cd072b5ad5f2c8a [file] [log] [blame]
/*
* 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 org.apache.openjpa.jdbc.meta.ClassMapping;
import org.apache.openjpa.jdbc.meta.RelationId;
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.Table;
import org.apache.openjpa.kernel.OpenJPAStateManager;
/**
* Secondary table row that tracks foreign keys to auto-inc columns.
*
* @author Abe White
*/
public class SecondaryRow
extends RowImpl {
private OpenJPAStateManager[] _fks = null;
private ColumnIO[] _fkIO = null;
private OpenJPAStateManager[] _rels = null;
private RelationId[] _callbacks = null;
/**
* Constructor; supply table and action.
*/
public SecondaryRow(Table table, int action) {
this(table.getColumns(), action);
}
protected SecondaryRow(Column[] cols, int action) {
super(cols, action);
}
@Override
public void setForeignKey(ForeignKey fk, OpenJPAStateManager sm)
throws SQLException {
setForeignKey(fk, null, sm);
}
@Override
public void setForeignKey(ForeignKey fk, ColumnIO io,
OpenJPAStateManager sm)
throws SQLException {
if (!delayForeignKey(fk, sm)) {
super.setForeignKey(fk, io, sm);
return;
}
// force valid
if (canSetAny(io, fk.getColumns().length
+ fk.getConstantColumns().length, false))
setValid(true);
// record foreig key for delayed flush
if (_fks == null)
_fks = new OpenJPAStateManager[getTable().getForeignKeys().length];
_fks[fk.getIndex()] = sm;
if (_fkIO != null)
_fkIO[fk.getIndex()] = io;
else if (io != null
&& ((getAction() == ACTION_INSERT
&& !io.isAllInsertable(fk, false))
|| (getAction() != ACTION_INSERT
&& !io.isAllUpdatable(fk, false)))) {
_fkIO = new ColumnIO[_fks.length];
_fkIO[fk.getIndex()] = io;
}
}
/**
* Record foreign keys to new auto-inc instances; flush them only when
* we have to generate our SQL to give the instance a chance to finalize
* its values.
*/
private boolean delayForeignKey(ForeignKey fk, OpenJPAStateManager sm) {
return fk.isPrimaryKeyAutoAssigned() && getAction() != ACTION_DELETE
&& sm != null && sm.isNew() && !sm.isFlushed();
}
@Override
public void setRelationId(Column col, OpenJPAStateManager sm,
RelationId rel)
throws SQLException {
if (sm == null || sm.getObjectId() != null || !sm.isNew()
|| sm.isFlushed() || !isPrimaryKeyAutoAssigned(sm))
super.setRelationId(col, sm, rel);
else {
if (_rels == null) {
Column[] cols = getTable().getRelationIdColumns();
_rels = new OpenJPAStateManager[cols.length];
_callbacks = new RelationId[cols.length];
}
int idx = getRelationIdIndex(col);
_rels[idx] = sm;
_callbacks[idx] = rel;
}
}
/**
* Return the index into our relation id array of the value for the
* given column.
*/
private int getRelationIdIndex(Column col) {
Column[] cols = getTable().getRelationIdColumns();
for (int i = 0; i < cols.length; i++)
if (cols[i] == col)
return i;
return -1;
}
/**
* Return true if any primary key columns of the given instance are
* auto-assigned.
*/
private static boolean isPrimaryKeyAutoAssigned(OpenJPAStateManager sm) {
ClassMapping cls = (ClassMapping) sm.getMetaData();
while (cls.getJoinablePCSuperclassMapping() != null)
cls = cls.getJoinablePCSuperclassMapping();
Column[] cols = cls.getPrimaryKeyColumns();
for (Column col : cols)
if (col.isAutoAssigned())
return true;
return false;
}
@Override
protected String generateSQL(DBDictionary dict) {
try {
if (_fks != null) {
ForeignKey[] fks = getTable().getForeignKeys();
ColumnIO io;
for (int i = 0; i < _fks.length; i++) {
if (_fks[i] != null) {
io = (_fkIO == null) ? null : _fkIO[i];
super.setForeignKey(fks[i], io, _fks[i]);
}
}
}
if (_rels != null) {
Column[] cols = getTable().getRelationIdColumns();
for (int i = 0; i < _rels.length; i++)
if (_rels[i] != null)
super.setRelationId(cols[i], _rels[i], _callbacks[i]);
}
}
catch (SQLException se) {
throw SQLExceptions.getStore(se, dict);
}
return super.generateSQL(dict);
}
@Override
protected RowImpl newInstance(Column[] cols, int action) {
return new SecondaryRow(cols, action);
}
@Override
public void copyInto(RowImpl row, boolean whereOnly) {
super.copyInto(row, whereOnly);
if (_fks == null || whereOnly || row.getAction() == ACTION_DELETE
|| !(row instanceof SecondaryRow))
return;
SecondaryRow srow = (SecondaryRow) row;
if (srow._fks == null)
srow._fks = new OpenJPAStateManager[_fks.length];
System.arraycopy(_fks, 0, srow._fks, 0, _fks.length);
if (_fkIO != null) {
if (srow._fkIO == null)
srow._fkIO = new ColumnIO[_fkIO.length];
System.arraycopy(_fkIO, 0, srow._fkIO, 0, _fkIO.length);
}
}
}