From a8f6d3c116564b1af3bbbc8319ba0715c042750a Mon Sep 17 00:00:00 2001 From: Catalina Wei Date: Thu, 24 Jan 2008 01:33:48 +0000 Subject: [PATCH] OPENJPA-477 Making StoreManager more flexible and extensible git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@614763 13f79535-47bb-0310-9956-ffa450edef68 --- .../jdbc/kernel/AbstractUpdateManager.java | 8 +- .../jdbc/kernel/JDBCBrokerFactory.java | 2 +- .../openjpa/jdbc/kernel/JDBCStoreManager.java | 45 +++++-- .../openjpa/jdbc/kernel/JDBCStoreQuery.java | 24 +++- .../jdbc/kernel/PessimisticLockManager.java | 66 ++++++++-- .../kernel/PreparedStatementManagerImpl.java | 31 ++++- .../openjpa/jdbc/kernel/SQLStoreQuery.java | 100 +++++++++++--- .../openjpa/jdbc/kernel/TableJDBCSeq.java | 58 ++++++-- .../openjpa/jdbc/kernel/exps/PCPath.java | 4 +- .../openjpa/jdbc/kernel/exps/Param.java | 2 +- .../apache/openjpa/jdbc/sql/DBDictionary.java | 88 +++++++++---- .../openjpa/jdbc/sql/ResultSetResult.java | 11 ++ .../org/apache/openjpa/jdbc/sql/RowImpl.java | 13 +- .../apache/openjpa/jdbc/sql/SQLBuffer.java | 8 ++ .../apache/openjpa/jdbc/sql/SelectImpl.java | 124 +++++++++++++----- .../org/apache/openjpa/kernel/Bootstrap.java | 4 +- .../org/apache/openjpa/kernel/BrokerImpl.java | 26 +++- .../openjpa/kernel/StateManagerImpl.java | 2 +- .../persistence/PersistenceProviderImpl.java | 21 ++- 19 files changed, 499 insertions(+), 138 deletions(-) diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractUpdateManager.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractUpdateManager.java index 1b5614230..46886b036 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractUpdateManager.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractUpdateManager.java @@ -181,7 +181,7 @@ public abstract class AbstractUpdateManager /** * Recursive method to insert the given instance, base class first. */ - private void insert(OpenJPAStateManager sm, ClassMapping mapping, + protected void insert(OpenJPAStateManager sm, ClassMapping mapping, RowManager rowMgr, JDBCStore store, Collection customs) throws SQLException { Boolean custom = mapping.isCustomInsert(sm, store); @@ -228,7 +228,7 @@ public abstract class AbstractUpdateManager /** * Recursive method to delete the given instance, base class last. */ - private void delete(OpenJPAStateManager sm, ClassMapping mapping, + protected void delete(OpenJPAStateManager sm, ClassMapping mapping, RowManager rowMgr, JDBCStore store, Collection customs) throws SQLException { Boolean custom = mapping.isCustomDelete(sm, store); @@ -271,7 +271,7 @@ public abstract class AbstractUpdateManager /** * Recursive method to update the given instance. */ - private void update(OpenJPAStateManager sm, BitSet dirty, + protected void update(OpenJPAStateManager sm, BitSet dirty, ClassMapping mapping, RowManager rowMgr, JDBCStore store, Collection customs) throws SQLException { Boolean custom = mapping.isCustomUpdate(sm, store); @@ -300,7 +300,7 @@ public abstract class AbstractUpdateManager /** * Update version and discriminator indicators. */ - private void updateIndicators(OpenJPAStateManager sm, ClassMapping mapping, + protected void updateIndicators(OpenJPAStateManager sm, ClassMapping mapping, RowManager rowMgr, JDBCStore store, Collection customs, boolean versionUpdateOnly) throws SQLException { while (mapping.getJoinablePCSuperclassMapping() != null) diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCBrokerFactory.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCBrokerFactory.java index 280fe838f..df3e36e92 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCBrokerFactory.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCBrokerFactory.java @@ -140,7 +140,7 @@ public class JDBCBrokerFactory /** * Synchronize the mappings of the classes listed in the configuration. */ - private void synchronizeMappings(ClassLoader loader) { + protected void synchronizeMappings(ClassLoader loader) { JDBCConfiguration conf = (JDBCConfiguration) getConfiguration(); String action = conf.getSynchronizeMappings(); if (StringUtils.isEmpty(action)) diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java index 5fd951b84..003f70d4a 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java @@ -272,7 +272,7 @@ public class JDBCStoreManager /** * Initialize a newly-loaded instance. */ - private boolean initializeState(OpenJPAStateManager sm, PCState state, + protected boolean initializeState(OpenJPAStateManager sm, PCState state, JDBCFetchConfiguration fetch, ConnectionInfo info) throws ClassNotFoundException, SQLException { Object oid = sm.getObjectId(); @@ -294,7 +294,7 @@ public class JDBCStoreManager Select.SUBS_EXACT); if (res == null && !selectPrimaryKey(sm, mapping, fetch)) return false; - if (res != null && !res.next()) + if (isEmptyResult(res)) return false; } else { ClassMapping[] mappings = mapping. @@ -311,16 +311,14 @@ public class JDBCStoreManager } else res = getInitializeStateUnionResult(sm, mapping, mappings, fetch); - if (res != null && !res.next()) + if (isEmptyResult(res)) return false; } // figure out what type of object this is; the state manager // only guarantees to provide a base class Class type; - if (res == null) - type = mapping.getDescribedType(); - else { + if ((type = getType(res, mapping)) == null) { if (res.getBaseMapping() != null) mapping = res.getBaseMapping(); res.startDataRequest(mapping.getDiscriminator()); @@ -342,7 +340,7 @@ public class JDBCStoreManager // re-get the mapping in case the instance was a subclass mapping = (ClassMapping) sm.getMetaData(); load(mapping, sm, fetch, res); - mapping.getVersion().afterLoad(sm, this); + getVersion(mapping, sm, res); } return true; } finally { @@ -350,6 +348,35 @@ public class JDBCStoreManager res.close(); } } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of getting version from the result set. + */ + protected void getVersion(ClassMapping mapping, OpenJPAStateManager sm, + Result res) throws SQLException { + mapping.getVersion().afterLoad(sm, this); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of checking whether the result set is empty or not. + */ + protected boolean isEmptyResult(Result res) throws SQLException { + if (res != null && !res.next()) + return true; + return false; + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of getting type from the result set. + */ + protected Class getType(Result res, ClassMapping mapping){ + if (res == null) + return mapping.getDescribedType(); + return null; + } /** * Allow the mapping to custom load data. Return null if the mapping @@ -427,7 +454,7 @@ public class JDBCStoreManager sel.wherePrimaryKey(sm.getObjectId(), base, this); Result exists = sel.execute(this, fetch); try { - if (!exists.next()) + if (isEmptyResult(exists)) return false; // record locked? @@ -478,7 +505,7 @@ public class JDBCStoreManager sel.wherePrimaryKey(sm.getObjectId(), mapping, this); res = sel.execute(this, jfetch, lockLevel); try { - if (!res.next()) + if (isEmptyResult(res)) return false; load(mapping, sm, jfetch, res); } finally { diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java index 969549a10..ccbf964e2 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java @@ -499,11 +499,11 @@ public class JDBCStoreQuery for (int i = 0; i < sql.length; i++) { stmnt = null; try { - stmnt = sql[i].prepareStatement(conn); - count += stmnt.executeUpdate(); + stmnt = prepareStatement(conn, sql[i]); + count += executeUpdate(conn, stmnt, sql[i], isUpdate); } catch (SQLException se) { throw SQLExceptions.getStore(se, sql[i].getSQL(), - _store.getDBDictionary()); + _store.getDBDictionary()); } finally { if (stmnt != null) try { stmnt.close(); } catch (SQLException se) {} @@ -649,4 +649,22 @@ public class JDBCStoreQuery sql[i] = ((Select) sels.get(i)).toSelect(false, fetch).getSQL(true); return sql; } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of executing update. + */ + protected int executeUpdate(Connection conn, PreparedStatement stmnt, + SQLBuffer sqlBuf, boolean isUpdate) throws SQLException { + return stmnt.executeUpdate(); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of preparing statement. + */ + protected PreparedStatement prepareStatement(Connection conn, SQLBuffer sql) + throws SQLException { + return sql.prepareStatement(conn); + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PessimisticLockManager.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PessimisticLockManager.java index 108cfbd7b..e08fbdea9 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PessimisticLockManager.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PessimisticLockManager.java @@ -132,18 +132,10 @@ public class PessimisticLockManager PreparedStatement stmnt = null; ResultSet rs = null; try { - stmnt = sql.prepareStatement(conn); - if (timeout >= 0 && dict.supportsQueryTimeout) { - if (timeout < 1000) { - timeout = 1000; - if (log.isWarnEnabled()) - log.warn(_loc.get("millis-query-timeout")); - } - stmnt.setQueryTimeout(timeout / 1000); - } - rs = stmnt.executeQuery(); - if (!rs.next()) - throw new LockException(sm.getManagedInstance()); + stmnt = prepareStatement(conn, sql); + setTimeout(stmnt, timeout); + rs = executeQuery(conn, stmnt, sql); + checkLock(rs, sm); } catch (SQLException se) { throw SQLExceptions.getStore(se, dict); } finally { @@ -168,4 +160,54 @@ public class PessimisticLockManager log.info(_loc.get("start-trans-for-lock")); } } + + public JDBCStore getStore() { + return _store; + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of preparing statement. + */ + protected PreparedStatement prepareStatement(Connection conn, SQLBuffer sql) + throws SQLException { + return sql.prepareStatement(conn); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of setting query timeout. + */ + protected void setTimeout(PreparedStatement stmnt, int timeout) + throws SQLException { + DBDictionary dict = _store.getDBDictionary(); + if (timeout >= 0 && dict.supportsQueryTimeout) { + if (timeout < 1000) { + timeout = 1000; + if (log.isWarnEnabled()) + log.warn(_loc.get("millis-query-timeout")); + } + stmnt.setQueryTimeout(timeout / 1000); + } + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of executing query. + */ + protected ResultSet executeQuery(Connection conn, PreparedStatement stmnt, + SQLBuffer sql) throws SQLException { + return stmnt.executeQuery(); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of checking lock from the result set. + */ + protected void checkLock(ResultSet rs, OpenJPAStateManager sm) + throws SQLException { + if (!rs.next()) + throw new LockException(sm.getManagedInstance()); + return; + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PreparedStatementManagerImpl.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PreparedStatementManagerImpl.java index a01899ea3..02c77e728 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PreparedStatementManagerImpl.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PreparedStatementManagerImpl.java @@ -35,7 +35,6 @@ import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.util.ApplicationIds; import org.apache.openjpa.util.OpenJPAException; import org.apache.openjpa.util.OptimisticException; -import org.apache.openjpa.meta.ClassMetaData; /** * Basic prepared statement manager implementation. @@ -89,12 +88,13 @@ public class PreparedStatementManagerImpl // prepare statement String sql = row.getSQL(_dict); - PreparedStatement stmnt = _conn.prepareStatement(sql); - + PreparedStatement stmnt = prepareStatement(sql); + // setup parameters and execute statement - row.flush(stmnt, _dict, _store); + if (stmnt != null) + row.flush(stmnt, _dict, _store); try { - int count = stmnt.executeUpdate(); + int count = executeUpdate(stmnt, sql, row); if (count != 1) { Object failed = row.getFailedObject(); if (failed != null) @@ -107,7 +107,8 @@ public class PreparedStatementManagerImpl } catch (SQLException se) { throw SQLExceptions.getStore(se, row.getFailedObject(), _dict); } finally { - try { stmnt.close(); } catch (SQLException se) {} + if (stmnt != null) + try { stmnt.close(); } catch (SQLException se) {} } // set auto assign values @@ -128,4 +129,22 @@ public class PreparedStatementManagerImpl public void flush() { } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of executing update. + */ + protected int executeUpdate(PreparedStatement stmnt, String sql, + RowImpl row) throws SQLException { + return stmnt.executeUpdate(); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of preparing statement. + */ + protected PreparedStatement prepareStatement(String sql) + throws SQLException { + return _conn.prepareStatement(sql); + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/SQLStoreQuery.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/SQLStoreQuery.java index b444495a2..0b0044c14 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/SQLStoreQuery.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/SQLStoreQuery.java @@ -23,6 +23,7 @@ import java.io.StreamTokenizer; import java.io.StringReader; import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; @@ -157,7 +158,7 @@ public class SQLStoreQuery /** * Executes the filter as a SQL query. */ - private static class SQLExecutor + protected static class SQLExecutor extends AbstractExecutor { private final ClassMetaData _meta; @@ -224,20 +225,14 @@ public class SQLStoreQuery PreparedStatement stmnt = null; try { - stmnt = buf.prepareCall(conn); + stmnt = prepareCall(conn, buf); int index = 0; - for (Iterator i = paramList.iterator(); i.hasNext();) + for (Iterator i = paramList.iterator(); i.hasNext() && + stmnt != null;) dict.setUnknown(stmnt, ++index, i.next(), null); - int count = 0; - if (_call && stmnt.execute() == false) { - count = stmnt.getUpdateCount(); - } - else { - // native insert, update, delete - count = stmnt.executeUpdate(); - } + int count = executeUpdate(store, conn, stmnt, buf); return Numbers.valueOf(count); } catch (SQLException se) { if (stmnt != null) @@ -276,20 +271,23 @@ public class SQLStoreQuery try { // use the right method depending on sel vs. proc, lrs setting if (_select && !range.lrs) - stmnt = buf.prepareStatement(conn); + stmnt = prepareStatement(conn, buf); else if (_select) - stmnt = buf.prepareStatement(conn, fetch, -1, -1); + stmnt = prepareStatement(conn, buf, fetch, -1, -1); else if (!range.lrs) - stmnt = buf.prepareCall(conn); + stmnt = prepareCall(conn, buf); else - stmnt = buf.prepareCall(conn, fetch, -1, -1); + stmnt = prepareCall(conn, buf, fetch, -1, -1); int index = 0; - for (Iterator i = paramList.iterator(); i.hasNext();) + for (Iterator i = paramList.iterator(); i.hasNext() && + stmnt != null;) dict.setUnknown(stmnt, ++index, i.next(), null); - ResultSetResult res = new ResultSetResult(conn, stmnt, - stmnt.executeQuery(), store); + ResultSet rs = executeQuery(store, conn, stmnt, buf, paramList); + ResultSetResult res = stmnt != null ? + new ResultSetResult(conn, stmnt, rs, store) : + new ResultSetResult(conn, rs, dict); if (_resultMapping != null) rop = new MappedQueryResultObjectProvider(_resultMapping, store, fetch, res); @@ -319,5 +317,71 @@ public class SQLStoreQuery public boolean isPacking(StoreQuery q) { return q.getContext().getCandidateType() == null; } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of preparing call statement. + */ + protected PreparedStatement prepareCall(Connection conn, SQLBuffer buf) + throws SQLException { + return buf.prepareCall(conn); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of executing update. + */ + protected int executeUpdate(JDBCStore store, Connection conn, + PreparedStatement stmnt, SQLBuffer buf) + throws SQLException { + int count = 0; + if (_call && stmnt.execute() == false) { + count = stmnt.getUpdateCount(); + } + else { + // native insert, update, delete + count = stmnt.executeUpdate(); + } + return count; + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of preparing call statement. + */ + protected PreparedStatement prepareCall(Connection conn, SQLBuffer buf, + JDBCFetchConfiguration fetch, int rsType, int rsConcur) + throws SQLException { + return buf.prepareCall(conn, fetch, rsType, rsConcur); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of preparing statement. + */ + protected PreparedStatement prepareStatement(Connection conn, + SQLBuffer buf) throws SQLException { + return buf.prepareStatement(conn); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of preparing statement. + */ + protected PreparedStatement prepareStatement(Connection conn, + SQLBuffer buf, JDBCFetchConfiguration fetch, int rsType, + int rsConcur) throws SQLException { + return buf.prepareStatement(conn, fetch, rsType, rsConcur); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of executing query. + */ + protected ResultSet executeQuery(JDBCStore store, Connection conn, + PreparedStatement stmnt, SQLBuffer buf, List paramList) + throws SQLException { + return stmnt.executeQuery(); + } } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java index 6b334d001..026cb31d4 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java @@ -30,7 +30,6 @@ import org.apache.openjpa.jdbc.conf.JDBCConfiguration; import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl; import org.apache.openjpa.jdbc.meta.ClassMapping; import org.apache.openjpa.jdbc.schema.Column; -import org.apache.openjpa.jdbc.schema.Index; import org.apache.openjpa.jdbc.schema.PrimaryKey; import org.apache.openjpa.jdbc.schema.Schema; import org.apache.openjpa.jdbc.schema.SchemaGroup; @@ -38,6 +37,7 @@ import org.apache.openjpa.jdbc.schema.SchemaTool; import org.apache.openjpa.jdbc.schema.Schemas; import org.apache.openjpa.jdbc.schema.Table; import org.apache.openjpa.jdbc.sql.DBDictionary; +import org.apache.openjpa.jdbc.sql.RowImpl; import org.apache.openjpa.jdbc.sql.SQLBuffer; import org.apache.openjpa.jdbc.sql.SQLExceptions; import org.apache.openjpa.lib.conf.Configurable; @@ -431,8 +431,8 @@ public class TableJDBCSeq PreparedStatement stmnt = null; try { - stmnt = insert.prepareStatement(conn); - stmnt.executeUpdate(); + stmnt = prepareStatement(conn, insert); + executeUpdate(_conf, conn, stmnt, insert, RowImpl.ACTION_INSERT); } finally { if (stmnt != null) try { stmnt.close(); } catch (SQLException se) {} @@ -464,17 +464,16 @@ public class TableJDBCSeq null, false, dict.supportsSelectForUpdate, 0, Long.MAX_VALUE, false, true); - PreparedStatement stmnt = select.prepareStatement(conn); + PreparedStatement stmnt = prepareStatement(conn, select); ResultSet rs = null; try { - rs = stmnt.executeQuery(); - if (!rs.next()) - return -1; - return dict.getLong(rs, 1); + rs = executeQuery(_conf, conn, stmnt, select); + return getSequence(rs, dict); } finally { if (rs != null) try { rs.close(); } catch (SQLException se) {} - try { stmnt.close(); } catch (SQLException se) {} + if (stmnt != null) + try { stmnt.close(); } catch (SQLException se) {} } } @@ -522,8 +521,8 @@ public class TableJDBCSeq append(_seqColumn).append(" = "). appendValue(Numbers.valueOf(cur), _seqColumn); - stmnt = upd.prepareStatement(conn); - updates = stmnt.executeUpdate(); + stmnt = prepareStatement(conn, upd); + updates = executeUpdate(_conf, conn, stmnt, upd, RowImpl.ACTION_UPDATE); } finally { if (rs != null) try { rs.close(); } catch (SQLException se) {} @@ -704,4 +703,41 @@ public class TableJDBCSeq public long seq = 1L; public long max = 0L; } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of preparing statement. + */ + protected PreparedStatement prepareStatement(Connection conn, SQLBuffer buf) + throws SQLException { + return buf.prepareStatement(conn); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of executing update. + */ + protected int executeUpdate(JDBCConfiguration conf, Connection conn, + PreparedStatement stmnt, SQLBuffer buf, int opcode) throws SQLException { + return stmnt.executeUpdate(); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of executing query. + */ + protected ResultSet executeQuery(JDBCConfiguration conf, Connection conn, + PreparedStatement stmnt, SQLBuffer buf) throws SQLException { + return stmnt.executeQuery(); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of getting sequence from the result set. + */ + protected long getSequence(ResultSet rs, DBDictionary dict) throws SQLException { + if (rs == null || !rs.next()) + return -1; + return dict.getLong(rs, 1); + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java index d2c45d543..0cff107e4 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/PCPath.java @@ -49,7 +49,7 @@ import org.apache.openjpa.util.UserException; * * @author Abe White */ -class PCPath +public class PCPath extends AbstractVal implements JDBCPath { @@ -549,7 +549,7 @@ class PCPath /** * Expression state. */ - private static class PathExpState + public static class PathExpState extends ExpState { public FieldMapping field = null; diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Param.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Param.java index 3e1ef5ed2..85d1d42d2 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Param.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Param.java @@ -93,7 +93,7 @@ public class Param /** * Expression state. */ - private static class ParamExpState + public static class ParamExpState extends ConstExpState { public Object sqlValue = null; diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java index 884ea93ac..b43d90a59 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java @@ -87,7 +87,6 @@ import org.apache.openjpa.jdbc.schema.Unique; import org.apache.openjpa.kernel.Filters; import org.apache.openjpa.kernel.OpenJPAStateManager; import org.apache.openjpa.kernel.exps.Path; -import org.apache.openjpa.kernel.exps.Literal; import org.apache.openjpa.lib.conf.Configurable; import org.apache.openjpa.lib.conf.Configuration; import org.apache.openjpa.lib.jdbc.ConnectionDecorator; @@ -1982,7 +1981,7 @@ public class DBDictionary ExpState state = val.initialize(sel, ctx, 0); // JDBC Paths are always PCPaths; PCPath implements Val ExpState pathState = ((Val) path).initialize(sel, ctx, 0); - val.calculateValue(sel, ctx, state, (Val) path, pathState); + calculateValue(val, sel, ctx, state, path, pathState); // append the value with a null for the Select; i // indicates that the @@ -3476,7 +3475,7 @@ public class DBDictionary if (str == null) return new Sequence[0]; - PreparedStatement stmnt = conn.prepareStatement(str); + PreparedStatement stmnt = prepareStatement(conn, str); ResultSet rs = null; try { int idx = 1; @@ -3485,21 +3484,19 @@ public class DBDictionary if (sequenceName != null) stmnt.setString(idx++, sequenceName); - rs = stmnt.executeQuery(); - List seqList = new ArrayList(); - while (rs.next()) - seqList.add(newSequence(rs)); - return (Sequence[]) seqList.toArray(new Sequence[seqList.size()]); - } finally { + rs = executeQuery(conn, stmnt, str); + return getSequence(rs); + } finally { if (rs != null) try { rs.close(); } catch (SQLException se) { } - try { - stmnt.close(); - } catch (SQLException se) { - } + if (stmnt != null) + try { + stmnt.close(); + } catch (SQLException se) { + } } } @@ -3880,20 +3877,16 @@ public class DBDictionary }); } - PreparedStatement stmnt = conn.prepareStatement(query); + PreparedStatement stmnt = prepareStatement(conn, query); ResultSet rs = null; try { - rs = stmnt.executeQuery(); - if (!rs.next()) - throw new StoreException(_loc.get("no-genkey")); - Object key = rs.getObject(1); - if (key == null) - log.warn(_loc.get("invalid-genkey", col)); - return key; + rs = executeQuery(conn, stmnt, query); + return getKey(rs, col); } finally { if (rs != null) try { rs.close(); } catch (SQLException se) {} - try { stmnt.close(); } catch (SQLException se) {} + if (stmnt != null) + try { stmnt.close(); } catch (SQLException se) {} } } @@ -4277,4 +4270,55 @@ public class DBDictionary OpenJPAStateManager sm, ClassMapping cmd ) { return disableBatch; } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of executing query. + */ + protected ResultSet executeQuery(Connection conn, PreparedStatement stmnt, String sql + ) throws SQLException { + return stmnt.executeQuery(); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of preparing statement. + */ + protected PreparedStatement prepareStatement(Connection conn, String sql) + throws SQLException { + return conn.prepareStatement(sql); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of getting sequence from the result set. + */ + protected Sequence[] getSequence(ResultSet rs) throws SQLException { + List seqList = new ArrayList(); + while (rs != null && rs.next()) + seqList.add(newSequence(rs)); + return (Sequence[]) seqList.toArray(new Sequence[seqList.size()]); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of getting key from the result set. + */ + protected Object getKey (ResultSet rs, Column col) throws SQLException { + if (!rs.next()) + throw new StoreException(_loc.get("no-genkey")); + Object key = rs.getObject(1); + if (key == null) + log.warn(_loc.get("invalid-genkey", col)); + return key; + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of calculating value. + */ + protected void calculateValue(Val val, Select sel, ExpContext ctx, + ExpState state, Path path, ExpState pathState) { + val.calculateValue(sel, ctx, state, (Val) path, pathState); + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ResultSetResult.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ResultSetResult.java index 2db1537f4..927024f01 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ResultSetResult.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ResultSetResult.java @@ -91,6 +91,17 @@ public class ResultSetResult setStore(store); } + /** + * Constructor. + */ + public ResultSetResult(Connection conn, + ResultSet rs, DBDictionary dict) { + _conn = conn; + _stmnt = null; + _rs = rs; + _dict = dict; + } + /** * JDBC 2 constructor. Relies on being able to retrieve the statement * from the result set, and the connection from the statement. diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java index a4f4a5f2a..ce012a8b4 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java @@ -20,7 +20,6 @@ package org.apache.openjpa.jdbc.sql; import java.io.InputStream; import java.io.Reader; -import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Array; @@ -57,10 +56,10 @@ import serp.util.Numbers; public class RowImpl implements Row, Cloneable { - protected static final Object NULL = new Object(); + public static final Object NULL = new Object(); protected static final int VALID = 2 << 0; - private static final int RAW = Integer.MIN_VALUE; + public static final int RAW = Integer.MIN_VALUE; protected byte flags = 0; private final Column[] _cols; @@ -950,4 +949,12 @@ public class RowImpl if (isValid()) row.setValid(true); } + + public Object[] getVals() { + return _vals; + } + + public int[] getTypes() { + return _types; + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java index 4a96a7a42..386245bcf 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java @@ -641,4 +641,12 @@ public final class SQLBuffer return sub; } } + + public void setParameters(List params) { + _params = params; + } + + public List getColumns() { + return _cols; + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java index 96d15f516..6d0da8071 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SelectImpl.java @@ -297,10 +297,11 @@ public class SelectImpl try { SQLBuffer sql = toSelectCount(); conn = store.getConnection(); - stmnt = sql.prepareStatement(conn); - rs = stmnt.executeQuery(); - rs.next(); - return rs.getInt(1); + stmnt = prepareStatement(conn, sql, null, + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, false); + rs = executeQuery(conn, stmnt, sql, false, store); + return getCount(rs); } finally { if (rs != null) try { rs.close(); } catch (SQLException se) {} @@ -342,31 +343,21 @@ public class SelectImpl } SQLBuffer sql = toSelect(forUpdate, fetch); - int rsType = (isLRS() && supportsRandomAccess(forUpdate)) + boolean isLRS = isLRS(); + int rsType = (isLRS && supportsRandomAccess(forUpdate)) ? -1 : ResultSet.TYPE_FORWARD_ONLY; Connection conn = store.getConnection(); PreparedStatement stmnt = null; ResultSet rs = null; try { - if (isLRS()) - stmnt = sql.prepareStatement(conn, fetch, rsType, -1); + if (isLRS) + stmnt = prepareStatement(conn, sql, fetch, rsType, -1, true); else - stmnt = sql.prepareStatement(conn, rsType, -1); - - // if this is a locking select and the lock timeout is greater than - // the configured query timeout, use the lock timeout - if (forUpdate && _dict.supportsQueryTimeout && fetch != null - && fetch.getLockTimeout() > stmnt.getQueryTimeout() * 1000) { - int timeout = fetch.getLockTimeout(); - if (timeout < 1000) { - timeout = 1000; - Log log = _conf.getLog(JDBCConfiguration.LOG_JDBC); - if (log.isWarnEnabled()) - log.warn(_loc.get("millis-query-timeout")); - } - stmnt.setQueryTimeout(timeout / 1000); - } - rs = stmnt.executeQuery(); + stmnt = prepareStatement(conn, sql, null, rsType, -1, false); + + setTimeout(stmnt, forUpdate, fetch); + + rs = executeQuery(conn, stmnt, sql, isLRS, store); } catch (SQLException se) { // clean up statement if (stmnt != null) @@ -375,17 +366,8 @@ public class SelectImpl throw se; } - SelectResult res = new SelectResult(conn, stmnt, rs, _dict); - res.setSelect(this); - res.setStore(store); - res.setLocking(forUpdate); - try { - addEagerResults(res, this, store, fetch); - } catch (SQLException se) { - res.close(); - throw se; - } - return res; + return getEagerResult(conn, stmnt, rs, store, fetch, forUpdate, + sql.getSQL()); } /** @@ -423,6 +405,80 @@ public class SelectImpl } } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of preparing statement. + */ + protected PreparedStatement prepareStatement(Connection conn, + SQLBuffer sql, JDBCFetchConfiguration fetch, int rsType, + int rsConcur, boolean isLRS) throws SQLException { + if (fetch == null) + return sql.prepareStatement(conn, rsType, rsConcur); + else + return sql.prepareStatement(conn, fetch, rsType, -1); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of setting query timeout. + */ + protected void setTimeout(PreparedStatement stmnt, boolean forUpdate, + JDBCFetchConfiguration fetch) throws SQLException { + // if this is a locking select and the lock timeout is greater than + // the configured query timeout, use the lock timeout + if (forUpdate && _dict.supportsQueryTimeout && fetch != null + && fetch.getLockTimeout() > stmnt.getQueryTimeout() * 1000) { + int timeout = fetch.getLockTimeout(); + if (timeout < 1000) { + timeout = 1000; + Log log = _conf.getLog(JDBCConfiguration.LOG_JDBC); + if (log.isWarnEnabled()) + log.warn(_loc.get("millis-query-timeout")); + } + stmnt.setQueryTimeout(timeout / 1000); + } + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of executing query. + */ + protected ResultSet executeQuery(Connection conn, PreparedStatement stmnt, + SQLBuffer sql, boolean isLRS, JDBCStore store) throws SQLException { + return stmnt.executeQuery(); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of getting count from the result set. + */ + protected int getCount(ResultSet rs) throws SQLException { + rs.next(); + return rs.getInt(1); + } + + /** + * This method is to provide override for non-JDBC or JDBC-like + * implementation of executing eager selects. + */ + protected Result getEagerResult(Connection conn, + PreparedStatement stmnt, ResultSet rs, JDBCStore store, + JDBCFetchConfiguration fetch, boolean forUpdate, String sqlStr) + throws SQLException { + SelectResult res = new SelectResult(conn, stmnt, rs, _dict); + res.setSelect(this); + res.setStore(store); + res.setLocking(forUpdate); + try { + addEagerResults(res, this, store, fetch); + } catch (SQLException se) { + res.close(); + throw se; + } + return res; + } + ///////////////////////// // Select implementation ///////////////////////// diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Bootstrap.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Bootstrap.java index 4af5d2a8c..b1f8e4201 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Bootstrap.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Bootstrap.java @@ -39,7 +39,7 @@ import org.apache.openjpa.util.UserException; */ public class Bootstrap { - private static final Class[] FACTORY_ARGS = + protected static final Class[] FACTORY_ARGS = new Class[]{ ConfigurationProvider.class }; private static Localizer s_loc = Localizer.forPackage(Bootstrap.class); @@ -124,7 +124,7 @@ public class Bootstrap { return (BrokerFactory) meth.invoke(null, new Object[]{ conf }); } - private static String getFactoryClassName(ConfigurationProvider conf, + protected static String getFactoryClassName(ConfigurationProvider conf, ClassLoader loader) { try { return getFactoryClass(conf, loader).getName(); diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java index 65af58107..265fbb2e0 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java @@ -2420,10 +2420,7 @@ public class BrokerImpl } // make sure we don't already have the instance cached - StateManagerImpl other = getStateManagerImplById(id, false); - if (other != null && !other.isDeleted() && !other.isNew()) - throw new ObjectExistsException(_loc.get("cache-exists", - obj.getClass().getName(), id)).setFailedObject(obj); + checkForDuplicateId(id, obj); // if had embedded sm, null it if (sm != null) @@ -3817,7 +3814,7 @@ public class BrokerImpl _cache.remove(id, sm); break; case STATUS_OID_ASSIGN: - _cache.assignObjectId(id, sm); + assignObjectId(_cache, id, sm); break; case STATUS_COMMIT_NEW: _cache.commitNew(id, sm); @@ -4700,4 +4697,23 @@ public class BrokerImpl }; } } + + /** + * Assign the object id to the cache. Exception will be + * thrown if the id already exists in the cache. + */ + protected void assignObjectId(Object cache, Object id, + StateManagerImpl sm) { + ((ManagedCache) cache).assignObjectId(id, sm); + } + + /** + * This method makes sure we don't already have the instance cached + */ + protected void checkForDuplicateId(Object id, Object obj) { + StateManagerImpl other = getStateManagerImplById(id, false); + if (other != null && !other.isDeleted() && !other.isNew()) + throw new ObjectExistsException(_loc.get("cache-exists", + obj.getClass().getName(), id)).setFailedObject(obj); + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java index eb33507a7..380c2610e 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java @@ -3001,7 +3001,7 @@ public class StateManagerImpl /** * Mark the field as loaded or unloaded. */ - private void setLoaded(int field, boolean isLoaded) { + public void setLoaded(int field, boolean isLoaded) { // don't continue if loaded state is already correct; otherwise we // can end up clearing _fieldImpl when we shouldn't if (_loaded.get(field) == isLoaded) diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java index cc51f1dd7..9ade648a9 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java @@ -22,8 +22,6 @@ import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.spi.ClassTransformer; import javax.persistence.spi.PersistenceProvider; @@ -73,7 +71,7 @@ public class PersistenceProviderImpl */ public OpenJPAEntityManagerFactory createEntityManagerFactory(String name, String resource, Map m) { - PersistenceProductDerivation pd = new PersistenceProductDerivation(); + PersistenceProductDerivation pd = newPersistenceProductDerivation(); try { Object poolValue = Configurations.removeProperty(EMF_POOL, m); ConfigurationProvider cp = pd.load(resource, name, m); @@ -101,7 +99,7 @@ public class PersistenceProviderImpl } if (poolValue == null || !((Boolean) poolValue).booleanValue()) - return Bootstrap.newBrokerFactory(cp, loader); + return newBrokerFactory(cp, loader); else return Bootstrap.getBrokerFactory(cp, loader); } @@ -201,4 +199,19 @@ public class PersistenceProviderImpl return _trans.transform(cl, name, previousVersion, pd, bytes); } } + + /** + * Return a persistence product deviration with default setting. + */ + public PersistenceProductDerivation newPersistenceProductDerivation() { + return new PersistenceProductDerivation(); + } + + /** + * Return a broker factory for the given configuration and class loader. + */ + public BrokerFactory newBrokerFactory(ConfigurationProvider cp, + ClassLoader loader) { + return Bootstrap.newBrokerFactory(cp, loader); + } }