diff --git a/src/java/com/samskivert/jdbc/depot/CacheInvalidator.java b/src/java/com/samskivert/jdbc/depot/CacheInvalidator.java index 0fec7ce..7ecd9ad 100644 --- a/src/java/com/samskivert/jdbc/depot/CacheInvalidator.java +++ b/src/java/com/samskivert/jdbc/depot/CacheInvalidator.java @@ -27,6 +27,16 @@ package com.samskivert.jdbc.depot; */ public interface CacheInvalidator { + /** + * Validates that this invalidator operates on the supplied persistent record class. This helps + * to catch programmer errors where one record type is used for a query clause and another is + * used for the cache invalidator. + * + * @exception IllegalArgumentException thrown if the supplied persistent record class does not + * match the class that this invalidator will flush from the cache. + */ + public void validateFlushType (Class pClass); + /** * Must invalidate all cache entries that depend on the records being modified or deleted. * This method is called just before the database statement is executed. diff --git a/src/java/com/samskivert/jdbc/depot/DepotRepository.java b/src/java/com/samskivert/jdbc/depot/DepotRepository.java index e4b1037..f93a14b 100644 --- a/src/java/com/samskivert/jdbc/depot/DepotRepository.java +++ b/src/java/com/samskivert/jdbc/depot/DepotRepository.java @@ -392,6 +392,9 @@ public abstract class DepotRepository Class type, final WhereClause key, CacheInvalidator invalidator, Object... fieldsValues) throws PersistenceException { + // sanity check + invalidator.validateFlushType(type); + // separate the arguments into keys and values final String[] fields = new String[fieldsValues.length/2]; final SQLExpression[] values = new SQLExpression[fields.length]; @@ -516,6 +519,9 @@ public abstract class DepotRepository { requireNotComputed(type, "updateLiteral"); + // sanity check + invalidator.validateFlushType(type); + // separate the arguments into keys and values final String[] fields = new String[fieldsToValues.size()]; final SQLExpression[] values = new SQLExpression[fields.length]; diff --git a/src/java/com/samskivert/jdbc/depot/FindOneQuery.java b/src/java/com/samskivert/jdbc/depot/FindOneQuery.java index 90d6485..f2639fd 100644 --- a/src/java/com/samskivert/jdbc/depot/FindOneQuery.java +++ b/src/java/com/samskivert/jdbc/depot/FindOneQuery.java @@ -43,6 +43,7 @@ public class FindOneQuery { _marsh = ctx.getMarshaller(type); _select = new SelectClause(type, _marsh.getFieldNames(), clauses); + _select.getWhereClause().validateQueryType(type); // sanity check _builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, _select)); _builder.newQuery(_select); } @@ -58,7 +59,8 @@ public class FindOneQuery } // from Query - public T invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { + public T invoke (Connection conn, DatabaseLiaison liaison) throws SQLException + { PreparedStatement stmt = _builder.prepare(conn); try { T result = null; @@ -76,7 +78,8 @@ public class FindOneQuery } // from Query - public void updateCache (PersistenceContext ctx, T result) { + public void updateCache (PersistenceContext ctx, T result) + { CacheKey key = getCacheKey(); if (key == null) { // no row-specific cache key was given diff --git a/src/java/com/samskivert/jdbc/depot/Key.java b/src/java/com/samskivert/jdbc/depot/Key.java index a18f924..f460435 100644 --- a/src/java/com/samskivert/jdbc/depot/Key.java +++ b/src/java/com/samskivert/jdbc/depot/Key.java @@ -188,12 +188,30 @@ public class Key extends WhereClause return this; // TODO: Optimally return a special class here containing only _values. } + // from CacheInvalidator + public void validateFlushType (Class pClass) + { + if (!pClass.equals(condition.getPersistentClass())) { + throw new IllegalArgumentException( + "Class mismatch between persistent record and cache invalidator " + + "[record=" + pClass.getSimpleName() + + ", invtype=" + condition.getPersistentClass().getSimpleName() + "]."); + } + } + // from CacheInvalidator public void invalidate (PersistenceContext ctx) { ctx.cacheInvalidate(this); } + @Override // from WhereClause + public void validateQueryType (Class pClass) + { + super.validateQueryType(pClass); + validateTypesMatch(pClass, condition.getPersistentClass()); + } + @Override public boolean equals (Object obj) { diff --git a/src/java/com/samskivert/jdbc/depot/MultiKey.java b/src/java/com/samskivert/jdbc/depot/MultiKey.java index 4fc5681..46c1d66 100644 --- a/src/java/com/samskivert/jdbc/depot/MultiKey.java +++ b/src/java/com/samskivert/jdbc/depot/MultiKey.java @@ -80,28 +80,6 @@ public class MultiKey extends WhereClause } } - // from SQLExpression - public void accept (ExpressionVisitor builder) throws Exception - { - builder.visit(this); - } - - // from SQLExpression - public void addClasses (Collection> classSet) - { - // nothing to add - } - - // from CacheInvalidator - public void invalidate (PersistenceContext ctx) - { - HashMap newMap = new HashMap(_map); - for (int i = 0; i < _mValues.length; i ++) { - newMap.put(_mField, _mValues[i]); - ctx.cacheInvalidate(new SimpleCacheKey(_pClass, newMap)); - } - } - public Class getPersistentClass () { return _pClass; @@ -122,6 +100,46 @@ public class MultiKey extends WhereClause return _mValues; } + // from SQLExpression + public void accept (ExpressionVisitor builder) throws Exception + { + builder.visit(this); + } + + // from SQLExpression + public void addClasses (Collection> classSet) + { + // nothing to add + } + + // from CacheInvalidator + public void validateFlushType (Class pClass) + { + if (!pClass.equals(_pClass)) { + throw new IllegalArgumentException( + "Class mismatch between persistent record and cache invalidator " + + "[record=" + pClass.getSimpleName() + + ", invtype=" + _pClass.getSimpleName() + "]."); + } + } + + // from CacheInvalidator + public void invalidate (PersistenceContext ctx) + { + HashMap newMap = new HashMap(_map); + for (int i = 0; i < _mValues.length; i ++) { + newMap.put(_mField, _mValues[i]); + ctx.cacheInvalidate(new SimpleCacheKey(_pClass, newMap)); + } + } + + @Override // from WhereClause + public void validateQueryType (Class pClass) + { + super.validateQueryType(pClass); + validateTypesMatch(pClass, _pClass); + } + protected String _mField; protected Comparable[] _mValues; protected Class _pClass; diff --git a/src/java/com/samskivert/jdbc/depot/WhereClause.java b/src/java/com/samskivert/jdbc/depot/WhereClause.java index bbd4026..ac03734 100644 --- a/src/java/com/samskivert/jdbc/depot/WhereClause.java +++ b/src/java/com/samskivert/jdbc/depot/WhereClause.java @@ -27,4 +27,28 @@ import com.samskivert.jdbc.depot.clause.QueryClause; */ public abstract class WhereClause extends QueryClause { + /** + * Validates that the supplied persistent record type is the type matched by this where clause. + * Not all clauses will be able to perform this validation, but those that can, should do so to + * help alleviate programmer error. + * + * @exception IllegalArgumentException thrown if the supplied class is known not to by the type + * matched by this where clause. + */ + public void validateQueryType (Class pClass) + { + // nothing by default + } + + /** + * A helper function for implementing {@link #validateQueryType}. + */ + protected void validateTypesMatch (Class qClass, Class kClass) + { + if (!qClass.equals(kClass)) { + throw new IllegalArgumentException( + "Class mismatch between persistent record and key in query " + + "[qtype=" + qClass.getSimpleName() + ", ktype=" + kClass.getSimpleName() + "]."); + } + } }