blob: d2b049c9d5d9414de6f240549d4e17cb44194f74 [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.meta.strats;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.openjpa.enhance.PersistenceCapable;
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.Embeddable;
import org.apache.openjpa.jdbc.meta.FieldMapping;
import org.apache.openjpa.jdbc.meta.FieldStrategy;
import org.apache.openjpa.jdbc.meta.ValueMapping;
import org.apache.openjpa.jdbc.meta.ValueMappingImpl;
import org.apache.openjpa.jdbc.schema.Column;
import org.apache.openjpa.jdbc.schema.ColumnIO;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.kernel.ObjectIdStateManager;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.kernel.StateManagerImpl;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.MetaDataModes;
import org.apache.openjpa.util.MetaDataException;
/**
* Base class for embedded value handlers.
*
* @author Abe White
* @since 0.4.0
*/
public abstract class EmbedValueHandler
extends AbstractValueHandler {
private static final long serialVersionUID = 1L;
private static final Localizer _loc = Localizer.forPackage
(EmbedValueHandler.class);
/**
* Maps embedded value and gathers columns and arguments into given lists.
* @deprecated
*/
@Deprecated
protected void map(ValueMapping vm, String name, ColumnIO io,
boolean adapt, List cols, List args) {
DBDictionary dict = vm.getMappingRepository().getDBDictionary();
DBIdentifier colName = DBIdentifier.newColumn(name, dict != null ? dict.delimitAll() : false);
map(vm, colName, io, adapt, cols, args);
}
/**
* Maps embedded value and gathers columns and arguments into given lists.
*/
protected void map(ValueMapping vm, DBIdentifier name, ColumnIO io,
boolean adapt, List cols, List args) {
// have to resolve embedded value to collect its columns
vm.getEmbeddedMapping().resolve(MetaDataModes.MODE_META | MetaDataModes.MODE_MAPPING);
// gather columns and result arguments
FieldMapping[] fms = vm.getEmbeddedMapping().getFieldMappings();
Column[] curCols;
Object[] curArgs;
ColumnIO curIO;
for (FieldMapping fm : fms) {
if (fm.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
continue;
FieldStrategy strat = fm.getStrategy();
if (!(strat instanceof Embeddable))
throw new MetaDataException(_loc.get("not-embeddable",
vm, fm));
ValueMapping val = fm.getValueMapping();
if (val.getEmbeddedMapping() != null)
map(val, name, io, adapt, cols, args);
curCols = ((Embeddable) strat).getColumns();
curIO = ((Embeddable) strat).getColumnIO();
for (int j = 0; j < curCols.length; j++) {
io.setInsertable(cols.size(), curIO.isInsertable(j, false));
io.setNullInsertable(cols.size(),
curIO.isInsertable(j, true));
io.setUpdatable(cols.size(), curIO.isUpdatable(j, false));
io.setNullUpdatable(cols.size(), curIO.isUpdatable(j, true));
cols.add(curCols[j]);
}
curArgs = ((Embeddable) fm.getStrategy()).getResultArguments();
if (curCols.length == 1)
args.add(curArgs);
else if (curCols.length > 1)
for (int j = 0; j < curCols.length; j++)
args.add((curArgs == null) ? null
: ((Object[]) curArgs)[j]);
}
}
/**
* Helper to convert an object value to its datastore equivalent.
*
* @param em state manager for embedded object
* @param vm owning value
* @param store store manager
* @param cols embedded columns
* @param rval return array if multiple columns
* @param idx index in columns array to start
*/
protected Object toDataStoreValue(OpenJPAStateManager em, ValueMapping vm,
JDBCStore store, Column[] cols, Object rval, int idx) {
// This is a placeholder to hold the value generated in
// toDataStoreValue1. When this method is called from
// ElementEmbedValueHandler or ObjectIdValueHandler,
// if the dimension of cols > 1, rval is an array of the
// same dimension. If the dimension of cols is 1, rval is null.
// If rval is not null, it is an array of objects and this array
// will be populated in toDatastoreValue1. If rval is null,
// a new value will be added to rvals in toDataStoreValue1
// and return to the caller.
List rvals = new ArrayList();
if (rval != null)
rvals.add(rval);
toDataStoreValue1(em, vm, store, cols, rvals, idx);
return rvals.get(0);
}
protected int toDataStoreValue1(OpenJPAStateManager em, ValueMapping vm,
JDBCStore store, Column[] cols, List rvals, int idx) {
// set rest of columns from fields
FieldMapping[] fms = vm.getEmbeddedMapping().getFieldMappings();
Object cval;
Column[] ecols;
Embeddable embed;
for (int i = 0; i < fms.length; i++) {
if (fms[i].getManagement() != FieldMetaData.MANAGE_PERSISTENT)
continue;
// This recursive code is mainly to deal with situations
// where an entity contains a collection of embeddableA.
// The embeddableA element in the collection contains an
// embeddableB. The parameter vm to toDataStoreValue is
// embeddableA. If some field in embeddableA is of type
// embeddableB, recursive call is required to populate the
// value for embeddableB.
ValueMapping val = fms[i].getValueMapping();
if (val.getEmbeddedMapping() != null) {
cval = (em == null) ? null : em.fetch(i);
if (cval instanceof PersistenceCapable) {
OpenJPAStateManager embedSm = (OpenJPAStateManager)
((PersistenceCapable)cval).pcGetStateManager();
idx = toDataStoreValue1(embedSm, val, store, cols, rvals,
idx);
} else if (cval instanceof ObjectIdStateManager) {
idx = toDataStoreValue1((ObjectIdStateManager)cval, val,
store, cols, rvals, idx);
} else if (cval == null) {
idx = toDataStoreValue1(null, val, store, cols, rvals, idx);
}
}
embed = (Embeddable) fms[i].getStrategy();
ecols = embed.getColumns();
if (ecols.length == 0)
continue;
cval = (em == null) ? null : getValue(embed, em, i);
cval = embed.toEmbeddedDataStoreValue(cval, store);
if (cols.length == 1) {
// rvals is empty
rvals.add(cval); // save the return value
} else if (ecols.length == 1) {
Object rval = rvals.get(0);
((Object[]) rval)[idx++] = cval;
} else {
Object rval = rvals.get(0);
System.arraycopy(cval, 0, rval, idx, ecols.length);
idx += ecols.length;
}
}
return idx;
}
private Object getValue(Embeddable embed, OpenJPAStateManager sm, int idx) {
if (embed instanceof MaxEmbeddedLobFieldStrategy) {
return ((MaxEmbeddedLobFieldStrategy)embed).getValue(sm);
}
return sm.fetch(idx);
}
/**
* Helper to convert a datastore value to its object equivalent.
*
* @param em state manager for embedded object
* @param vm owning value
* @param val datastore value
* @param store optional store manager
* @param fetch optional fetch configuration
* @param cols embedded columns
* @param idx index in columns array to start
*/
protected void toObjectValue(OpenJPAStateManager em, ValueMapping vm,
Object val, JDBCStore store, JDBCFetchConfiguration fetch,
Column[] cols, int idx)
throws SQLException {
toObjectValue1(em, vm, val, store, fetch, cols, idx);
}
protected int toObjectValue1(OpenJPAStateManager em, ValueMapping vm,
Object val, JDBCStore store, JDBCFetchConfiguration fetch,
Column[] cols, int idx)
throws SQLException {
FieldMapping[] fms = vm.getEmbeddedMapping().getFieldMappings();
Embeddable embed;
Object cval;
Column[] ecols;
for (FieldMapping fm : fms) {
if (fm.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
continue;
ValueMapping vm1 = fm.getValueMapping();
OpenJPAStateManager em1 = null;
embed = (Embeddable) fm.getStrategy();
if (vm1.getEmbeddedMapping() != null) {
if (em instanceof StateManagerImpl) {
em1 = store.getContext().embed(null, null, em, vm1);
idx = toObjectValue1(em1, vm1, val, store, fetch, cols, idx);
}
else if (em instanceof ObjectIdStateManager) {
em1 = new ObjectIdStateManager(null, null, vm1);
idx = toObjectValue1(em1, vm1, val, store, null,
getColumns(fm), idx);
}
if (em1 != null) {
cval = em1.getManagedInstance();
}
else {
cval = null;
}
}
else {
ecols = embed.getColumns();
if (ecols.length == 0)
cval = null;
else if (idx == 0 && ecols.length == cols.length)
cval = val;
else if (ecols.length == 1)
cval = ((Object[]) val)[idx++];
else {
cval = new Object[ecols.length];
System.arraycopy(val, idx, cval, 0, ecols.length);
idx += ecols.length;
}
}
if (store != null && em instanceof StateManagerImpl)
embed.loadEmbedded(em, store, fetch, cval);
else {
if (!(em instanceof ObjectIdStateManager))
cval = embed.toEmbeddedObjectValue(cval);
if (fm.getHandler() != null)
cval = fm.getHandler().toObjectValue(fm, cval);
em.store(fm.getIndex(), cval);
}
}
return idx;
}
private Column[] getColumns(FieldMapping fm) {
List<Column> colList = new ArrayList<>();
getEmbeddedIdCols(fm, colList);
Column[] cols = new Column[colList.size()];
int i = 0;
for (Column col : colList) {
cols[i++] = col;
}
return cols;
}
public static void getEmbeddedIdCols(FieldMapping fmd, List cols) {
ClassMapping embed = fmd.getEmbeddedMapping();
FieldMapping[] fmds = embed.getFieldMappings();
for (FieldMapping fieldMapping : fmds) {
if (fieldMapping.getValue().getEmbeddedMetaData() == null) {
getIdColumns(fieldMapping, cols);
}
else {
getEmbeddedIdCols(fieldMapping, cols);
}
}
}
public static void getIdColumns(FieldMapping fmd, List cols) {
Column[] pkCols = ((ValueMappingImpl)fmd.getValue()).getColumns();
for (Column pkCol : pkCols) {
Column newCol = new Column();
newCol.copy(pkCol);
cols.add(newCol);
}
}
}