From 979a1fd17b50e059bc2279a0d47f100f0b765ffa Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Wed, 11 Oct 2006 20:07:05 +0000 Subject: [PATCH] Promoted a number of classes out of DepotRepository because they are used all across the package. Moved marshaller creation and management into the PersistenceContext because marshallers should be shared across a persistence context. Also moved the actual "get a connection and do the query" code into the persistence context which is sort of a nice separation. The repository and friends now just put together Query and Modifier objects and pass them to the context for execution. The context will eventually handle interaction with the cache as well (exposing manual manipulation mechanisms as needed to the DepotRepository). --- .../jdbc/depot/DepotMarshaller.java | 25 +- .../jdbc/depot/DepotRepository.java | 230 +++--------------- src/java/com/samskivert/jdbc/depot/Key.java | 64 +++++ .../com/samskivert/jdbc/depot/Modifier.java | 51 ++++ .../jdbc/depot/PersistenceContext.java | 104 +++++++- src/java/com/samskivert/jdbc/depot/Query.java | 44 ++++ 6 files changed, 303 insertions(+), 215 deletions(-) create mode 100644 src/java/com/samskivert/jdbc/depot/Key.java create mode 100644 src/java/com/samskivert/jdbc/depot/Modifier.java create mode 100644 src/java/com/samskivert/jdbc/depot/Query.java diff --git a/src/java/com/samskivert/jdbc/depot/DepotMarshaller.java b/src/java/com/samskivert/jdbc/depot/DepotMarshaller.java index 0b371b8..6729d1f 100644 --- a/src/java/com/samskivert/jdbc/depot/DepotMarshaller.java +++ b/src/java/com/samskivert/jdbc/depot/DepotMarshaller.java @@ -223,7 +223,7 @@ public class DepotMarshaller * Throws an exception if the persistent object did not declare a primary * key. */ - public DepotRepository.Key getPrimaryKey (Object object) + public Key getPrimaryKey (Object object) { if (!hasPrimaryKey()) { throw new UnsupportedOperationException( @@ -245,7 +245,7 @@ public class DepotMarshaller * Creates a primary key record for the type of object handled by this * marshaller, using the supplied primary key vlaue. */ - public DepotRepository.Key makePrimaryKey (Comparable... values) + public Key makePrimaryKey (Comparable... values) { if (!hasPrimaryKey()) { throw new UnsupportedOperationException( @@ -261,7 +261,7 @@ public class DepotMarshaller FieldMarshaller field = _pkColumns.get(ii); indices[ii] = field.getColumnName(); } - return new DepotRepository.Key(indices, values); + return new Key(indices, values); } /** @@ -354,8 +354,7 @@ public class DepotMarshaller * Creates a query for instances of this persistent object type using the * supplied key. If null is supplied all instances will be loaded. */ - public PreparedStatement createQuery ( - Connection conn, DepotRepository.Key key) + public PreparedStatement createQuery (Connection conn, Key key) throws SQLException { String query = "select " + _fullColumnList + " from " + getTableName(); @@ -440,8 +439,7 @@ public class DepotMarshaller * @return the newly assigned primary key or null if the object does not * use primary keys or this is not the right time to assign the key. */ - public DepotRepository.Key assignPrimaryKey ( - Connection conn, Object po, boolean postFactum) + public Key assignPrimaryKey (Connection conn, Object po, boolean postFactum) throws SQLException { // if we have no primary key or no generator, then we're done @@ -469,8 +467,7 @@ public class DepotMarshaller * Creates a statement that will update the supplied persistent object * using the supplied key. */ - public PreparedStatement createUpdate ( - Connection conn, Object po, DepotRepository.Key key) + public PreparedStatement createUpdate (Connection conn, Object po, Key key) throws SQLException { return createUpdate(conn, po, key, _allFields); @@ -481,8 +478,7 @@ public class DepotMarshaller * using the supplied key. */ public PreparedStatement createUpdate ( - Connection conn, Object po, DepotRepository.Key key, - String[] modifiedFields) + Connection conn, Object po, Key key, String[] modifiedFields) throws SQLException { StringBuilder update = new StringBuilder(); @@ -523,7 +519,7 @@ public class DepotMarshaller * persistent objects that match the supplied key. */ public PreparedStatement createPartialUpdate ( - Connection conn, DepotRepository.Key key, + Connection conn, Key key, String[] modifiedFields, Object[] modifiedValues) throws SQLException { @@ -553,8 +549,7 @@ public class DepotMarshaller /** * Creates a statement that will delete all rows matching the supplied key. */ - public PreparedStatement createDelete ( - Connection conn, DepotRepository.Key key) + public PreparedStatement createDelete (Connection conn, Key key) throws SQLException { String query = "delete from " + getTableName() + @@ -570,7 +565,7 @@ public class DepotMarshaller * the supplied key. */ public PreparedStatement createLiteralUpdate ( - Connection conn, DepotRepository.Key key, + Connection conn, Key key, String[] modifiedFields, Object[] modifiedValues) throws SQLException { diff --git a/src/java/com/samskivert/jdbc/depot/DepotRepository.java b/src/java/com/samskivert/jdbc/depot/DepotRepository.java index 8e958e6..2748eb7 100644 --- a/src/java/com/samskivert/jdbc/depot/DepotRepository.java +++ b/src/java/com/samskivert/jdbc/depot/DepotRepository.java @@ -31,7 +31,6 @@ import java.util.HashMap; import com.samskivert.io.PersistenceException; import com.samskivert.jdbc.ConnectionProvider; -import com.samskivert.jdbc.DuplicateKeyException; /** * Provides a base for classes that provide access to persistent objects. Also @@ -46,18 +45,15 @@ public class DepotRepository */ protected DepotRepository (ConnectionProvider conprov) { - this(conprov, new PersistenceContext()); + _ctx = new PersistenceContext(getClass().getName(), conprov); } /** - * Creates a repository with the supplied connection provider and - * persistence context. + * Creates a repository with the supplied persistence context. */ - protected DepotRepository ( - ConnectionProvider conprov, PersistenceContext context) + protected DepotRepository (PersistenceContext context) { - _conprov = conprov; - _context = context; + _ctx = context; } /** @@ -66,7 +62,7 @@ public class DepotRepository protected T load (Class type, Comparable primaryKey) throws PersistenceException { - return load(type, getMarshaller(type).makePrimaryKey(primaryKey)); + return load(type, _ctx.getMarshaller(type).makePrimaryKey(primaryKey)); } /** @@ -75,8 +71,8 @@ public class DepotRepository protected T load (Class type, Key key) throws PersistenceException { - final DepotMarshaller marsh = getMarshaller(type); - return invoke(new Query(key) { + final DepotMarshaller marsh = _ctx.getMarshaller(type); + return _ctx.invoke(new Query(key) { public T invoke (Connection conn) throws SQLException { PreparedStatement stmt = marsh.createQuery(conn, _key); try { @@ -112,8 +108,8 @@ public class DepotRepository Class type, Key key) throws PersistenceException { - final DepotMarshaller marsh = getMarshaller(type); - return invoke(new Query>(key) { + final DepotMarshaller marsh = _ctx.getMarshaller(type); + return _ctx.invoke(new Query>(key) { public ArrayList invoke (Connection conn) throws SQLException { PreparedStatement stmt = marsh.createQuery(conn, _key); try { @@ -141,8 +137,8 @@ public class DepotRepository protected int insert (final Object record) throws PersistenceException { - final DepotMarshaller marsh = getMarshaller(record.getClass()); - return invoke(new Modifier(null) { + final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass()); + return _ctx.invoke(new Modifier(null) { public int invoke (Connection conn) throws SQLException { // update our modifier's key so that it can cache our results updateKey(marsh.assignPrimaryKey(conn, record, false)); @@ -168,8 +164,8 @@ public class DepotRepository protected int update (final Object record) throws PersistenceException { - final DepotMarshaller marsh = getMarshaller(record.getClass()); - return invoke(new Modifier(marsh.getPrimaryKey(record)) { + final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass()); + return _ctx.invoke(new Modifier(marsh.getPrimaryKey(record)) { public int invoke (Connection conn) throws SQLException { PreparedStatement stmt = marsh.createUpdate(conn, record, _key); try { @@ -190,8 +186,8 @@ public class DepotRepository protected int update (final Object record, final String ... modifiedFields) throws PersistenceException { - final DepotMarshaller marsh = getMarshaller(record.getClass()); - return invoke(new Modifier(marsh.getPrimaryKey(record)) { + final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass()); + return _ctx.invoke(new Modifier(marsh.getPrimaryKey(record)) { public int invoke (Connection conn) throws SQLException { PreparedStatement stmt = marsh.createUpdate( conn, record, _key, modifiedFields); @@ -220,7 +216,8 @@ public class DepotRepository throws PersistenceException { return updatePartial( - type, getMarshaller(type).makePrimaryKey(primaryKey), fieldsValues); + type, _ctx.getMarshaller(type).makePrimaryKey(primaryKey), + fieldsValues); } /** @@ -246,8 +243,8 @@ public class DepotRepository values[ii] = fieldsValues[idx++]; } - final DepotMarshaller marsh = getMarshaller(type); - return invoke(new Modifier(key) { + final DepotMarshaller marsh = _ctx.getMarshaller(type); + return _ctx.invoke(new Modifier(key) { public int invoke (Connection conn) throws SQLException { PreparedStatement stmt = marsh.createPartialUpdate( conn, _key, fields, values); @@ -284,7 +281,8 @@ public class DepotRepository throws PersistenceException { return updateLiteral( - type, getMarshaller(type).makePrimaryKey(primaryKey), fieldsValues); + type, _ctx.getMarshaller(type).makePrimaryKey(primaryKey), + fieldsValues); } /** @@ -318,8 +316,8 @@ public class DepotRepository values[ii] = fieldsValues[idx++]; } - final DepotMarshaller marsh = getMarshaller(type); - return invoke(new Modifier(key) { + final DepotMarshaller marsh = _ctx.getMarshaller(type); + return _ctx.invoke(new Modifier(key) { public int invoke (Connection conn) throws SQLException { PreparedStatement stmt = marsh.createLiteralUpdate( conn, _key, fields, values); @@ -344,9 +342,9 @@ public class DepotRepository protected int store (final Object record) throws PersistenceException { - final DepotMarshaller marsh = getMarshaller(record.getClass()); + final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass()); Key key = marsh.hasPrimaryKey() ? marsh.getPrimaryKey(record) : null; - return invoke(new Modifier(key) { + return _ctx.invoke(new Modifier(key) { public int invoke (Connection conn) throws SQLException { PreparedStatement stmt = null; try { @@ -387,7 +385,7 @@ public class DepotRepository { @SuppressWarnings("unchecked") Class type = (Class)record.getClass(); - DepotMarshaller marsh = getMarshaller(type); + DepotMarshaller marsh = _ctx.getMarshaller(type); return deleteAll(type, marsh.getPrimaryKey(record)); } @@ -400,7 +398,8 @@ public class DepotRepository protected int delete (Class type, Comparable primaryKey) throws PersistenceException { - return deleteAll(type, getMarshaller(type).makePrimaryKey(primaryKey)); + return deleteAll( + type, _ctx.getMarshaller(type).makePrimaryKey(primaryKey)); } /** @@ -412,8 +411,8 @@ public class DepotRepository protected int deleteAll (Class type, Key key) throws PersistenceException { - final DepotMarshaller marsh = getMarshaller(type); - return invoke(new Modifier(key) { + final DepotMarshaller marsh = _ctx.getMarshaller(type); + return _ctx.invoke(new Modifier(key) { public int invoke (Connection conn) throws SQLException { PreparedStatement stmt = marsh.createDelete(conn, _key); try { @@ -425,146 +424,6 @@ public class DepotRepository }); } - protected DepotMarshaller getMarshaller (Class type) - throws PersistenceException - { - @SuppressWarnings("unchecked")DepotMarshaller marshaller = - (DepotMarshaller)_marshallers.get(type); - if (marshaller == null) { - _marshallers.put( - type, marshaller = new DepotMarshaller(type, _context)); - // initialize the marshaller which may create or migrate the table - // for its underlying persistent object - final DepotMarshaller fm = marshaller; - invoke(new Modifier(null) { - public int invoke (Connection conn) throws SQLException { - fm.init(conn); - return 0; - } - }); - } - return marshaller; - } - - protected T invoke (Query query) - throws PersistenceException - { - // TODO: check the cache using query.getKey() - - // TODO: retry query on transient failure - Connection conn = _conprov.getConnection(getIdent(), true); - try { - return query.invoke(conn); - - } catch (SQLException sqe) { - throw new PersistenceException("Query failure " + query, sqe); - - } finally { - _conprov.releaseConnection(getIdent(), true, conn); - } - } - - protected int invoke (Modifier modifier) - throws PersistenceException - { - // TODO: invalidate the cache using the modifier's key - - // TODO: retry query on transient failure - Connection conn = _conprov.getConnection(getIdent(), false); - try { - int result = modifier.invoke(conn); - // TODO: (optionally) cache the results of the modifier - return result; - - } catch (SQLException sqe) { - // convert this exception to a DuplicateKeyException if appropriate - String msg = sqe.getMessage(); - if (msg != null && msg.indexOf("Duplicate entry") != -1) { - throw new DuplicateKeyException(msg); - } else { - throw new PersistenceException( - "Modifier failure " + modifier, sqe); - } - - } finally { - _conprov.releaseConnection(getIdent(), false, conn); - } - } - - /** - * Returns the identifier to be used when obtaining JDBC connections from - * our connection provider. The default implementation uses the class name - * of this repository. - */ - protected String getIdent () - { - return getClass().getName(); - } - - protected static class Key - { - public String[] indices; - public Comparable[] values; - - public Key (String[] indices, Comparable[] values) - { - this.indices = indices; - this.values = values; - } - - public Key (String index, Comparable value) - { - this(new String[] { index }, new Comparable[] { value }); - } - - public String toWhereClause () - { - StringBuilder where = new StringBuilder(); - for (int ii = 0; ii < indices.length; ii++) { - if (ii > 0) { - where.append(" and "); - } - where.append(indices[ii]).append(" = ?"); - } - return where.toString(); - } - - public void bindArguments (PreparedStatement stmt, int startIdx) - throws SQLException - { - for (int ii = 0; ii < indices.length; ii++) { - stmt.setObject(startIdx++, values[ii]); - } - } - } - - protected static class Key2 extends Key - { - public Key2 (String index1, Comparable value1, - String index2, Comparable value2) - { - super(new String[] { index1, index2 }, - new Comparable[] { value1, value2 }); - } - } - - protected static abstract class Query - { - public Key getKey () - { - return _key; - } - - public abstract T invoke (Connection conn) throws SQLException; - - protected Query (Key key) - { - _key = key; - } - - protected Key _key; - } - protected static abstract class CollectionQuery extends Query { @@ -575,32 +434,5 @@ public class DepotRepository public abstract T invoke (Connection conn) throws SQLException; } - protected static abstract class Modifier - { - public Key getKey () - { - return _key; - } - - public abstract int invoke (Connection conn) throws SQLException; - - protected Modifier (Key key) - { - _key = key; - } - - protected void updateKey (Key key) - { - if (key != null) { - _key = key; - } - } - - protected Key _key; - } - - protected ConnectionProvider _conprov; - protected PersistenceContext _context; - protected HashMap, DepotMarshaller> _marshallers = - new HashMap, DepotMarshaller>(); + protected PersistenceContext _ctx; } diff --git a/src/java/com/samskivert/jdbc/depot/Key.java b/src/java/com/samskivert/jdbc/depot/Key.java new file mode 100644 index 0000000..b4db6f0 --- /dev/null +++ b/src/java/com/samskivert/jdbc/depot/Key.java @@ -0,0 +1,64 @@ +// +// $Id$ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2006 Michael Bayne +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc.depot; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * Encapsulates a key used to match persistent objects in a query. + */ +public class Key +{ + public String[] indices; + public Comparable[] values; + + public Key (String[] indices, Comparable[] values) + { + this.indices = indices; + this.values = values; + } + + public Key (String index, Comparable value) + { + this(new String[] { index }, new Comparable[] { value }); + } + + public String toWhereClause () + { + StringBuilder where = new StringBuilder(); + for (int ii = 0; ii < indices.length; ii++) { + if (ii > 0) { + where.append(" and "); + } + where.append(indices[ii]).append(" = ?"); + } + return where.toString(); + } + + public void bindArguments (PreparedStatement stmt, int startIdx) + throws SQLException + { + for (int ii = 0; ii < indices.length; ii++) { + stmt.setObject(startIdx++, values[ii]); + } + } +} diff --git a/src/java/com/samskivert/jdbc/depot/Modifier.java b/src/java/com/samskivert/jdbc/depot/Modifier.java new file mode 100644 index 0000000..c4e9361 --- /dev/null +++ b/src/java/com/samskivert/jdbc/depot/Modifier.java @@ -0,0 +1,51 @@ +// +// $Id$ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2006 Michael Bayne +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc.depot; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Encapsulates a modification of persistent objects. + */ +public abstract class Modifier +{ + public Key getKey () + { + return _key; + } + + public abstract int invoke (Connection conn) throws SQLException; + + protected Modifier (Key key) + { + _key = key; + } + + protected void updateKey (Key key) + { + if (key != null) { + _key = key; + } + } + + protected Key _key; +} diff --git a/src/java/com/samskivert/jdbc/depot/PersistenceContext.java b/src/java/com/samskivert/jdbc/depot/PersistenceContext.java index d2d41dd..9e5943a 100644 --- a/src/java/com/samskivert/jdbc/depot/PersistenceContext.java +++ b/src/java/com/samskivert/jdbc/depot/PersistenceContext.java @@ -23,12 +23,114 @@ package com.samskivert.jdbc.depot; import java.util.HashMap; import javax.persistence.TableGenerator; +import java.sql.Connection; +import java.sql.SQLException; + +import com.samskivert.io.PersistenceException; +import com.samskivert.jdbc.ConnectionProvider; +import com.samskivert.jdbc.DuplicateKeyException; + /** * Defines a scope in which global annotations are shared. */ public class PersistenceContext { /** Map {@link TableGenerator} instances by name. */ - public static HashMap tableGenerators = + public HashMap tableGenerators = new HashMap(); + + /** + * Creates a persistence context that will use the supplied provider to + * obtain JDBC connections. + * + * @param ident the identifier to provide to the connection provider when + * requesting a connection. + */ + public PersistenceContext (String ident, ConnectionProvider conprov) + { + _ident = ident; + _conprov = conprov; + } + + /** + * Returns the marshaller for the specified persistent object class, + * creating and initializing it if necessary. + */ + public DepotMarshaller getMarshaller (Class type) + throws PersistenceException + { + @SuppressWarnings("unchecked") DepotMarshaller marshaller = + (DepotMarshaller)_marshallers.get(type); + if (marshaller == null) { + _marshallers.put( + type, marshaller = new DepotMarshaller(type, this)); + // initialize the marshaller which may create or migrate the table + // for its underlying persistent object + final DepotMarshaller fm = marshaller; + invoke(new Modifier(null) { + public int invoke (Connection conn) throws SQLException { + fm.init(conn); + return 0; + } + }); + } + return marshaller; + } + + /** + * Invokes a non-modifying query and returns its result. + */ + public T invoke (Query query) + throws PersistenceException + { + // TODO: check the cache using query.getKey() + + // TODO: retry query on transient failure + Connection conn = _conprov.getConnection(_ident, true); + try { + return query.invoke(conn); + + } catch (SQLException sqe) { + throw new PersistenceException("Query failure " + query, sqe); + + } finally { + _conprov.releaseConnection(_ident, true, conn); + } + } + + /** + * Invokes a modifying query and returns the number of rows modified. + */ + public int invoke (Modifier modifier) + throws PersistenceException + { + // TODO: invalidate the cache using the modifier's key + + // TODO: retry query on transient failure + Connection conn = _conprov.getConnection(_ident, false); + try { + int result = modifier.invoke(conn); + // TODO: (optionally) cache the results of the modifier + return result; + + } catch (SQLException sqe) { + // convert this exception to a DuplicateKeyException if appropriate + String msg = sqe.getMessage(); + if (msg != null && msg.indexOf("Duplicate entry") != -1) { + throw new DuplicateKeyException(msg); + } else { + throw new PersistenceException( + "Modifier failure " + modifier, sqe); + } + + } finally { + _conprov.releaseConnection(_ident, false, conn); + } + } + + protected String _ident; + protected ConnectionProvider _conprov; + + protected HashMap, DepotMarshaller> _marshallers = + new HashMap, DepotMarshaller>(); } diff --git a/src/java/com/samskivert/jdbc/depot/Query.java b/src/java/com/samskivert/jdbc/depot/Query.java new file mode 100644 index 0000000..12f5621 --- /dev/null +++ b/src/java/com/samskivert/jdbc/depot/Query.java @@ -0,0 +1,44 @@ +// +// $Id$ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2006 Michael Bayne +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.samskivert.jdbc.depot; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Encapsulates a non-modifying query of persistent objects. + */ +public abstract class Query +{ + public Key getKey () + { + return _key; + } + + public abstract T invoke (Connection conn) throws SQLException; + + protected Query (Key key) + { + _key = key; + } + + protected Key _key; +}