| /* |
| * 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.ClassMapping; |
| import org.apache.openjpa.jdbc.meta.RelationId; |
| import org.apache.openjpa.jdbc.meta.ValueHandler; |
| import org.apache.openjpa.jdbc.meta.ValueMapping; |
| 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.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.kernel.OpenJPAStateManager; |
| import org.apache.openjpa.lib.util.Localizer; |
| import org.apache.openjpa.util.InvalidStateException; |
| |
| /** |
| * Utility methods for strategies using value handlers. |
| * |
| * @author Abe White |
| * @since 0.4.0 |
| */ |
| public class HandlerStrategies { |
| |
| private static final Localizer _loc = Localizer.forPackage |
| (HandlerStrategies.class); |
| |
| /** |
| * Map the given value. |
| */ |
| public static Column[] map(ValueMapping vm, String name, ColumnIO io, |
| boolean adapt) { |
| ValueMappingInfo vinfo = vm.getValueInfo(); |
| vinfo.assertNoJoin(vm, true); |
| vinfo.assertNoForeignKey(vm, !adapt); |
| |
| DBDictionary dict = vm.getMappingRepository().getDBDictionary(); |
| DBIdentifier colName = DBIdentifier.newColumn(name, dict != null ? dict.delimitAll() : false); |
| Column[] cols = vm.getHandler().map(vm, colName.getName(), io, adapt); |
| if (cols.length > 0 && cols[0].getTable() == null) { |
| cols = vinfo.getColumns(vm, colName, cols, |
| vm.getFieldMapping().getTable(), adapt); |
| if (vinfo.isImplicitRelation()) { |
| for (int i = 0; i < cols.length; i++) { |
| cols[i].setImplicitRelation(true); |
| } |
| } |
| ColumnIO mappedIO = vinfo.getColumnIO(); |
| vm.setColumns(cols); |
| vm.setColumnIO(mappedIO); |
| if (mappedIO != null) { |
| for (int i = 0; i < cols.length; i++) { |
| io.setInsertable(i, mappedIO.isInsertable(i, false)); |
| io.setNullInsertable(i, mappedIO.isInsertable(i, true)); |
| io.setUpdatable(i, mappedIO.isUpdatable(i, false)); |
| io.setNullUpdatable(i, mappedIO.isUpdatable(i, true)); |
| } |
| } |
| } |
| vm.mapConstraints(colName, adapt); |
| return cols; |
| } |
| |
| /** |
| * Set the given value into the given row. |
| * Return false if the given value can not be set, for example, due to |
| * null constraints on the columns. |
| */ |
| public static boolean set(ValueMapping vm, Object val, JDBCStore store, |
| Row row, Column[] cols, ColumnIO io, boolean nullNone) |
| throws SQLException { |
| if (!canSetAny(row, io, cols)) |
| return false; |
| |
| ValueHandler handler = vm.getHandler(); |
| val = handler.toDataStoreValue(vm, val, store); |
| boolean isSet = false; |
| if (val == null) { |
| for (int i = 0; i < cols.length; i++) |
| if (canSet(row, io, i, true)) { |
| isSet = true; |
| set(row, cols[i], null, handler, nullNone); |
| } |
| } else if (cols.length == 1) { |
| if (canSet(row, io, 0, val == null)) { |
| isSet = true; |
| set(row, cols[0], val, handler, nullNone); |
| } |
| } else { |
| Object[] vals = (Object[]) val; |
| for (int i = 0; i < vals.length; i++) |
| if (canSet(row, io, i, vals[i] == null)) { |
| isSet = true; |
| set(row, cols[i], vals[i], handler, nullNone); |
| } |
| } |
| return isSet; |
| } |
| |
| /** |
| * Return true if the given column index is settable. |
| */ |
| private static boolean canSet(Row row, ColumnIO io, int i, |
| boolean nullValue) { |
| if (row.getAction() == Row.ACTION_INSERT) |
| return io.isInsertable(i, nullValue); |
| if (row.getAction() == Row.ACTION_UPDATE) |
| return io.isUpdatable(i, nullValue); |
| return true; |
| } |
| |
| /** |
| * Return true if the any column up to the given index is settable. |
| */ |
| private static boolean canSetAny(Row row, ColumnIO io, Column[] cols) { |
| if (row.getAction() == Row.ACTION_INSERT) |
| return io.isAnyInsertable(cols, false); |
| if (row.getAction() == Row.ACTION_UPDATE) |
| return io.isAnyUpdatable(cols, false); |
| return true; |
| } |
| |
| /** |
| * Set a value into a row, taking care not to override column defaults |
| * with nulls unless the user wants us to. |
| */ |
| private static void set(Row row, Column col, Object val, |
| ValueHandler handler, boolean nullNone) |
| throws SQLException { |
| if (val == null) |
| row.setNull(col, nullNone); |
| else if (col.isRelationId() && handler instanceof RelationId) |
| row.setRelationId(col, (OpenJPAStateManager) val, |
| (RelationId) handler); |
| else |
| row.setObject(col, val); |
| } |
| |
| /** |
| * Add where conditions to the given row. |
| */ |
| public static void where(ValueMapping vm, Object val, JDBCStore store, |
| Row row, Column[] cols) |
| throws SQLException { |
| if (cols.length == 0) |
| return; |
| |
| val = toDataStoreValue(vm, val, cols, store); |
| if (val == null) |
| for (int i = 0; i < cols.length; i++) |
| row.whereNull(cols[i]); |
| else if (cols.length == 1) |
| where(row, cols[0], val); |
| else { |
| Object[] vals = (Object[]) val; |
| for (int i = 0; i < vals.length; i++) |
| where(row, cols[i], vals[i]); |
| } |
| } |
| |
| /** |
| * Set a where condition on the given row. |
| */ |
| private static void where(Row row, Column col, Object val) |
| throws SQLException { |
| if (val == null) |
| row.whereNull(col); |
| else |
| row.whereObject(col, val); |
| } |
| |
| /** |
| * Load the Object value from the given result. |
| */ |
| public static Object loadObject(ValueMapping vm, OpenJPAStateManager sm, |
| JDBCStore store, JDBCFetchConfiguration fetch, Result res, |
| Joins joins, Column[] cols, boolean objectValueRequiresLoad) |
| throws SQLException { |
| if (cols.length == 0) |
| throw new InvalidStateException(_loc.get("cant-project-owned", |
| vm)); |
| |
| Object val = loadDataStore(vm, res, joins, cols); |
| if (objectValueRequiresLoad) |
| return vm.getHandler().toObjectValue(vm, val, sm, store, fetch); |
| return vm.getHandler().toObjectValue(vm, val); |
| } |
| |
| /** |
| * Load the datastore value from the given result. This method does |
| * <b>not</b> process the loaded value through |
| * {@link ValueHandler#toObjectValue}. |
| */ |
| public static Object loadDataStore(ValueMapping vm, Result res, |
| Joins joins, Column[] cols) |
| throws SQLException { |
| if (cols.length == 0) |
| return null; |
| if (cols.length == 1) |
| return res.getObject(cols[0], vm.getHandler(). |
| getResultArgument(vm), joins); |
| |
| Object[] vals = new Object[cols.length]; |
| Object[] args = (Object[]) vm.getHandler().getResultArgument(vm); |
| for (int i = 0; i < cols.length; i++) |
| vals[i] = res.getObject(cols[i], (args == null) ? null : args[i], |
| joins); |
| return vals; |
| } |
| |
| /** |
| * Convert the given object to its datastore value(s). Relation ids are |
| * converted to their final values immediately. |
| */ |
| public static Object toDataStoreValue(ValueMapping vm, Object val, |
| Column[] cols, JDBCStore store) { |
| ValueHandler handler = vm.getHandler(); |
| val = handler.toDataStoreValue(vm, val, store); |
| if (val == null) { |
| if (cols.length > 1) |
| return new Object[cols.length]; |
| return null; |
| } |
| |
| // relation ids are returned as state managers; resolve the final |
| // datastore value immediately |
| Object[] vals; |
| for (int i = 0; i < cols.length; i++) { |
| if (!cols[i].isRelationId()) |
| continue; |
| if (!(handler instanceof RelationId)) |
| break; |
| if (cols.length == 1) { |
| val = ((RelationId) handler).toRelationDataStoreValue |
| ((OpenJPAStateManager) val, cols[i]); |
| } else { |
| vals = (Object[]) val; |
| vals[i] = ((RelationId) handler).toRelationDataStoreValue |
| ((OpenJPAStateManager) vals[i], cols[i]); |
| } |
| } |
| return val; |
| } |
| |
| /** |
| * Throw the proper exception if the given handler-controlled value |
| * represents an unjoinable relation. |
| */ |
| public static void assertJoinable(ValueMapping vm) { |
| ClassMapping rel = vm.getTypeMapping(); |
| if (rel != null && (rel.getTable() == null |
| || !rel.getTable().equals(vm.getFieldMapping().getTable()))) |
| throw RelationStrategies.unjoinable(vm); |
| } |
| } |