Added some sanity checks to notice and catch programmer errors as early as

possible.
This commit is contained in:
Michael Bayne
2007-08-15 18:44:35 +00:00
parent b884f89e80
commit 8e13453362
6 changed files with 103 additions and 24 deletions
@@ -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.
@@ -392,6 +392,9 @@ public abstract class DepotRepository
Class<T> 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];
@@ -43,6 +43,7 @@ public class FindOneQuery<T extends PersistentRecord>
{
_marsh = ctx.getMarshaller(type);
_select = new SelectClause<T>(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<T extends PersistentRecord>
}
// 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<T extends PersistentRecord>
}
// 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
@@ -188,12 +188,30 @@ public class Key<T extends PersistentRecord> 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)
{
@@ -80,28 +80,6 @@ public class MultiKey<T extends PersistentRecord> extends WhereClause
}
}
// from SQLExpression
public void accept (ExpressionVisitor builder) throws Exception
{
builder.visit(this);
}
// from SQLExpression
public void addClasses (Collection<Class<? extends PersistentRecord>> classSet)
{
// nothing to add
}
// from CacheInvalidator
public void invalidate (PersistenceContext ctx)
{
HashMap<String, Comparable> newMap = new HashMap<String, Comparable>(_map);
for (int i = 0; i < _mValues.length; i ++) {
newMap.put(_mField, _mValues[i]);
ctx.cacheInvalidate(new SimpleCacheKey(_pClass, newMap));
}
}
public Class<T> getPersistentClass ()
{
return _pClass;
@@ -122,6 +100,46 @@ public class MultiKey<T extends PersistentRecord> extends WhereClause
return _mValues;
}
// from SQLExpression
public void accept (ExpressionVisitor builder) throws Exception
{
builder.visit(this);
}
// from SQLExpression
public void addClasses (Collection<Class<? extends PersistentRecord>> 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<String, Comparable> newMap = new HashMap<String, Comparable>(_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<T> _pClass;
@@ -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() + "].");
}
}
}