From e3ff96745d225041c20b8bb88f5802087c51f94b Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Thu, 23 Jun 2011 22:12:47 +0000 Subject: [PATCH] Added support for limit to deleteAll. I would have prefered to only add this support to the Query interface, and not clutter up DepotRepository with two additional overloaded methods. However, that would not have been possible without duplicating a bunch of logic from DepotRepository. Yet another reason why the Query builder approach is awesome and the fuckloads of overloaded methods approach isn't. --- .../com/samskivert/depot/DepotRepository.java | 42 ++++++++++++++++--- src/main/java/com/samskivert/depot/Query.java | 17 ++++---- .../samskivert/depot/impl/BuildVisitor.java | 3 ++ .../samskivert/depot/impl/MySQLBuilder.java | 3 ++ .../depot/impl/clause/DeleteClause.java | 12 +++++- .../java/com/samskivert/depot/QueryTest.java | 18 ++++++++ 6 files changed, 81 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/samskivert/depot/DepotRepository.java b/src/main/java/com/samskivert/depot/DepotRepository.java index 9bd7020..775c7b7 100644 --- a/src/main/java/com/samskivert/depot/DepotRepository.java +++ b/src/main/java/com/samskivert/depot/DepotRepository.java @@ -24,6 +24,7 @@ import com.samskivert.util.ArrayUtil; import com.samskivert.depot.clause.FieldOverride; import com.samskivert.depot.clause.InsertClause; +import com.samskivert.depot.clause.Limit; import com.samskivert.depot.clause.QueryClause; import com.samskivert.depot.clause.WhereClause; import com.samskivert.depot.expression.ColumnExp; @@ -673,20 +674,34 @@ public abstract class DepotRepository * * @throws DatabaseException if any problem is encountered communicating with the database. */ - public int deleteAll (Class type, final WhereClause where) + public int deleteAll (Class type, WhereClause where) + throws DatabaseException + { + return deleteAll(type, where, null, null); + } + + /** + * Deletes all persistent objects from the database that match the supplied where clause, up to + * the specified limit. + * + * @return the number of rows deleted by this action. + * + * @throws DatabaseException if any problem is encountered communicating with the database. + */ + public int deleteAll (Class type, WhereClause where, Limit lim) throws DatabaseException { if (where instanceof CacheInvalidator) { // our where clause knows how to do its own deletion, yay! - return deleteAll(type, where, (CacheInvalidator)where); + return deleteAll(type, where, lim, (CacheInvalidator)where); } else if (_ctx.getMarshaller(type).hasPrimaryKey()) { // look up the primary keys for all matching rows matching and delete using those - KeySet pwhere = KeySet.newKeySet(type, findAllKeys(type, true, where)); + KeySet pwhere = KeySet.newKeySet(type, findAllKeys(type, true, where, lim)); return deleteAll(type, pwhere, pwhere); } else { // otherwise just do the delete directly as we can't have cached a record that has no // primary key in the first place - return deleteAll(type, where, null); + return deleteAll(type, where, lim, null); } } @@ -698,7 +713,22 @@ public abstract class DepotRepository * @throws DatabaseException if any problem is encountered communicating with the database. */ public int deleteAll ( - final Class type, final WhereClause where, CacheInvalidator invalidator) + Class type, WhereClause where, CacheInvalidator invalidator) + throws DatabaseException + { + return deleteAll(type, where, null, invalidator); + } + + /** + * Deletes all persistent objects from the database that match the supplied key, up to the + * supplied limit. + * + * @return the number of rows deleted by this action. + * + * @throws DatabaseException if any problem is encountered communicating with the database. + */ + public int deleteAll ( + final Class type, WhereClause where, Limit limit, CacheInvalidator invalidator) throws DatabaseException { if (invalidator instanceof ValidatingCacheInvalidator) { @@ -706,7 +736,7 @@ public abstract class DepotRepository } where.validateQueryType(type); // and another - DeleteClause delete = new DeleteClause(type, where); + DeleteClause delete = new DeleteClause(type, where, limit); final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, delete)); builder.newQuery(delete); diff --git a/src/main/java/com/samskivert/depot/Query.java b/src/main/java/com/samskivert/depot/Query.java index d0ab749..49495ea 100644 --- a/src/main/java/com/samskivert/depot/Query.java +++ b/src/main/java/com/samskivert/depot/Query.java @@ -523,8 +523,8 @@ public class Query } /** - * Deletes the records that match the configured query clauses. Note that only the where - * clauses are used to evaluate a deletion. Attempts to use other clauses will result in + * Deletes the records that match the configured query clauses. Note that only the where and + * limit clauses are used to evaluate a deletion. Attempts to use other clauses will result in * failure. * * @return the number of rows deleted by this action. @@ -534,12 +534,16 @@ public class Query public int delete () { assertValidDelete(); - return _repo.deleteAll(_pclass, _where); + return _repo.deleteAll(_pclass, _where, _limit); } /** - * Deletes the records that match the configured query clauses. The supplied cache invalidator - * is used to remove deleted records from the cache. + * Deletes the records that match the configured query clauses. Note that only the where and + * limit clauses are used to evaluate a deletion. Attempts to use other clauses will result in + * failure. + * + * @param invalidator if non-null, used to remove deleted records from the cache; if null, no + * cache deletion will be performed. * * @return the number of rows deleted by this action. * @@ -548,7 +552,7 @@ public class Query public int delete (CacheInvalidator invalidator) { assertValidDelete(); - return _repo.deleteAll(_pclass, _where, invalidator); + return _repo.deleteAll(_pclass, _where, _limit, invalidator); } protected Query (PersistenceContext ctx, DepotRepository repo, Class pclass) @@ -602,7 +606,6 @@ public class Query checkState(_joins == null, "Join clauses not supported by delete."); checkState(_orderBy == null, "OrderBy clause not applicable for delete."); checkState(_groupBy == null, "GroupBy clause not applicable for delete."); - checkState(_limit == null, "Limit clause not supported by delete."); checkState(_fromOverride == null, "FromOverride clause not applicable for delete."); checkState(_fieldDefs == null, "FieldDefinition clauses not applicable for delete."); checkState(_forUpdate == null, "ForUpdate clause not supported by delete."); diff --git a/src/main/java/com/samskivert/depot/impl/BuildVisitor.java b/src/main/java/com/samskivert/depot/impl/BuildVisitor.java index 2752d23..bce08e3 100644 --- a/src/main/java/com/samskivert/depot/impl/BuildVisitor.java +++ b/src/main/java/com/samskivert/depot/impl/BuildVisitor.java @@ -469,6 +469,9 @@ public abstract class BuildVisitor implements FragmentVisitor appendTableAbbreviation(deleteClause.getPersistentClass()); _builder.append(" "); deleteClause.getWhereClause().accept(this); + if (deleteClause.getLimit() != null) { + deleteClause.getLimit().accept(this); + } return null; } diff --git a/src/main/java/com/samskivert/depot/impl/MySQLBuilder.java b/src/main/java/com/samskivert/depot/impl/MySQLBuilder.java index f6d569d..fdb8c00 100644 --- a/src/main/java/com/samskivert/depot/impl/MySQLBuilder.java +++ b/src/main/java/com/samskivert/depot/impl/MySQLBuilder.java @@ -52,6 +52,9 @@ public class MySQLBuilder _types.setUseTableAbbreviations(false); try { deleteClause.getWhereClause().accept(this); + if (deleteClause.getLimit() != null) { + deleteClause.getLimit().accept(this); + } } finally { _types.setUseTableAbbreviations(savedFlag); } diff --git a/src/main/java/com/samskivert/depot/impl/clause/DeleteClause.java b/src/main/java/com/samskivert/depot/impl/clause/DeleteClause.java index c86e6fa..e61b08a 100644 --- a/src/main/java/com/samskivert/depot/impl/clause/DeleteClause.java +++ b/src/main/java/com/samskivert/depot/impl/clause/DeleteClause.java @@ -7,6 +7,7 @@ package com.samskivert.depot.impl.clause; import java.util.Collection; import com.samskivert.depot.PersistentRecord; +import com.samskivert.depot.clause.Limit; import com.samskivert.depot.clause.QueryClause; import com.samskivert.depot.clause.WhereClause; @@ -18,10 +19,11 @@ import com.samskivert.depot.impl.FragmentVisitor; public class DeleteClause implements QueryClause { - public DeleteClause (Class pClass, WhereClause where) + public DeleteClause (Class pClass, WhereClause where, Limit limit) { _pClass = pClass; _where = where; + _limit = limit; } public Class getPersistentClass () @@ -34,6 +36,11 @@ public class DeleteClause return _where; } + public Limit getLimit () + { + return _limit; + } + // from SQLFragment public void addClasses (Collection> classSet) { @@ -51,4 +58,7 @@ public class DeleteClause /** The where clause. */ protected WhereClause _where; + + /** An optional limit clause. */ + protected Limit _limit; } diff --git a/src/test/java/com/samskivert/depot/QueryTest.java b/src/test/java/com/samskivert/depot/QueryTest.java index 09b83ef..b3f78b4 100644 --- a/src/test/java/com/samskivert/depot/QueryTest.java +++ b/src/test/java/com/samskivert/depot/QueryTest.java @@ -94,6 +94,24 @@ public class QueryTest extends TestBase // _repo.deleteAll(TestRecord.class, KeySet.newSimpleKeySet(TestRecord.class, ids)); } + @Test public void testLimitedDelete () + { + for (int ii = 1; ii <= CREATE_RECORDS; ii++) { + TestRecord record = createTestRecord(ii); + record.name = "Spam! " + ii; + record.age = RandomUtil.getInt(100); + record.awesomeness = RandomUtil.getFloat(1.0F); + record.homeTown = "Over there"; + _repo.insert(record); + } + + _repo.from(TestRecord.class).whereTrue().limit(50).delete(); + assertEquals(CREATE_RECORDS-50, _repo.findAll(TestRecord.class).size()); + + _repo.from(TestRecord.class).whereTrue().delete(); + assertEquals(0, _repo.findAll(TestRecord.class).size()); + } + // the HSQL in-memory database persists for the lifetime of the VM, which means we have to // clean up after ourselves in every test; thus we go ahead and share a repository protected TestRepository _repo = createTestRepository();