blob: 6528a600955afad3eb2abd307f12d27935070328 [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.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import org.apache.openjpa.jdbc.kernel.EagerFetchModes;
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.schema.Column;
import org.apache.openjpa.jdbc.sql.Joins;
import org.apache.openjpa.jdbc.sql.Result;
import org.apache.openjpa.jdbc.sql.SQLBuffer;
import org.apache.openjpa.jdbc.sql.SQLExceptions;
import org.apache.openjpa.jdbc.sql.Select;
import org.apache.openjpa.jdbc.sql.Union;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.lib.util.Closeable;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.util.AbstractLRSProxyMap;
import org.apache.openjpa.util.InvalidStateException;
/**
* Large result set map.
*
* @author Abe White
*/
class LRSProxyMap
extends AbstractLRSProxyMap {
private static final Localizer _loc = Localizer.forPackage
(LRSProxyMap.class);
private final LRSMapFieldStrategy _strat;
public LRSProxyMap(LRSMapFieldStrategy strat) {
super(strat.getFieldMapping().getKey().getDeclaredType(),
strat.getFieldMapping().getElement().getDeclaredType());
_strat = strat;
}
@Override
protected synchronized int count() {
boolean derivedVal = _strat.getFieldMapping().getElement().
getValueMappedBy() != null;
final ClassMapping[] clss = (derivedVal)
? _strat.getIndependentKeyMappings(false)
: _strat.getIndependentValueMappings(false);
final OpenJPAStateManager sm = assertOwner();
final JDBCStore store = getStore();
Union union = store.getSQLFactory().newUnion
(Math.max(1, clss.length));
union.select(new Union.Selector() {
@Override
public void select(Select sel, int idx) {
ClassMapping cls = (clss.length == 0) ? null : clss[idx];
sel.whereForeignKey(_strat.getJoinForeignKey(cls),
sm.getObjectId(), _strat.getFieldMapping().
getDefiningMapping(), store);
}
});
try {
return union.getCount(store);
} catch (SQLException se) {
throw SQLExceptions.getStore(se, store.getDBDictionary());
}
}
@Override
protected boolean hasKey(Object key) {
return has(key, true);
}
@Override
protected boolean hasValue(Object value) {
return has(value, false);
}
private boolean has(final Object obj, final boolean key) {
final boolean derivedKey = key && _strat.getFieldMapping().
getKey().getValueMappedBy() != null;
final boolean derivedVal = !key && _strat.getFieldMapping().
getElement().getValueMappedBy() != null;
final ClassMapping[] clss = ((key && !derivedKey) || derivedVal)
? _strat.getIndependentKeyMappings(derivedVal)
: _strat.getIndependentValueMappings(derivedKey);
final OpenJPAStateManager sm = assertOwner();
final JDBCStore store = getStore();
Union union = store.getSQLFactory().newUnion
(Math.max(1, clss.length));
union.select(new Union.Selector() {
@Override
public void select(Select sel, int idx) {
ClassMapping cls = (clss.length == 0) ? null : clss[idx];
sel.whereForeignKey(_strat.getJoinForeignKey(cls),
sm.getObjectId(), _strat.getFieldMapping().
getDefiningMapping(), store);
Joins joins = null;
Column[] cols;
Object val;
if (key) {
if (derivedKey)
joins = _strat.joinValueRelation(sel.newJoins(), cls);
val = _strat.toKeyDataStoreValue(obj, store);
cols = _strat.getKeyColumns(cls);
} else {
if (derivedVal)
joins = _strat.joinKeyRelation(sel.newJoins(), cls);
val = _strat.toDataStoreValue(obj, store);
cols = _strat.getValueColumns(cls);
}
Object[] vals = (cols.length == 1) ? null : (Object[]) val;
SQLBuffer sql = new SQLBuffer(store.getDBDictionary());
for (int i = 0; i < cols.length; i++) {
if (i > 0)
sql.append(" AND ");
sql.append(sel.getColumnAlias(cols[i], joins));
if (vals == null)
sql.append((val == null) ? " IS " : " = ").
appendValue(val, cols[i]);
else
sql.append((vals[i] == null) ? " IS " : " = ").
appendValue(vals[i], cols[i]);
}
sel.where(sql, joins);
}
});
try {
return union.getCount(store) > 0;
} catch (SQLException se) {
throw SQLExceptions.getStore(se, store.getDBDictionary());
}
}
@Override
protected Collection keys(final Object obj) {
final OpenJPAStateManager sm = assertOwner();
final JDBCStore store = getStore();
if (_strat.getFieldMapping().getKey().getValueMappedBy() != null) {
Object key = _strat.deriveKey(store, obj);
if (hasKey(key))
return Collections.singleton(key);
return Collections.EMPTY_LIST;
}
final ClassMapping[] clss = _strat.getIndependentKeyMappings(true);
final JDBCFetchConfiguration fetch = store.getFetchConfiguration();
final Joins[] resJoins = new Joins[Math.max(1, clss.length)];
Union union = store.getSQLFactory().newUnion
(Math.max(1, clss.length));
if (fetch.getSubclassFetchMode(_strat.getFieldMapping().
getKeyMapping().getTypeMapping()) != EagerFetchModes.EAGER_JOIN)
union.abortUnion();
union.select(new Union.Selector() {
@Override
public void select(Select sel, int idx) {
ClassMapping cls = (clss.length == 0) ? null : clss[idx];
sel.whereForeignKey(_strat.getJoinForeignKey(cls),
sm.getObjectId(), _strat.getFieldMapping().
getDefiningMapping(), store);
if (_strat.getFieldMapping().getElement().getValueMappedBy()
!= null)
resJoins[idx] = _strat.joinKeyRelation(sel.newJoins(),
cls);
Object val = _strat.toDataStoreValue(obj, store);
Column[] cols = _strat.getValueColumns(cls);
Object[] vals = (cols.length == 1) ? null : (Object[]) val;
SQLBuffer sql = new SQLBuffer(store.getDBDictionary());
for (int i = 0; i < cols.length; i++) {
if (i > 0)
sql.append(" AND ");
sql.append(sel.getColumnAlias(cols[i]));
if (vals == null)
sql.append((val == null) ? " IS " : " = ").
appendValue(val, cols[i]);
else
sql.append((vals[i] == null) ? " IS " : " = ").
appendValue(vals[i], cols[i]);
}
sel.where(sql);
if (resJoins[idx] == null)
resJoins[idx] = _strat.joinKeyRelation(sel.newJoins(),
cls);
_strat.selectKey(sel, cls, sm, store, fetch, resJoins[idx]);
}
});
Result res = null;
Collection keys = new ArrayList(3);
try {
res = union.execute(store, fetch);
while (res.next())
keys.add(_strat.loadKey(sm, store, fetch, res,
resJoins[res.indexOf()]));
return keys;
} catch (SQLException se) {
throw SQLExceptions.getStore(se, store.getDBDictionary());
} finally {
if (res != null)
res.close();
}
}
@Override
protected Object value(final Object obj) {
final OpenJPAStateManager sm = assertOwner();
final JDBCStore store = getStore();
if (_strat.getFieldMapping().getElement().getValueMappedBy() != null) {
Object val = _strat.deriveValue(store, obj);
if (hasValue(val))
return val;
return null;
}
final JDBCFetchConfiguration fetch = store.getFetchConfiguration();
final ClassMapping[] clss = _strat.getIndependentValueMappings(true);
final Joins[] resJoins = new Joins[Math.max(1, clss.length)];
Union union = store.getSQLFactory().newUnion(Math.max(1, clss.length));
union.setExpectedResultCount(1, false);
if (fetch.getSubclassFetchMode(_strat.getFieldMapping().
getElementMapping().getTypeMapping())
!= EagerFetchModes.EAGER_JOIN)
union.abortUnion();
union.select(new Union.Selector() {
@Override
public void select(Select sel, int idx) {
ClassMapping cls = (clss.length == 0) ? null : clss[idx];
sel.whereForeignKey(_strat.getJoinForeignKey(cls),
sm.getObjectId(), _strat.getFieldMapping().
getDefiningMapping(), store);
if (_strat.getFieldMapping().getKey().getValueMappedBy()
!= null)
resJoins[idx] = _strat.joinValueRelation(sel.newJoins(),
cls);
Object key = _strat.toKeyDataStoreValue(obj, store);
Column[] cols = _strat.getKeyColumns(cls);
Object[] vals = (cols.length == 1) ? null : (Object[]) key;
SQLBuffer sql = new SQLBuffer(store.getDBDictionary());
for (int i = 0; i < cols.length; i++) {
if (i > 0)
sql.append(" AND ");
sql.append(sel.getColumnAlias(cols[i], resJoins[idx]));
if (vals == null)
sql.append((key == null) ? " IS " : " = ").
appendValue(key, cols[i]);
else
sql.append((vals[i] == null) ? " IS " : " = ").
appendValue(vals[i], cols[i]);
}
sel.where(sql, resJoins[idx]);
if (resJoins[idx] == null)
resJoins[idx] = _strat.joinValueRelation(sel.newJoins(),
cls);
_strat.selectValue(sel, cls, sm, store, fetch, resJoins[idx]);
}
});
Result res = null;
try {
res = union.execute(store, fetch);
if (res.next())
return _strat.loadValue(sm, store, fetch, res,
resJoins[res.indexOf()]);
return null;
} catch (SQLException se) {
throw SQLExceptions.getStore(se, store.getDBDictionary());
} finally {
if (res != null)
res.close();
}
}
@Override
protected Iterator itr() {
OpenJPAStateManager sm = assertOwner();
JDBCStore store = getStore();
JDBCFetchConfiguration fetch = store.getFetchConfiguration();
try {
Joins[] joins = new Joins[2];
Result[] res = _strat.getResults(sm, store, fetch, EagerFetchModes.EAGER_JOIN,
joins, true);
return new ResultIterator(sm, store, fetch, res, joins);
} catch (SQLException se) {
throw SQLExceptions.getStore(se, store.getDBDictionary());
}
}
private OpenJPAStateManager assertOwner() {
OpenJPAStateManager sm = getOwner();
if (sm == null)
throw new InvalidStateException(_loc.get("lrs-no-owner",
_strat.getFieldMapping()));
return sm;
}
private JDBCStore getStore() {
return (JDBCStore) getOwner().getContext().getStoreManager().
getInnermostDelegate();
}
/**
* Closeable iterator built around key and value JDBC results.
*/
private class ResultIterator
implements Iterator, Closeable {
private final OpenJPAStateManager _sm;
private final JDBCStore _store;
private final JDBCFetchConfiguration _fetch;
private final Result[] _res;
private final Joins[] _joins;
private Boolean _next = null;
public ResultIterator(OpenJPAStateManager sm, JDBCStore store,
JDBCFetchConfiguration fetch, Result[] res, Joins[] joins) {
_sm = sm;
_store = store;
_fetch = fetch;
_res = res;
_joins = joins;
}
@Override
public boolean hasNext() {
if (_next == null) {
try {
_next = (_res[0].next()) ? Boolean.TRUE : Boolean.FALSE;
if (_next.booleanValue() && _res[1] != _res[0])
_res[1].next();
} catch (SQLException se) {
throw SQLExceptions.getStore(se, _store.getDBDictionary());
}
}
return _next.booleanValue();
}
@Override
public Object next() {
if (!hasNext())
throw new NoSuchElementException();
_next = null;
boolean keyDerived = _strat.getFieldMapping().getKey().
getValueMappedBy() != null;
boolean valDerived = _strat.getFieldMapping().getElement().
getValueMappedBy() != null;
Entry entry = new Entry();
try {
if (!keyDerived)
entry.key = _strat.loadKey(_sm, _store, _fetch, _res[0],
_joins[0]);
if (!valDerived)
entry.val = _strat.loadValue(_sm, _store, _fetch, _res[1],
_joins[1]);
if (keyDerived)
entry.key = _strat.deriveKey(_store, entry.val);
if (valDerived)
entry.val = _strat.deriveValue(_store, entry.key);
return entry;
} catch (SQLException se) {
throw SQLExceptions.getStore(se, _store.getDBDictionary());
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void close() {
_next = Boolean.FALSE;
_res[0].close();
if (_res[1] != _res[0])
_res[1].close();
}
}
/**
* Map.Entry struct.
*/
private static class Entry
implements Map.Entry {
public Object key;
public Object val;
@Override
public Object getKey() {
return key;
}
@Override
public Object getValue() {
return val;
}
@Override
public Object setValue(Object val) {
throw new UnsupportedOperationException();
}
}
}