blob: 77dff655254c4ac16a6edbeebf4542775e2cea07 [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 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.Embeddable;
import org.apache.openjpa.jdbc.meta.Joinable;
import org.apache.openjpa.jdbc.meta.ValueMappingInfo;
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.PrimaryKey;
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.SQLBuffer;
import org.apache.openjpa.jdbc.sql.Select;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.ValueStrategies;
import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.MetaDataException;
/**
* Direct mapping from a primitive value to a column.
*
* @author Abe White
* @since 0.4.0
*/
public class PrimitiveFieldStrategy
extends AbstractFieldStrategy
implements Joinable, Embeddable {
private static final long serialVersionUID = 1L;
private static final Object NULL = new Object();
private static final Localizer _loc = Localizer.forPackage
(PrimitiveFieldStrategy.class);
private boolean _stateImage = false;
@Override
public void map(boolean adapt) {
if (field.isSerialized() || !field.getType().isPrimitive())
throw new MetaDataException(_loc.get("not-primitive", field));
assertNotMappedBy();
// map join key, if any
field.mapJoin(adapt, false);
field.getKeyMapping().getValueInfo().assertNoSchemaComponents
(field.getKey(), !adapt);
field.getElementMapping().getValueInfo().assertNoSchemaComponents
(field.getElement(), !adapt);
ValueMappingInfo vinfo = field.getValueInfo();
vinfo.assertNoJoin(field, true);
vinfo.assertNoForeignKey(field, !adapt);
// Determine whether to delimit the base field name
DBDictionary dict = field.getMappingRepository().getDBDictionary();
DBIdentifier fieldName = DBIdentifier.newColumn(field.getName(), dict != null ? dict.delimitAll() : false);
// get value columns
Column tmpCol = new Column();
tmpCol.setIdentifier(fieldName);
tmpCol.setJavaType(field.getTypeCode());
Column[] cols = vinfo.getColumns(field, fieldName,
new Column[]{ tmpCol }, field.getTable(), adapt);
if (field.getValueStrategy() == ValueStrategies.AUTOASSIGN)
cols[0].setAutoAssigned(true);
if (vinfo.isImplicitRelation())
for (Column col : cols) {
col.setImplicitRelation(true);
}
field.setColumns(cols);
field.setColumnIO(vinfo.getColumnIO());
field.mapConstraints(fieldName, adapt);
// add primary key columns to table pk if logical
field.mapPrimaryKey(adapt);
PrimaryKey pk = field.getTable().getPrimaryKey();
if (field.isPrimaryKey() && pk != null && (adapt || pk.isLogical()))
pk.addColumn(cols[0]);
// set joinable
field.getDefiningMapping().setJoinable(field.getColumns()[0], this);
}
@Override
public void initialize() {
// record whether we're using a state image indicator, which requires
// that we do special null checks when loading primitives
_stateImage = field.getDefiningMapping().getVersion().getStrategy().
getAlias().equals(StateComparisonVersionStrategy.ALIAS);
if (_stateImage)
field.setUsesImplData(null);
}
@Override
public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
throws SQLException {
if (!field.getColumnIO().isInsertable(0, false))
return;
Row row = field.getRow(sm, store, rm, Row.ACTION_INSERT);
if (row != null)
update(sm, row);
}
@Override
public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
throws SQLException {
if (!field.getColumnIO().isUpdatable(0, false))
return;
Row row = field.getRow(sm, store, rm, Row.ACTION_UPDATE);
if (row != null)
update(sm, row);
}
@Override
public void delete(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
throws SQLException {
field.deleteRow(sm, store, rm);
}
/**
* Set the value of the owning field into the given row.
*/
private void update(OpenJPAStateManager sm, Row row)
throws SQLException {
Column col = field.getColumns()[0];
switch (field.getTypeCode()) {
case JavaTypes.BOOLEAN:
row.setBoolean(col, sm.fetchBoolean(field.getIndex()));
break;
case JavaTypes.BYTE:
row.setByte(col, sm.fetchByte(field.getIndex()));
break;
case JavaTypes.CHAR:
row.setChar(col, sm.fetchChar(field.getIndex()));
break;
case JavaTypes.DOUBLE:
row.setDouble(col, sm.fetchDouble(field.getIndex()));
break;
case JavaTypes.FLOAT:
row.setFloat(col, sm.fetchFloat(field.getIndex()));
break;
case JavaTypes.INT:
row.setInt(col, sm.fetchInt(field.getIndex()));
break;
case JavaTypes.LONG:
row.setLong(col, sm.fetchLong(field.getIndex()));
break;
case JavaTypes.SHORT:
row.setShort(col, sm.fetchShort(field.getIndex()));
break;
default:
throw new InternalException();
}
}
@Override
public int supportsSelect(Select sel, int type, OpenJPAStateManager sm,
JDBCStore store, JDBCFetchConfiguration fetch) {
if (type == Select.TYPE_JOINLESS && sel.isSelected(field.getTable()))
return 1;
return 0;
}
@Override
public int select(Select sel, OpenJPAStateManager sm, JDBCStore store,
JDBCFetchConfiguration fetch, int eagerMode) {
sel.select(field.getColumns()[0], field.join(sel));
return 1;
}
@Override
public void load(OpenJPAStateManager sm, JDBCStore store,
JDBCFetchConfiguration fetch, Result res)
throws SQLException {
Column col = field.getColumns()[0];
if (!res.contains(col))
return;
int idx = field.getIndex();
boolean checkNull = _stateImage && !field.isJoinOuter();
switch (field.getTypeCode()) {
case JavaTypes.BOOLEAN:
sm.storeBoolean(idx, res.getBoolean(col));
break;
case JavaTypes.BYTE:
sm.storeByte(idx, res.getByte(col));
break;
case JavaTypes.CHAR:
sm.storeChar(idx, res.getChar(col));
break;
case JavaTypes.DOUBLE:
sm.storeDouble(idx, res.getDouble(col));
checkNull = false;
break;
case JavaTypes.FLOAT:
sm.storeFloat(idx, res.getFloat(col));
checkNull = false;
break;
case JavaTypes.INT:
sm.storeInt(idx, res.getInt(col));
break;
case JavaTypes.LONG:
sm.storeLong(idx, res.getLong(col));
break;
case JavaTypes.SHORT:
sm.storeShort(idx, res.getShort(col));
break;
default:
throw new InternalException();
}
// we're using state image versioning, so record that the actual db
// value was null so we add the correct OL check on update
if (checkNull && res.wasNull())
sm.setImplData(field.getIndex(), NULL);
}
@Override
public void appendIsNull(SQLBuffer sql, Select sel, Joins joins) {
joins = join(joins, false);
sql.append(sel.getColumnAlias(field.getColumns()[0], joins)).
append(" IS ").appendValue(null, field.getColumns()[0]);
}
@Override
public void appendIsNotNull(SQLBuffer sql, Select sel, Joins joins) {
joins = join(joins, false);
sql.append(sel.getColumnAlias(field.getColumns()[0], joins)).
append(" IS NOT ").appendValue(null, field.getColumns()[0]);
}
@Override
public Joins join(Joins joins, boolean forceOuter) {
return field.join(joins, forceOuter, false);
}
@Override
public Object loadProjection(JDBCStore store, JDBCFetchConfiguration fetch,
Result res, Joins joins)
throws SQLException {
return res.getObject(field.getColumns()[0], null, joins);
}
@Override
public boolean isVersionable() {
if (field.isJoinOuter())
return false;
switch (field.getTypeCode()) {
case JavaTypes.BOOLEAN:
case JavaTypes.BYTE:
case JavaTypes.CHAR:
case JavaTypes.INT:
case JavaTypes.LONG:
case JavaTypes.SHORT:
return true;
default:
return false;
}
}
@Override
public void where(OpenJPAStateManager sm, JDBCStore store, RowManager rm,
Object prevValue)
throws SQLException {
Row row = field.getRow(sm, store, rm, Row.ACTION_UPDATE);
if (row == null)
return;
// for primitives loaded as default vals, check to see if was null in
// the database when loaded == remove the impl data at the same time
// to be sure we don't think the value is null after the commit
Column col = field.getColumns()[0];
if (sm.setImplData(field.getIndex(), null) == NULL)
row.whereNull(col);
else
row.whereObject(col, prevValue);
}
///////////////////////////
// Joinable implementation
///////////////////////////
@Override
public int getFieldIndex() {
return field.getIndex();
}
@Override
public Object getPrimaryKeyValue(Result res, Column[] cols, ForeignKey fk,
JDBCStore store, Joins joins)
throws SQLException {
Column col = cols[0];
if (fk != null)
col = fk.getColumn(col);
return JavaTypes.convert(res.getObject(col, null, joins),
field.getTypeCode());
}
@Override
public Column[] getColumns() {
return field.getColumns();
}
@Override
public Object getJoinValue(Object fieldVal, Column col, JDBCStore store) {
return fieldVal;
}
@Override
public Object getJoinValue(OpenJPAStateManager sm, Column col,
JDBCStore store) {
return sm.fetch(field.getIndex());
}
@Override
public void setAutoAssignedValue(OpenJPAStateManager sm, JDBCStore store,
Column col, Object autoInc) {
int idx = field.getIndex();
switch (field.getTypeCode()) {
case JavaTypes.BOOLEAN:
if (autoInc == null)
sm.storeBoolean(idx, false);
else if (autoInc instanceof Boolean)
sm.storeBoolean(idx, (Boolean) autoInc);
else
sm.storeBoolean(idx, ((Number) autoInc).intValue() != 0);
break;
case JavaTypes.BYTE:
if (autoInc == null)
sm.storeByte(idx, (byte) 0);
else
sm.storeByte(idx, ((Number) autoInc).byteValue());
break;
case JavaTypes.CHAR:
if (autoInc == null)
sm.storeChar(idx, (char) 0);
else if (autoInc instanceof Character)
sm.storeChar(idx, (Character) autoInc);
else if (autoInc instanceof String)
sm.storeChar(idx, ((String) autoInc).charAt(0));
else
sm.storeChar(idx, (char) ((Number) autoInc).intValue());
break;
case JavaTypes.DOUBLE:
if (autoInc == null)
sm.storeDouble(idx, 0D);
else
sm.storeDouble(idx, ((Number) autoInc).doubleValue());
break;
case JavaTypes.FLOAT:
if (autoInc == null)
sm.storeFloat(idx, 0F);
else
sm.storeFloat(idx, ((Number) autoInc).floatValue());
break;
case JavaTypes.INT:
if (autoInc == null)
sm.storeInt(idx, 0);
else
sm.storeInt(idx, ((Number) autoInc).intValue());
break;
case JavaTypes.LONG:
if (autoInc == null)
sm.storeLong(idx, 0L);
else
sm.storeLong(idx, ((Number) autoInc).longValue());
break;
case JavaTypes.SHORT:
if (autoInc == null)
sm.storeShort(idx, (short) 0);
else
sm.storeShort(idx, ((Number) autoInc).shortValue());
break;
default:
throw new InternalException();
}
}
/////////////////////////////
// Embeddable implementation
/////////////////////////////
@Override
public ColumnIO getColumnIO() {
return field.getColumnIO();
}
@Override
public Object[] getResultArguments() {
return null;
}
@Override
public Object toEmbeddedObjectValue(Object val) {
return val;
}
@Override
public Object toEmbeddedDataStoreValue(Object val, JDBCStore store) {
return toDataStoreValue(val, store);
}
@Override
public void loadEmbedded(OpenJPAStateManager sm, JDBCStore store,
JDBCFetchConfiguration fetch, Object val)
throws SQLException {
sm.store(field.getIndex(), val);
}
}