Added some sanity checks to notice and catch programmer errors as early as
possible.
This commit is contained in:
@@ -27,6 +27,16 @@ package com.samskivert.jdbc.depot;
|
|||||||
*/
|
*/
|
||||||
public interface CacheInvalidator
|
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.
|
* 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.
|
* 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)
|
Class<T> type, final WhereClause key, CacheInvalidator invalidator, Object... fieldsValues)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
|
// sanity check
|
||||||
|
invalidator.validateFlushType(type);
|
||||||
|
|
||||||
// separate the arguments into keys and values
|
// separate the arguments into keys and values
|
||||||
final String[] fields = new String[fieldsValues.length/2];
|
final String[] fields = new String[fieldsValues.length/2];
|
||||||
final SQLExpression[] values = new SQLExpression[fields.length];
|
final SQLExpression[] values = new SQLExpression[fields.length];
|
||||||
@@ -516,6 +519,9 @@ public abstract class DepotRepository
|
|||||||
{
|
{
|
||||||
requireNotComputed(type, "updateLiteral");
|
requireNotComputed(type, "updateLiteral");
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
invalidator.validateFlushType(type);
|
||||||
|
|
||||||
// separate the arguments into keys and values
|
// separate the arguments into keys and values
|
||||||
final String[] fields = new String[fieldsToValues.size()];
|
final String[] fields = new String[fieldsToValues.size()];
|
||||||
final SQLExpression[] values = new SQLExpression[fields.length];
|
final SQLExpression[] values = new SQLExpression[fields.length];
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public class FindOneQuery<T extends PersistentRecord>
|
|||||||
{
|
{
|
||||||
_marsh = ctx.getMarshaller(type);
|
_marsh = ctx.getMarshaller(type);
|
||||||
_select = new SelectClause<T>(type, _marsh.getFieldNames(), clauses);
|
_select = new SelectClause<T>(type, _marsh.getFieldNames(), clauses);
|
||||||
|
_select.getWhereClause().validateQueryType(type); // sanity check
|
||||||
_builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, _select));
|
_builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, _select));
|
||||||
_builder.newQuery(_select);
|
_builder.newQuery(_select);
|
||||||
}
|
}
|
||||||
@@ -58,7 +59,8 @@ public class FindOneQuery<T extends PersistentRecord>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// from Query
|
// 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);
|
PreparedStatement stmt = _builder.prepare(conn);
|
||||||
try {
|
try {
|
||||||
T result = null;
|
T result = null;
|
||||||
@@ -76,7 +78,8 @@ public class FindOneQuery<T extends PersistentRecord>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// from Query
|
// from Query
|
||||||
public void updateCache (PersistenceContext ctx, T result) {
|
public void updateCache (PersistenceContext ctx, T result)
|
||||||
|
{
|
||||||
CacheKey key = getCacheKey();
|
CacheKey key = getCacheKey();
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
// no row-specific cache key was given
|
// 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.
|
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
|
// from CacheInvalidator
|
||||||
public void invalidate (PersistenceContext ctx)
|
public void invalidate (PersistenceContext ctx)
|
||||||
{
|
{
|
||||||
ctx.cacheInvalidate(this);
|
ctx.cacheInvalidate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override // from WhereClause
|
||||||
|
public void validateQueryType (Class<?> pClass)
|
||||||
|
{
|
||||||
|
super.validateQueryType(pClass);
|
||||||
|
validateTypesMatch(pClass, condition.getPersistentClass());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals (Object obj)
|
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 ()
|
public Class<T> getPersistentClass ()
|
||||||
{
|
{
|
||||||
return _pClass;
|
return _pClass;
|
||||||
@@ -122,6 +100,46 @@ public class MultiKey<T extends PersistentRecord> extends WhereClause
|
|||||||
return _mValues;
|
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 String _mField;
|
||||||
protected Comparable[] _mValues;
|
protected Comparable[] _mValues;
|
||||||
protected Class<T> _pClass;
|
protected Class<T> _pClass;
|
||||||
|
|||||||
@@ -27,4 +27,28 @@ import com.samskivert.jdbc.depot.clause.QueryClause;
|
|||||||
*/
|
*/
|
||||||
public abstract class WhereClause extends 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() + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user