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; +}