[#2010] Add listener API to Record / UpdatableRecord
* Added support for REFRESH event
This commit is contained in:
parent
ee9f1fed76
commit
290601afce
@ -108,6 +108,25 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
|
||||
assertEquals(asList("loadStart", "loadEnd"), listener2.events);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecordListenerStore() throws Exception {
|
||||
jOOQAbstractTest.reset = false;
|
||||
|
||||
B book =
|
||||
create()
|
||||
.selectFrom(TBook())
|
||||
.where(TBook_ID().eq(1))
|
||||
.fetchOne();
|
||||
|
||||
SimpleRecordListener listener1 = new SimpleRecordListener();
|
||||
book.attach(create(listener1).configuration());
|
||||
|
||||
// TODO: There is an internal load operation involved here. Is that
|
||||
// really the desired behaviour?
|
||||
book.refresh();
|
||||
assertEquals(asList("loadStart", "loadEnd", "refreshStart", "refreshEnd"), listener1.events);
|
||||
}
|
||||
|
||||
private static class SimpleRecordListener extends DefaultRecordListener {
|
||||
List<String> events = new ArrayList<String>();
|
||||
|
||||
@ -120,5 +139,55 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
|
||||
public void loadEnd(RecordContext ctx) {
|
||||
events.add("loadEnd");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeStart(RecordContext ctx) {
|
||||
events.add("storeStart");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeEnd(RecordContext ctx) {
|
||||
events.add("storeEnd");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertStart(RecordContext ctx) {
|
||||
events.add("insertStart");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertEnd(RecordContext ctx) {
|
||||
events.add("insertEnd");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStart(RecordContext ctx) {
|
||||
events.add("updateStart");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateEnd(RecordContext ctx) {
|
||||
events.add("updateEnd");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteStart(RecordContext ctx) {
|
||||
events.add("deleteStart");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteEnd(RecordContext ctx) {
|
||||
events.add("deleteEnd");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshStart(RecordContext ctx) {
|
||||
events.add("refreshStart");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshEnd(RecordContext ctx) {
|
||||
events.add("refreshEnd");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1508,6 +1508,11 @@ public abstract class jOOQAbstractTest<
|
||||
new RecordListenerTests(this).testRecordListenerLoad();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecordListenerStore() throws Exception {
|
||||
new RecordListenerTests(this).testRecordListenerStore();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResultSetType() throws Exception {
|
||||
new ResultSetTests(this).testResultSetType();
|
||||
|
||||
@ -346,10 +346,10 @@ abstract class AbstractRecord extends AbstractStore implements Record {
|
||||
@Override
|
||||
public Record original() {
|
||||
return Utils.newRecord((Class<AbstractRecord>) getClass(), fields.fields.fields, configuration())
|
||||
.initialise(new RecordInitialiser<AbstractRecord, RuntimeException>() {
|
||||
.operate(new RecordOperation<AbstractRecord, RuntimeException>() {
|
||||
|
||||
@Override
|
||||
public AbstractRecord initialise(AbstractRecord record) throws RuntimeException {
|
||||
public AbstractRecord operate(AbstractRecord record) throws RuntimeException {
|
||||
Value<?>[] v = getValues();
|
||||
|
||||
for (int i = 0; i < v.length; i++) {
|
||||
@ -498,10 +498,10 @@ abstract class AbstractRecord extends AbstractStore implements Record {
|
||||
@Override
|
||||
public final <R extends Record> R into(final Table<R> table) {
|
||||
return Utils.newRecord(table, configuration())
|
||||
.initialise(new RecordInitialiser<R, MappingException>() {
|
||||
.operate(new RecordOperation<R, MappingException>() {
|
||||
|
||||
@Override
|
||||
public R initialise(R record) throws MappingException {
|
||||
public R operate(R record) throws MappingException {
|
||||
try {
|
||||
for (Field<?> targetField : table.fields()) {
|
||||
Field<?> sourceField = field(targetField);
|
||||
|
||||
@ -514,7 +514,7 @@ public abstract class AbstractRoutine<T> extends AbstractQueryPart implements Ro
|
||||
if (sqlType == Types.STRUCT) {
|
||||
UDTRecord<?> record = Utils
|
||||
.newRecord((Class<? extends UDTRecord<?>>) parameter.getType())
|
||||
.<RuntimeException>initialise(null);
|
||||
.<RuntimeException>operate(null);
|
||||
statement.registerOutParameter(index, Types.STRUCT, record.getSQLTypeName());
|
||||
}
|
||||
|
||||
|
||||
@ -378,10 +378,10 @@ abstract class AbstractStoreQuery<R extends Record> extends AbstractQuery implem
|
||||
for (final Number id : ids) {
|
||||
getReturnedRecords().add(
|
||||
Utils.newRecord(into, configuration)
|
||||
.initialise(new RecordInitialiser<R, RuntimeException>() {
|
||||
.operate(new RecordOperation<R, RuntimeException>() {
|
||||
|
||||
@Override
|
||||
public R initialise(R record) throws RuntimeException {
|
||||
public R operate(R record) throws RuntimeException {
|
||||
((AbstractRecord) record).setValue(field, new Value<Number>(id));
|
||||
return record;
|
||||
}
|
||||
|
||||
@ -1403,7 +1403,7 @@ class CursorImpl<R extends Record> implements Cursor<R> {
|
||||
}
|
||||
|
||||
record = Utils.newRecord((Class<AbstractRecord>) type, fields, ctx.configuration())
|
||||
.initialise(initialiser);
|
||||
.operate(initialiser);
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
@ -1427,10 +1427,10 @@ class CursorImpl<R extends Record> implements Cursor<R> {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private class CursorRecordInitialiser implements RecordInitialiser<AbstractRecord, SQLException> {
|
||||
private class CursorRecordInitialiser implements RecordOperation<AbstractRecord, SQLException> {
|
||||
|
||||
@Override
|
||||
public AbstractRecord initialise(AbstractRecord record) throws SQLException {
|
||||
public AbstractRecord operate(AbstractRecord record) throws SQLException {
|
||||
ctx.record(record);
|
||||
listener.recordStart(ctx);
|
||||
|
||||
|
||||
@ -131,7 +131,7 @@ class DefaultBindContext extends AbstractBindContext {
|
||||
// [#1126] Oracle's UDTs need to be bound with their type name
|
||||
else if (UDTRecord.class.isAssignableFrom(type)) {
|
||||
String typeName = Utils.newRecord((Class<UDTRecord<?>>) type)
|
||||
.<RuntimeException>initialise(null)
|
||||
.<RuntimeException>operate(null)
|
||||
.getUDT()
|
||||
.getName();
|
||||
stmt.setNull(nextIndex(), sqlType, typeName);
|
||||
|
||||
@ -1480,21 +1480,21 @@ public class DefaultDSLContext implements DSLContext, Serializable {
|
||||
|
||||
@Override
|
||||
public <R extends UDTRecord<R>> R newRecord(UDT<R> type) {
|
||||
return Utils.newRecord(type, configuration).<RuntimeException>initialise(null);
|
||||
return Utils.newRecord(type, configuration).<RuntimeException>operate(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends Record> R newRecord(Table<R> table) {
|
||||
return Utils.newRecord(table, configuration).<RuntimeException>initialise(null);
|
||||
return Utils.newRecord(table, configuration).<RuntimeException>operate(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends Record> R newRecord(Table<R> table, final Object source) {
|
||||
return Utils.newRecord(table, configuration)
|
||||
.initialise(new RecordInitialiser<R, RuntimeException>() {
|
||||
.operate(new RecordOperation<R, RuntimeException>() {
|
||||
|
||||
@Override
|
||||
public R initialise(R record) {
|
||||
public R operate(R record) {
|
||||
record.from(source);
|
||||
return record;
|
||||
}
|
||||
|
||||
@ -36,8 +36,12 @@
|
||||
package org.jooq.impl;
|
||||
|
||||
import static org.jooq.ExecuteType.READ;
|
||||
import static org.jooq.ExecuteType.WRITE;
|
||||
import static org.jooq.impl.RecordDelegate.RecordLifecycleType.LOAD;
|
||||
import static org.jooq.impl.RecordDelegate.RecordLifecycleType.REFRESH;
|
||||
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.ExecuteType;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.RecordContext;
|
||||
import org.jooq.RecordListener;
|
||||
@ -49,17 +53,31 @@ import org.jooq.RecordListenerProvider;
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
class RecordStub<R extends Record> {
|
||||
class RecordDelegate<R extends Record> {
|
||||
|
||||
private final Configuration configuration;
|
||||
private final R record;
|
||||
private final Configuration configuration;
|
||||
private final R record;
|
||||
private final RecordLifecycleType type;
|
||||
|
||||
RecordStub(Configuration configuration, R record) {
|
||||
this.configuration = configuration;
|
||||
this.record = record;
|
||||
RecordDelegate(Configuration configuration, R record) {
|
||||
this(configuration, record, LOAD);
|
||||
}
|
||||
|
||||
final <E extends Exception> R initialise(RecordInitialiser<R, E> initialiser) throws E {
|
||||
RecordDelegate(Configuration configuration, R record, RecordLifecycleType type) {
|
||||
this.configuration = configuration;
|
||||
this.record = record;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
static final <R extends Record> RecordDelegate<R> delegate(Configuration configuration, R record) {
|
||||
return new RecordDelegate<R>(configuration, record);
|
||||
}
|
||||
|
||||
static final <R extends Record> RecordDelegate<R> delegate(Configuration configuration, R record, RecordLifecycleType type) {
|
||||
return new RecordDelegate<R>(configuration, record, type);
|
||||
}
|
||||
|
||||
final <E extends Exception> R operate(RecordOperation<R, E> operation) throws E {
|
||||
RecordListenerProvider[] providers = null;
|
||||
RecordListener[] listeners = null;
|
||||
RecordContext ctx = null;
|
||||
@ -69,7 +87,7 @@ class RecordStub<R extends Record> {
|
||||
|
||||
if (providers != null) {
|
||||
listeners = new RecordListener[providers.length];
|
||||
ctx = new DefaultRecordContext(configuration, READ, record);
|
||||
ctx = new DefaultRecordContext(configuration, executeType(), record);
|
||||
|
||||
for (int i = 0; i < providers.length; i++) {
|
||||
listeners[i] = providers[i].provide();
|
||||
@ -79,20 +97,51 @@ class RecordStub<R extends Record> {
|
||||
|
||||
if (listeners != null) {
|
||||
for (RecordListener listener : listeners) {
|
||||
listener.loadStart(ctx);
|
||||
switch (type) {
|
||||
case LOAD: listener.loadStart(ctx); break;
|
||||
case REFRESH: listener.refreshStart(ctx); break;
|
||||
case STORE: listener.storeStart(ctx); break;
|
||||
case INSERT: listener.insertStart(ctx); break;
|
||||
case UPDATE: listener.updateStart(ctx); break;
|
||||
case DELETE: listener.deleteStart(ctx); break;
|
||||
default:
|
||||
throw new IllegalStateException("Type not supported: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (initialiser != null) {
|
||||
initialiser.initialise(record);
|
||||
if (operation != null) {
|
||||
operation.operate(record);
|
||||
}
|
||||
|
||||
if (listeners != null) {
|
||||
for (RecordListener listener : listeners) {
|
||||
listener.loadEnd(ctx);
|
||||
switch (type) {
|
||||
case LOAD: listener.loadEnd(ctx); break;
|
||||
case REFRESH: listener.refreshEnd(ctx); break;
|
||||
case STORE: listener.storeEnd(ctx); break;
|
||||
case INSERT: listener.insertEnd(ctx); break;
|
||||
case UPDATE: listener.updateEnd(ctx); break;
|
||||
case DELETE: listener.deleteEnd(ctx); break;
|
||||
default:
|
||||
throw new IllegalStateException("Type not supported: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
private final ExecuteType executeType() {
|
||||
return type == LOAD || type == REFRESH ? READ : WRITE;
|
||||
}
|
||||
|
||||
enum RecordLifecycleType {
|
||||
LOAD,
|
||||
REFRESH,
|
||||
STORE,
|
||||
INSERT,
|
||||
UPDATE,
|
||||
DELETE
|
||||
}
|
||||
}
|
||||
@ -39,15 +39,15 @@ import org.jooq.Record;
|
||||
import org.jooq.RecordListener;
|
||||
|
||||
/**
|
||||
* An stub for {@link Record} objects, abstracting {@link RecordListener}
|
||||
* lifecycle handling.
|
||||
* An operation callback for {@link Record} objects, abstracting
|
||||
* {@link RecordListener} lifecycle handling.
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
interface RecordInitialiser<R extends Record, E extends Exception> {
|
||||
interface RecordOperation<R extends Record, E extends Exception> {
|
||||
|
||||
/**
|
||||
* Callback method to initialise a record.
|
||||
*/
|
||||
R initialise(R record) throws E;
|
||||
R operate(R record) throws E;
|
||||
}
|
||||
@ -36,6 +36,8 @@
|
||||
package org.jooq.impl;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static org.jooq.impl.RecordDelegate.delegate;
|
||||
import static org.jooq.impl.RecordDelegate.RecordLifecycleType.REFRESH;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.sql.Timestamp;
|
||||
@ -340,15 +342,21 @@ public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableReco
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void refresh(Field<?>... f) {
|
||||
SelectQuery<?> select = create().selectQuery();
|
||||
public final void refresh(final Field<?>... f) {
|
||||
SelectQuery<Record> select = create().selectQuery();
|
||||
select.addSelect(f);
|
||||
select.addFrom(getTable());
|
||||
Utils.addConditions(select, this, getPrimaryKey().getFieldsArray());
|
||||
|
||||
if (select.execute() == 1) {
|
||||
AbstractRecord record = (AbstractRecord) select.getResult().get(0);
|
||||
setValues(f, record);
|
||||
delegate(configuration(), select.getResult().get(0), REFRESH)
|
||||
.operate(new RecordOperation<Record, RuntimeException>() {
|
||||
@Override
|
||||
public Record operate(Record record) throws RuntimeException {
|
||||
setValues(f, (AbstractRecord) record);
|
||||
return record;
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
throw new InvalidResultException("Exactly one row expected for refresh. Record does not exist in database.");
|
||||
@ -370,10 +378,10 @@ public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableReco
|
||||
@Override
|
||||
public final R copy() {
|
||||
return Utils.newRecord(getTable(), configuration())
|
||||
.initialise(new RecordInitialiser<R, RuntimeException>() {
|
||||
|
||||
.operate(new RecordOperation<R, RuntimeException>() {
|
||||
|
||||
@Override
|
||||
public R initialise(R copy) throws RuntimeException {
|
||||
public R operate(R copy) throws RuntimeException {
|
||||
// Copy all fields. This marks them all as isChanged, which is important
|
||||
List<TableField<R, ?>> key = getPrimaryKey().getFields();
|
||||
for (Field<?> field : fields.fields.fields) {
|
||||
|
||||
@ -236,21 +236,21 @@ final class Utils {
|
||||
/**
|
||||
* Create a new record
|
||||
*/
|
||||
static final <R extends Record> RecordStub<R> newRecord(Class<R> type) {
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(Class<R> type) {
|
||||
return newRecord(type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new record
|
||||
*/
|
||||
static final <R extends Record> RecordStub<R> newRecord(Class<R> type, Field<?>[] fields) {
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(Class<R> type, Field<?>[] fields) {
|
||||
return newRecord(type, fields, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new record
|
||||
*/
|
||||
static final <R extends Record> RecordStub<R> newRecord(Table<R> type) {
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(Table<R> type) {
|
||||
return newRecord(type, null);
|
||||
}
|
||||
|
||||
@ -258,21 +258,21 @@ final class Utils {
|
||||
* Create a new record
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static final <R extends Record> RecordStub<R> newRecord(Table<R> type, Configuration configuration) {
|
||||
return (RecordStub<R>) newRecord(type.getRecordType(), type.fields(), configuration);
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(Table<R> type, Configuration configuration) {
|
||||
return (RecordDelegate<R>) newRecord(type.getRecordType(), type.fields(), configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new UDT record
|
||||
*/
|
||||
static final <R extends UDTRecord<R>> RecordStub<R> newRecord(UDT<R> type) {
|
||||
static final <R extends UDTRecord<R>> RecordDelegate<R> newRecord(UDT<R> type) {
|
||||
return newRecord(type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new UDT record
|
||||
*/
|
||||
static final <R extends UDTRecord<R>> RecordStub<R> newRecord(UDT<R> type, Configuration configuration) {
|
||||
static final <R extends UDTRecord<R>> RecordDelegate<R> newRecord(UDT<R> type, Configuration configuration) {
|
||||
return newRecord(type.getRecordType(), type.fields(), configuration);
|
||||
}
|
||||
|
||||
@ -280,7 +280,7 @@ final class Utils {
|
||||
* Create a new record
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
static final <R extends Record> RecordStub<R> newRecord(Class<R> type, Field<?>[] fields, Configuration configuration) {
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(Class<R> type, Field<?>[] fields, Configuration configuration) {
|
||||
try {
|
||||
R record;
|
||||
|
||||
@ -301,7 +301,7 @@ final class Utils {
|
||||
record.attach(configuration);
|
||||
}
|
||||
|
||||
return new RecordStub<R>(configuration, record);
|
||||
return new RecordDelegate<R>(configuration, record);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Could not construct new record", e);
|
||||
@ -2398,10 +2398,10 @@ final class Utils {
|
||||
}
|
||||
|
||||
return Utils.newRecord((Class<UDTRecord<?>>) type)
|
||||
.initialise(new RecordInitialiser<UDTRecord<?>, SQLException>() {
|
||||
.operate(new RecordOperation<UDTRecord<?>, SQLException>() {
|
||||
|
||||
@Override
|
||||
public UDTRecord<?> initialise(UDTRecord<?> record) throws SQLException {
|
||||
public UDTRecord<?> operate(UDTRecord<?> record) throws SQLException {
|
||||
List<String> values = PostgresUtils.toPGObject(object.toString());
|
||||
|
||||
Row row = record.fieldsRow();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user