001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.jdbc.meta.strats;
020:
021: import java.sql.SQLException;
022:
023: import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
024: import org.apache.openjpa.jdbc.kernel.JDBCStore;
025: import org.apache.openjpa.jdbc.meta.Embeddable;
026: import org.apache.openjpa.jdbc.meta.Joinable;
027: import org.apache.openjpa.jdbc.meta.ValueMappingInfo;
028: import org.apache.openjpa.jdbc.schema.Column;
029: import org.apache.openjpa.jdbc.schema.ColumnIO;
030: import org.apache.openjpa.jdbc.schema.ForeignKey;
031: import org.apache.openjpa.jdbc.schema.PrimaryKey;
032: import org.apache.openjpa.jdbc.sql.Joins;
033: import org.apache.openjpa.jdbc.sql.Result;
034: import org.apache.openjpa.jdbc.sql.Row;
035: import org.apache.openjpa.jdbc.sql.RowManager;
036: import org.apache.openjpa.jdbc.sql.SQLBuffer;
037: import org.apache.openjpa.jdbc.sql.Select;
038: import org.apache.openjpa.kernel.OpenJPAStateManager;
039: import org.apache.openjpa.lib.util.Localizer;
040: import org.apache.openjpa.meta.JavaTypes;
041: import org.apache.openjpa.meta.ValueStrategies;
042: import org.apache.openjpa.util.InternalException;
043: import org.apache.openjpa.util.MetaDataException;
044:
045: /**
046: * Direct mapping from a primitive value to a column.
047: *
048: * @author Abe White
049: * @since 0.4.0
050: */
051: public class PrimitiveFieldStrategy extends AbstractFieldStrategy
052: implements Joinable, Embeddable {
053:
054: private static final Object NULL = new Object();
055:
056: private static final Localizer _loc = Localizer
057: .forPackage(PrimitiveFieldStrategy.class);
058:
059: private boolean _stateImage = false;
060:
061: public void map(boolean adapt) {
062: if (field.isSerialized() || !field.getType().isPrimitive())
063: throw new MetaDataException(_loc
064: .get("not-primitive", field));
065: assertNotMappedBy();
066:
067: // map join key, if any
068: field.mapJoin(adapt, false);
069: field.getKeyMapping().getValueInfo().assertNoSchemaComponents(
070: field.getKey(), !adapt);
071: field.getElementMapping().getValueInfo()
072: .assertNoSchemaComponents(field.getElement(), !adapt);
073:
074: ValueMappingInfo vinfo = field.getValueInfo();
075: vinfo.assertNoJoin(field, true);
076: vinfo.assertNoForeignKey(field, !adapt);
077:
078: // get value columns
079: Column tmpCol = new Column();
080: tmpCol.setName(field.getName());
081: tmpCol.setJavaType(field.getTypeCode());
082:
083: Column[] cols = vinfo.getColumns(field, field.getName(),
084: new Column[] { tmpCol }, field.getTable(), adapt);
085: if (field.getValueStrategy() == ValueStrategies.AUTOASSIGN)
086: cols[0].setAutoAssigned(true);
087:
088: field.setColumns(cols);
089: field.setColumnIO(vinfo.getColumnIO());
090: field.mapConstraints(field.getName(), adapt);
091:
092: // add primary key columns to table pk if logical
093: field.mapPrimaryKey(adapt);
094: PrimaryKey pk = field.getTable().getPrimaryKey();
095: if (field.isPrimaryKey() && pk != null
096: && (adapt || pk.isLogical()))
097: pk.addColumn(cols[0]);
098:
099: // set joinable
100: field.getDefiningMapping().setJoinable(field.getColumns()[0],
101: this );
102: }
103:
104: public void initialize() {
105: // record whether we're using a state image indicator, which requires
106: // that we do special null checks when loading primitives
107: _stateImage = field.getDefiningMapping().getVersion()
108: .getStrategy().getAlias().equals(
109: StateComparisonVersionStrategy.ALIAS);
110: if (_stateImage)
111: field.setUsesImplData(null);
112: }
113:
114: public void insert(OpenJPAStateManager sm, JDBCStore store,
115: RowManager rm) throws SQLException {
116: if (!field.getColumnIO().isInsertable(0, false))
117: return;
118: Row row = field.getRow(sm, store, rm, Row.ACTION_INSERT);
119: if (row != null)
120: update(sm, row);
121: }
122:
123: public void update(OpenJPAStateManager sm, JDBCStore store,
124: RowManager rm) throws SQLException {
125: if (!field.getColumnIO().isUpdatable(0, false))
126: return;
127: Row row = field.getRow(sm, store, rm, Row.ACTION_UPDATE);
128: if (row != null)
129: update(sm, row);
130: }
131:
132: public void delete(OpenJPAStateManager sm, JDBCStore store,
133: RowManager rm) throws SQLException {
134: field.deleteRow(sm, store, rm);
135: }
136:
137: /**
138: * Set the value of the owning field into the given row.
139: */
140: private void update(OpenJPAStateManager sm, Row row)
141: throws SQLException {
142: Column col = field.getColumns()[0];
143: switch (field.getTypeCode()) {
144: case JavaTypes.BOOLEAN:
145: row.setBoolean(col, sm.fetchBoolean(field.getIndex()));
146: break;
147: case JavaTypes.BYTE:
148: row.setByte(col, sm.fetchByte(field.getIndex()));
149: break;
150: case JavaTypes.CHAR:
151: row.setChar(col, sm.fetchChar(field.getIndex()));
152: break;
153: case JavaTypes.DOUBLE:
154: row.setDouble(col, sm.fetchDouble(field.getIndex()));
155: break;
156: case JavaTypes.FLOAT:
157: row.setFloat(col, sm.fetchFloat(field.getIndex()));
158: break;
159: case JavaTypes.INT:
160: row.setInt(col, sm.fetchInt(field.getIndex()));
161: break;
162: case JavaTypes.LONG:
163: row.setLong(col, sm.fetchLong(field.getIndex()));
164: break;
165: case JavaTypes.SHORT:
166: row.setShort(col, sm.fetchShort(field.getIndex()));
167: break;
168: default:
169: throw new InternalException();
170: }
171: }
172:
173: public int supportsSelect(Select sel, int type,
174: OpenJPAStateManager sm, JDBCStore store,
175: JDBCFetchConfiguration fetch) {
176: if (type == Select.TYPE_JOINLESS
177: && sel.isSelected(field.getTable()))
178: return 1;
179: return 0;
180: }
181:
182: public int select(Select sel, OpenJPAStateManager sm,
183: JDBCStore store, JDBCFetchConfiguration fetch, int eagerMode) {
184: sel.select(field.getColumns()[0], field.join(sel));
185: return 1;
186: }
187:
188: public void load(OpenJPAStateManager sm, JDBCStore store,
189: JDBCFetchConfiguration fetch, Result res)
190: throws SQLException {
191: Column col = field.getColumns()[0];
192: if (!res.contains(col))
193: return;
194:
195: int idx = field.getIndex();
196: boolean checkNull = _stateImage && !field.isJoinOuter();
197: switch (field.getTypeCode()) {
198: case JavaTypes.BOOLEAN:
199: sm.storeBoolean(idx, res.getBoolean(col));
200: break;
201: case JavaTypes.BYTE:
202: sm.storeByte(idx, res.getByte(col));
203: break;
204: case JavaTypes.CHAR:
205: sm.storeChar(idx, res.getChar(col));
206: break;
207: case JavaTypes.DOUBLE:
208: sm.storeDouble(idx, res.getDouble(col));
209: checkNull = false;
210: break;
211: case JavaTypes.FLOAT:
212: sm.storeFloat(idx, res.getFloat(col));
213: checkNull = false;
214: break;
215: case JavaTypes.INT:
216: sm.storeInt(idx, res.getInt(col));
217: break;
218: case JavaTypes.LONG:
219: sm.storeLong(idx, res.getLong(col));
220: break;
221: case JavaTypes.SHORT:
222: sm.storeShort(idx, res.getShort(col));
223: break;
224: default:
225: throw new InternalException();
226: }
227:
228: // we're using state image versioning, so record that the actual db
229: // value was null so we add the correct OL check on update
230: if (checkNull && res.wasNull())
231: sm.setImplData(field.getIndex(), NULL);
232: }
233:
234: public void appendIsNull(SQLBuffer sql, Select sel, Joins joins) {
235: joins = join(joins, false);
236: sql.append(sel.getColumnAlias(field.getColumns()[0], joins))
237: .append(" IS ")
238: .appendValue(null, field.getColumns()[0]);
239: }
240:
241: public void appendIsNotNull(SQLBuffer sql, Select sel, Joins joins) {
242: joins = join(joins, false);
243: sql.append(sel.getColumnAlias(field.getColumns()[0], joins))
244: .append(" IS NOT ").appendValue(null,
245: field.getColumns()[0]);
246: }
247:
248: public Joins join(Joins joins, boolean forceOuter) {
249: return field.join(joins, forceOuter, false);
250: }
251:
252: public Object loadProjection(JDBCStore store,
253: JDBCFetchConfiguration fetch, Result res, Joins joins)
254: throws SQLException {
255: return res.getObject(field.getColumns()[0], null, joins);
256: }
257:
258: public boolean isVersionable() {
259: if (field.isJoinOuter())
260: return false;
261: switch (field.getTypeCode()) {
262: case JavaTypes.BOOLEAN:
263: case JavaTypes.BYTE:
264: case JavaTypes.CHAR:
265: case JavaTypes.INT:
266: case JavaTypes.LONG:
267: case JavaTypes.SHORT:
268: return true;
269: default:
270: return false;
271: }
272: }
273:
274: public void where(OpenJPAStateManager sm, JDBCStore store,
275: RowManager rm, Object prevValue) throws SQLException {
276: Row row = field.getRow(sm, store, rm, Row.ACTION_UPDATE);
277: if (row == null)
278: return;
279:
280: // for primitives loaded as default vals, check to see if was null in
281: // the database when loaded == remove the impl data at the same time
282: // to be sure we don't think the value is null after the commit
283: Column col = field.getColumns()[0];
284: if (sm.setImplData(field.getIndex(), null) == NULL)
285: row.whereNull(col);
286: else
287: row.whereObject(col, prevValue);
288: }
289:
290: ///////////////////////////
291: // Joinable implementation
292: ///////////////////////////
293:
294: public int getFieldIndex() {
295: return field.getIndex();
296: }
297:
298: public Object getPrimaryKeyValue(Result res, Column[] cols,
299: ForeignKey fk, JDBCStore store, Joins joins)
300: throws SQLException {
301: Column col = cols[0];
302: if (fk != null)
303: col = fk.getColumn(col);
304: return JavaTypes.convert(res.getObject(col, null, joins), field
305: .getTypeCode());
306: }
307:
308: public Column[] getColumns() {
309: return field.getColumns();
310: }
311:
312: public Object getJoinValue(Object fieldVal, Column col,
313: JDBCStore store) {
314: return fieldVal;
315: }
316:
317: public Object getJoinValue(OpenJPAStateManager sm, Column col,
318: JDBCStore store) {
319: return sm.fetch(field.getIndex());
320: }
321:
322: public void setAutoAssignedValue(OpenJPAStateManager sm,
323: JDBCStore store, Column col, Object autoInc) {
324: int idx = field.getIndex();
325: switch (field.getTypeCode()) {
326: case JavaTypes.BOOLEAN:
327: if (autoInc == null)
328: sm.storeBoolean(idx, false);
329: else if (autoInc instanceof Boolean)
330: sm
331: .storeBoolean(idx, ((Boolean) autoInc)
332: .booleanValue());
333: else
334: sm
335: .storeBoolean(idx, ((Number) autoInc)
336: .intValue() != 0);
337: break;
338: case JavaTypes.BYTE:
339: if (autoInc == null)
340: sm.storeByte(idx, (byte) 0);
341: else
342: sm.storeByte(idx, ((Number) autoInc).byteValue());
343: break;
344: case JavaTypes.CHAR:
345: if (autoInc == null)
346: sm.storeChar(idx, (char) 0);
347: else if (autoInc instanceof Character)
348: sm.storeChar(idx, ((Character) autoInc).charValue());
349: else if (autoInc instanceof String)
350: sm.storeChar(idx, ((String) autoInc).charAt(0));
351: else
352: sm.storeChar(idx, (char) ((Number) autoInc).intValue());
353: break;
354: case JavaTypes.DOUBLE:
355: if (autoInc == null)
356: sm.storeDouble(idx, 0D);
357: else
358: sm.storeDouble(idx, ((Number) autoInc).doubleValue());
359: break;
360: case JavaTypes.FLOAT:
361: if (autoInc == null)
362: sm.storeFloat(idx, 0F);
363: else
364: sm.storeFloat(idx, ((Number) autoInc).floatValue());
365: break;
366: case JavaTypes.INT:
367: if (autoInc == null)
368: sm.storeInt(idx, 0);
369: else
370: sm.storeInt(idx, ((Number) autoInc).intValue());
371: break;
372: case JavaTypes.LONG:
373: if (autoInc == null)
374: sm.storeLong(idx, 0L);
375: else
376: sm.storeLong(idx, ((Number) autoInc).longValue());
377: break;
378: case JavaTypes.SHORT:
379: if (autoInc == null)
380: sm.storeShort(idx, (short) 0);
381: else
382: sm.storeShort(idx, ((Number) autoInc).shortValue());
383: break;
384: default:
385: throw new InternalException();
386: }
387: }
388:
389: /////////////////////////////
390: // Embeddable implementation
391: /////////////////////////////
392:
393: public ColumnIO getColumnIO() {
394: return field.getColumnIO();
395: }
396:
397: public Object[] getResultArguments() {
398: return null;
399: }
400:
401: public Object toEmbeddedObjectValue(Object val) {
402: return val;
403: }
404:
405: public Object toEmbeddedDataStoreValue(Object val, JDBCStore store) {
406: return toDataStoreValue(val, store);
407: }
408:
409: public void loadEmbedded(OpenJPAStateManager sm, JDBCStore store,
410: JDBCFetchConfiguration fetch, Object val)
411: throws SQLException {
412: sm.store(field.getIndex(), val);
413: }
414: }
|