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();