[#2265] Added prototype implementation for Record.delete()

This commit is contained in:
Lukas Eder 2013-05-11 12:45:36 +02:00
parent 9946b236e7
commit b729a58a07
6 changed files with 115 additions and 51 deletions

View File

@ -53,7 +53,9 @@ import java.util.Collections;
import org.jooq.Cursor;
import org.jooq.DSLContext;
import org.jooq.Delete;
import org.jooq.ExecuteContext;
import org.jooq.Query;
import org.jooq.Record;
import org.jooq.Record1;
import org.jooq.Record2;
@ -100,21 +102,31 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
* This listener is used to check if a <code>SELECT</code> statement is
* issued after a call to {@link Record#refresh()}.
*/
private static class NoSelectAfterRefreshListener extends DefaultExecuteListener {
private static class NoStatementAfterCRUDListener extends DefaultExecuteListener {
/**
* Default UID
*/
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 1L;
private final Class<? extends Query> type;
private final String crudMethod;
NoStatementAfterCRUDListener(Class<? extends Query> type, String crudMethod) {
this.type = type;
this.crudMethod = crudMethod;
}
@Override
public void start(ExecuteContext ctx) {
super.start(ctx);
if (ctx.query() instanceof Select) {
for (StackTraceElement e : Thread.currentThread().getStackTrace()) {
if (e.getMethodName().equals("refresh")) {
fail("Record.refresh() should not execute any queries");
if (ctx.query() != null) {
if (type.isAssignableFrom(ctx.query().getClass())) {
for (StackTraceElement e : Thread.currentThread().getStackTrace()) {
if (e.getMethodName().equals(crudMethod)) {
fail("Record." + crudMethod + "() should not execute any " + type.getName() + " queries");
}
}
}
}
@ -140,7 +152,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
@Test
public void testKeepRSWithCloseAfterFetch() throws Exception {
DSLContext create = create(new NoSelectAfterRefreshListener());
DSLContext create = create(new NoStatementAfterCRUDListener(Select.class, "refresh"));
Result<B> b1 = create.selectFrom(TBook()).fetch();
assertNull(b1.resultSet());
@ -194,7 +206,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
return;
}
DSLContext create = create(new NoSelectAfterRefreshListener());
DSLContext create = create(new NoStatementAfterCRUDListener(Select.class, "refresh"));
Result<B> b2 = create.selectFrom(TBook()).keepResultSet(KEEP_AFTER_FETCH).fetch();
B r = b2.get(0);
assertNotNull(b2.resultSet());
@ -259,7 +271,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
}
jOOQAbstractTest.reset = false;
DSLContext create = create(new NoSelectAfterRefreshListener());
DSLContext create = create(new NoStatementAfterCRUDListener(Select.class, "refresh"));
// Use plain SQL to prevent fetching of UpdatableRecord
Result<B> books =
@ -297,7 +309,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
}
jOOQAbstractTest.reset = false;
DSLContext create = create(new NoSelectAfterRefreshListener());
DSLContext create = create(new NoStatementAfterCRUDListener(Select.class, "refresh"));
Cursor<B> books =
create.selectFrom(TBook())
@ -337,7 +349,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
jOOQAbstractTest.reset = false;
DSLContext create = create(new NoSelectAfterRefreshListener());
DSLContext create = create(new NoStatementAfterCRUDListener(Select.class, "refresh"));
Record book =
create.select()
.from(TBook().getName())
@ -374,7 +386,7 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
}
@Test
public void testKeepRSWithUpdateOnChangeRemove() throws Exception {
public void testKeepRSWithUpdateOnChangeDelete() throws Exception {
switch (dialect()) {
case SQLITE:
log.info("SKIPPING", "KeepResultSet tests");
@ -382,6 +394,24 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
}
jOOQAbstractTest.reset = false;
DSLContext create = create(new NoStatementAfterCRUDListener(Delete.class, "delete"));
Result<B> books =
create.selectFrom(TBook())
.orderBy(TBook_ID())
.keepResultSet(UPDATE_ON_CHANGE)
.fetch();
B b2 = books.get(1);
assertEquals(1, b2.delete());
ResultSet rs = b2.resultSet();
rs.beforeFirst();
assertTrue(rs.next());
assertTrue(rs.next());
assertTrue(rs.next());
assertFalse(rs.next());
System.out.println(books);
}
/*

View File

@ -2241,8 +2241,8 @@ public abstract class jOOQAbstractTest<
}
@Test
public void testKeepRSWithUpdateOnChangeRemove() throws Exception {
new KeepResultSetTests(this).testKeepRSWithUpdateOnChangeRemove();
public void testKeepRSWithUpdateOnChangeDelete() throws Exception {
new KeepResultSetTests(this).testKeepRSWithUpdateOnChangeDelete();
}
@Test

View File

@ -1042,6 +1042,8 @@ public interface Record extends Attachable, Comparable<Record> {
// Methods related to the underlying ResultSet (if applicable)
// -------------------------------------------------------------------------
int delete() throws DataAccessException;
/**
* Refresh this record from the database.
* <p>

View File

@ -298,6 +298,7 @@ public interface UpdatableRecord<R extends UpdatableRecord<R>> extends TableReco
* @throws DataChangedException If optimistic locking is enabled and the
* record has already been changed/deleted in the database
*/
@Override
int delete() throws DataAccessException, DataChangedException;
/**

View File

@ -66,6 +66,7 @@ import org.jooq.UniqueKey;
import org.jooq.exception.DataAccessException;
import org.jooq.exception.InvalidResultException;
import org.jooq.exception.MappingException;
import org.jooq.impl.CursorImpl.CursorResultSet;
import org.jooq.tools.Convert;
import org.jooq.tools.JooqLogger;
@ -86,7 +87,7 @@ abstract class AbstractRecord extends AbstractStore implements Record {
final RowImpl fields;
final Value<?>[] values;
transient KeepResultSetMode keepResultSetMode;
transient ResultSet rs;
transient CursorResultSet rs;
transient int rsIndex;
AbstractRecord(Collection<? extends Field<?>> fields) {
@ -699,6 +700,28 @@ abstract class AbstractRecord extends AbstractStore implements Record {
// XXX: Methods related to the underlying ResultSet (if applicable)
// -------------------------------------------------------------------------
/**
* {@inheritDoc}
* <p>
* Subclasses may override this
*/
@Override
public int delete() {
checkRsAvailable("Cannot delete record. No ResultSet available");
try {
// [#2265] TODO: This code is prototypical.
rs.absolute(rsIndex - 1);
rs.deleteRow();
return 1;
}
catch (SQLException e) {
throw translate("Cannot delete record", e);
}
}
@Override
public final void refresh() {
refresh(fields.fields.fields);
@ -710,22 +733,25 @@ abstract class AbstractRecord extends AbstractStore implements Record {
* Subclasses may override this
*/
@Override
public void refresh(Field<?>... f) throws DataAccessException {
if (rs != null) {
try {
public void refresh(Field<?>... f) {
checkRsAvailable("Cannot refresh record. No ResultSet available");
// [#2265] TODO: This code is prototypical. fetchLazy() is not
// the best way to fetch a record
rs.absolute(rsIndex - 1);
AbstractRecord record = (AbstractRecord) create().fetchLazy(rs).fetchOne();
setValues(f, record);
}
catch (SQLException e) {
throw translate("Cannot refresh record", e);
}
try {
// [#2265] TODO: This code is prototypical. fetchLazy() is not
// the best way to fetch a record
rs.absolute(rsIndex - 1);
AbstractRecord record = (AbstractRecord) create().fetchLazy(rs).fetchOne();
setValues(f, record);
}
else {
throw new DataAccessException("Cannot refresh record. No ResultSet available");
catch (SQLException e) {
throw translate("Cannot refresh record", e);
}
}
private final void checkRsAvailable(String message) {
if (rs == null) {
throw new DataAccessException(message);
}
}

View File

@ -302,35 +302,40 @@ public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableReco
@Override
public final int delete() {
TableField<R, ?>[] keys = getPrimaryKey().getFieldsArray();
if (rs != null) {
return super.delete();
}
else {
TableField<R, ?>[] keys = getPrimaryKey().getFieldsArray();
try {
DeleteQuery<R> delete1 = create().deleteQuery(getTable());
Utils.addConditions(delete1, this, keys);
try {
DeleteQuery<R> delete1 = create().deleteQuery(getTable());
Utils.addConditions(delete1, this, keys);
if (isExecuteWithOptimisticLocking()) {
if (isExecuteWithOptimisticLocking()) {
// [#1596] Add additional conditions for version and/or timestamp columns
if (isTimestampOrVersionAvailable()) {
addConditionForVersionAndTimestamp(delete1);
// [#1596] Add additional conditions for version and/or timestamp columns
if (isTimestampOrVersionAvailable()) {
addConditionForVersionAndTimestamp(delete1);
}
// [#1547] Try fetching the Record again first, and compare this
// Record's original values with the ones in the database
else {
checkIfChanged(keys);
}
}
// [#1547] Try fetching the Record again first, and compare this
// Record's original values with the ones in the database
else {
checkIfChanged(keys);
}
int result = delete1.execute();
checkIfChanged(result, null, null);
return result;
}
int result = delete1.execute();
checkIfChanged(result, null, null);
return result;
}
// [#673] If store() is called after delete(), a new INSERT should
// be executed and the record should be recreated
finally {
changed(true);
// [#673] If store() is called after delete(), a new INSERT should
// be executed and the record should be recreated
finally {
changed(true);
}
}
}