blob: 53ad0ffd41059b7b71b8874da0cbd0c1c6684c16 [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.kernel;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.meta.ClassMapping;
import org.apache.openjpa.jdbc.sql.JoinSyntaxes;
import org.apache.openjpa.kernel.FetchConfiguration;
import org.apache.openjpa.kernel.FetchConfigurationImpl;
import org.apache.openjpa.kernel.StoreContext;
import org.apache.openjpa.lib.rop.EagerResultList;
import org.apache.openjpa.lib.rop.ListResultObjectProvider;
import org.apache.openjpa.lib.rop.ResultList;
import org.apache.openjpa.lib.rop.ResultObjectProvider;
import org.apache.openjpa.lib.rop.SimpleResultList;
import org.apache.openjpa.lib.rop.SoftRandomAccessResultList;
import org.apache.openjpa.lib.rop.WindowResultList;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.StringUtil;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.util.UserException;
/**
* JDBC extensions to OpenJPA's {@link FetchConfiguration}.
*
* @author Abe White
*/
public class JDBCFetchConfigurationImpl
extends FetchConfigurationImpl
implements JDBCFetchConfiguration {
private static final long serialVersionUID = 1L;
private static final Localizer _loc = Localizer.forPackage(JDBCFetchConfigurationImpl.class);
/**
* Hint keys that correspond to a mutable bean-style setter in this receiver.
* These keys are registered with both <code>openjpa.FetchPlan</code> and <code>openjpa.jdbc</code> as prefix.
* <br>
* A hint without a setter method is also recognized by this receiver.
*/
static {
String[] prefixes = {"openjpa.FetchPlan", "openjpa.jdbc"};
Class<?> target = JDBCFetchConfiguration.class;
populateHintSetter(target, "EagerFetchMode", int.class, prefixes);
populateHintSetter(target, "FetchDirection", int.class, prefixes);
populateHintSetter(target, "Isolation", int.class, prefixes);
populateHintSetter(target, "setIsolation", "TransactionIsolation", int.class, "openjpa.jdbc");
populateHintSetter(target, "JoinSyntax", int.class, prefixes);
populateHintSetter(target, "SubclassFetchMode", int.class, prefixes);
populateHintSetter(target, "LRSSize", int.class, prefixes);
populateHintSetter(target, "setLRSSize", "LRSSizeAlgorithm", int.class, prefixes);
populateHintSetter(target, "ResultSetType", int.class, prefixes);
}
/**
* Configurable JDBC state shared throughout a traversal chain.
*/
protected static class JDBCConfigurationState implements Serializable {
private static final long serialVersionUID = 1L;
public int eagerMode = 0;
public int subclassMode = 0;
public int type = 0;
public int direction = 0;
public int size = 0;
public int syntax = 0;
public Set<String> joins = null;
public Set<String> fetchInnerJoins = null;
public int isolationLevel = -1;
public boolean ignoreDfgForFkSelect = false;
}
protected final JDBCConfigurationState _state;
public JDBCFetchConfigurationImpl() {
this(null, null);
}
protected JDBCFetchConfigurationImpl(ConfigurationState state,
JDBCConfigurationState jstate) {
super(state);
_state = (jstate == null) ? new JDBCConfigurationState() : jstate;
}
@Override
protected FetchConfigurationImpl newInstance(ConfigurationState state) {
JDBCConfigurationState jstate = (state == null) ? null : _state;
return new JDBCFetchConfigurationImpl(state, jstate);
}
@Override
public void setContext(StoreContext ctx) {
super.setContext(ctx);
JDBCConfiguration conf = getJDBCConfiguration();
if (conf == null)
return;
setEagerFetchMode(conf.getEagerFetchModeConstant());
setSubclassFetchMode(conf.getSubclassFetchModeConstant());
setResultSetType(conf.getResultSetTypeConstant());
setFetchDirection(conf.getFetchDirectionConstant());
setLRSSize(conf.getLRSSizeConstant());
setJoinSyntax(conf.getDBDictionaryInstance().joinSyntax);
}
@Override
public void copy(FetchConfiguration fetch) {
super.copy(fetch);
JDBCFetchConfiguration jf = (JDBCFetchConfiguration) fetch;
setEagerFetchMode(jf.getEagerFetchMode());
setSubclassFetchMode(jf.getSubclassFetchMode());
setResultSetType(jf.getResultSetType());
setFetchDirection(jf.getFetchDirection());
setLRSSize(jf.getLRSSize());
setJoinSyntax(jf.getJoinSyntax());
addJoins(jf.getJoins());
setIgnoreDfgForFkSelect(jf.getIgnoreDfgForFkSelect());
}
@Override
public boolean getIgnoreDfgForFkSelect() {
return _state.ignoreDfgForFkSelect;
}
@Override
public void setIgnoreDfgForFkSelect(boolean b) {
_state.ignoreDfgForFkSelect = b;
}
@Override
public int getEagerFetchMode() {
return _state.eagerMode;
}
@Override
public JDBCFetchConfiguration setEagerFetchMode(int mode) {
if (mode != DEFAULT
&& mode != EagerFetchModes.EAGER_NONE
&& mode != EagerFetchModes.EAGER_JOIN
&& mode != EagerFetchModes.EAGER_PARALLEL)
throw new IllegalArgumentException(_loc.get("bad-fetch-mode", mode).getMessage());
if (mode == DEFAULT) {
JDBCConfiguration conf = getJDBCConfiguration();
if (conf != null)
mode = conf.getEagerFetchModeConstant();
}
if (mode != DEFAULT)
_state.eagerMode = mode;
return this;
}
@Override
public int getSubclassFetchMode() {
return _state.subclassMode;
}
@Override
public int getSubclassFetchMode(ClassMapping cls) {
if (cls == null)
return _state.subclassMode;
int mode = cls.getSubclassFetchMode();
if (mode == DEFAULT)
return _state.subclassMode;
return Math.min(mode, _state.subclassMode);
}
@Override
public JDBCFetchConfiguration setSubclassFetchMode(int mode) {
if (mode != DEFAULT
&& mode != EagerFetchModes.EAGER_NONE
&& mode != EagerFetchModes.EAGER_JOIN
&& mode != EagerFetchModes.EAGER_PARALLEL)
throw new IllegalArgumentException(_loc.get("bad-fetch-mode", mode).getMessage());
if (mode == DEFAULT) {
JDBCConfiguration conf = getJDBCConfiguration();
if (conf != null)
mode = conf.getSubclassFetchModeConstant();
}
if (mode != DEFAULT)
_state.subclassMode = mode;
return this;
}
@Override
public int getResultSetType() {
return _state.type;
}
@Override
public JDBCFetchConfiguration setResultSetType(int type) {
if (type != DEFAULT
&& type != ResultSet.TYPE_FORWARD_ONLY
&& type != ResultSet.TYPE_SCROLL_INSENSITIVE
&& type != ResultSet.TYPE_SCROLL_SENSITIVE)
throw new IllegalArgumentException(_loc.get("bad-resultset-type", type).getMessage());
if (type == DEFAULT) {
JDBCConfiguration conf = getJDBCConfiguration();
if (conf != null)
_state.type = conf.getResultSetTypeConstant();
} else
_state.type = type;
return this;
}
@Override
public int getFetchDirection() {
return _state.direction;
}
@Override
public JDBCFetchConfiguration setFetchDirection(int direction) {
if (direction != DEFAULT
&& direction != ResultSet.FETCH_FORWARD
&& direction != ResultSet.FETCH_REVERSE
&& direction != ResultSet.FETCH_UNKNOWN)
throw new IllegalArgumentException(_loc.get("bad-fetch-direction", direction)
.getMessage());
if (direction == DEFAULT) {
JDBCConfiguration conf = getJDBCConfiguration();
if (conf != null)
_state.direction = conf.getFetchDirectionConstant();
} else
_state.direction = direction;
return this;
}
@Override
public int getLRSSize() {
return _state.size;
}
@Override
public JDBCFetchConfiguration setLRSSize(int size) {
if (size != DEFAULT
&& size != LRSSizes.SIZE_QUERY
&& size != LRSSizes.SIZE_LAST
&& size != LRSSizes.SIZE_UNKNOWN)
throw new IllegalArgumentException(_loc.get("bad-lrs-size", size).getMessage());
if (size == DEFAULT) {
JDBCConfiguration conf = getJDBCConfiguration();
if (conf != null)
_state.size = conf.getLRSSizeConstant();
} else
_state.size = size;
return this;
}
@Override
public int getJoinSyntax() {
return _state.syntax;
}
@Override
public JDBCFetchConfiguration setJoinSyntax(int syntax) {
if (syntax != DEFAULT
&& syntax != JoinSyntaxes.SYNTAX_SQL92
&& syntax != JoinSyntaxes.SYNTAX_TRADITIONAL
&& syntax != JoinSyntaxes.SYNTAX_DATABASE)
throw new IllegalArgumentException(_loc.get("bad-join-syntax", syntax).getMessage());
if (syntax == DEFAULT) {
JDBCConfiguration conf = getJDBCConfiguration();
if (conf != null)
_state.syntax = conf.getDBDictionaryInstance().joinSyntax;
} else
_state.syntax = syntax;
return this;
}
@Override
public ResultList<?> newResultList(ResultObjectProvider rop) {
// if built around a list, just use a simple wrapper
if (rop instanceof ListResultObjectProvider)
return new SimpleResultList(rop);
// if built around a paging list, use a window provider with the
// same window size
if (rop instanceof PagingResultObjectProvider)
return new WindowResultList(rop, ((PagingResultObjectProvider)
rop).getPageSize());
// if fetch size < 0 just read in all results immediately
if (getFetchBatchSize() < 0)
return new EagerResultList(rop);
// if foward only or forward direction use a forward window
if (_state.type == ResultSet.TYPE_FORWARD_ONLY
|| _state.direction == ResultSet.FETCH_FORWARD
|| !rop.supportsRandomAccess()) {
if (getFetchBatchSize() > 0 && getFetchBatchSize() <= 50)
return new WindowResultList(rop, getFetchBatchSize());
return new WindowResultList(rop, 50);
}
// if skipping around use a caching random access list
if (_state.direction == ResultSet.FETCH_UNKNOWN)
return new SoftRandomAccessResultList(rop);
// scrolling reverse... just use non-caching simple result list
return new SimpleResultList(rop);
}
@Override
public Set<String> getJoins() {
if (_state.joins == null)
return Collections.emptySet();
return _state.joins;
}
@Override
public boolean hasJoin(String field) {
return _state.joins != null && _state.joins.contains(field);
}
@Override
public JDBCFetchConfiguration addJoin(String join) {
if (StringUtil.isEmpty(join))
throw new UserException(_loc.get("null-join"));
lock();
try {
if (_state.joins == null)
_state.joins = new HashSet<>();
_state.joins.add(join);
} finally {
unlock();
}
return this;
}
@Override
public JDBCFetchConfiguration addJoins(Collection<String> joins) {
if (joins == null || joins.isEmpty())
return this;
for (Iterator<String> itr = joins.iterator(); itr.hasNext();)
addJoin(itr.next());
return this;
}
@Override
public JDBCFetchConfiguration removeJoin(String field) {
lock();
try {
if (_state.joins != null)
_state.joins.remove(field);
} finally {
unlock();
}
return this;
}
@Override
public JDBCFetchConfiguration removeJoins(Collection<String> joins) {
lock();
try {
if (_state.joins != null)
_state.joins.removeAll(joins);
} finally {
unlock();
}
return this;
}
@Override
public JDBCFetchConfiguration clearJoins() {
lock();
try {
if (_state.joins != null)
_state.joins.clear();
} finally {
unlock();
}
return this;
}
@Override
public int getIsolation() {
return _state.isolationLevel;
}
@Override
public JDBCFetchConfiguration setIsolation(int level) {
if (level != -1 && level != DEFAULT
&& level != Connection.TRANSACTION_NONE
&& level != Connection.TRANSACTION_READ_UNCOMMITTED
&& level != Connection.TRANSACTION_READ_COMMITTED
&& level != Connection.TRANSACTION_REPEATABLE_READ
&& level != Connection.TRANSACTION_SERIALIZABLE)
throw new IllegalArgumentException(_loc.get("bad-level", level).getMessage());
if (level == DEFAULT)
_state.isolationLevel = -1;
else
_state.isolationLevel = level;
return this;
}
@Override
public JDBCFetchConfiguration traverseJDBC(FieldMetaData fm) {
return (JDBCFetchConfiguration) traverse(fm);
}
/**
* Access JDBC configuration information. May return null if not a
* JDBC back-end (possible to get a JDBCFetchConfiguration on non-JDBC
* back end in remote client).
*/
private JDBCConfiguration getJDBCConfiguration() {
StoreContext ctx = getContext();
if (ctx == null)
return null;
OpenJPAConfiguration conf = ctx.getConfiguration();
if (!(conf instanceof JDBCConfiguration))
return null;
return (JDBCConfiguration) conf;
}
@Override
public Set<String> getFetchInnerJoins() {
if (_state.fetchInnerJoins == null)
return Collections.emptySet();
return _state.fetchInnerJoins;
}
@Override
public boolean hasFetchInnerJoin(String field) {
return _state.fetchInnerJoins != null &&
_state.fetchInnerJoins.contains(field);
}
@Override
public JDBCFetchConfiguration addFetchInnerJoin(String join) {
if (StringUtil.isEmpty(join))
throw new UserException(_loc.get("null-join"));
lock();
try {
if (_state.fetchInnerJoins == null)
_state.fetchInnerJoins = new HashSet<>();
_state.fetchInnerJoins.add(join);
} finally {
unlock();
}
return this;
}
@Override
public JDBCFetchConfiguration addFetchInnerJoins(Collection<String> joins) {
if (joins == null || joins.isEmpty())
return this;
for (Iterator<String> itr = joins.iterator(); itr.hasNext();)
addFetchInnerJoin(itr.next());
return this;
}
}