[#1846] Add ResultQuery.keepResultSet() to indicate whether to keep
references to a potentially updatable JDBC ResultSet from Result
This commit is contained in:
parent
a580bebbfe
commit
cfce76a651
164
jOOQ-test/src/org/jooq/test/_/testcases/KeepResultSetTests.java
Normal file
164
jOOQ-test/src/org/jooq/test/_/testcases/KeepResultSetTests.java
Normal file
@ -0,0 +1,164 @@
|
||||
/**
|
||||
* Copyright (c) 2009-2013, Lukas Eder, lukas.eder@gmail.com
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed to you under the Apache License, Version 2.0
|
||||
* (the "License"); You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* . Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* . Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* . Neither the name "jOOQ" nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.jooq.test._.testcases;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.jooq.KeepResultSetMode.CLOSE_AFTER_FETCH;
|
||||
import static org.jooq.KeepResultSetMode.KEEP_AFTER_FETCH;
|
||||
import static org.jooq.KeepResultSetMode.UPDATE_ON_CHANGE;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.jooq.Cursor;
|
||||
import org.jooq.Record1;
|
||||
import org.jooq.Record2;
|
||||
import org.jooq.Record3;
|
||||
import org.jooq.Record6;
|
||||
import org.jooq.Result;
|
||||
import org.jooq.TableRecord;
|
||||
import org.jooq.UpdatableRecord;
|
||||
import org.jooq.test.BaseTest;
|
||||
import org.jooq.test.jOOQAbstractTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class KeepResultSetTests<
|
||||
A extends UpdatableRecord<A> & Record6<Integer, String, String, Date, Integer, ?>,
|
||||
AP,
|
||||
B extends UpdatableRecord<B>,
|
||||
S extends UpdatableRecord<S> & Record1<String>,
|
||||
B2S extends UpdatableRecord<B2S> & Record3<String, Integer, Integer>,
|
||||
BS extends UpdatableRecord<BS>,
|
||||
L extends TableRecord<L> & Record2<String, String>,
|
||||
X extends TableRecord<X>,
|
||||
DATE extends UpdatableRecord<DATE>,
|
||||
BOOL extends UpdatableRecord<BOOL>,
|
||||
D extends UpdatableRecord<D>,
|
||||
T extends UpdatableRecord<T>,
|
||||
U extends TableRecord<U>,
|
||||
UU extends UpdatableRecord<UU>,
|
||||
I extends TableRecord<I>,
|
||||
IPK extends UpdatableRecord<IPK>,
|
||||
T725 extends UpdatableRecord<T725>,
|
||||
T639 extends UpdatableRecord<T639>,
|
||||
T785 extends TableRecord<T785>>
|
||||
extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T725, T639, T785> {
|
||||
|
||||
public KeepResultSetTests(jOOQAbstractTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T725, T639, T785> delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
private void testFailUpdateRow(ResultSet rs) {
|
||||
try {
|
||||
rs.updateRow();
|
||||
fail();
|
||||
}
|
||||
catch (SQLException expected) {}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeepRSWithCloseAfterFetch() throws Exception {
|
||||
Result<B> b1 = create().selectFrom(TBook()).fetch();
|
||||
assertNull(b1.resultSet());
|
||||
|
||||
Result<B> b2 = create().selectFrom(TBook()).keepResultSet(CLOSE_AFTER_FETCH).fetch();
|
||||
assertNull(b2.resultSet());
|
||||
|
||||
Cursor<B> c1 = create().selectFrom(TBook()).keepResultSet(CLOSE_AFTER_FETCH).fetchLazy();
|
||||
while (c1.hasNext()) {
|
||||
Result<B> result = c1.fetch(1);
|
||||
assertNull(result.resultSet());
|
||||
assertNotNull(c1.resultSet());
|
||||
}
|
||||
|
||||
assertNull(c1.resultSet());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeepRSWithKeepAfterFetch() throws Exception {
|
||||
Result<B> b2 = create().selectFrom(TBook()).keepResultSet(KEEP_AFTER_FETCH).fetch();
|
||||
assertNotNull(b2.resultSet());
|
||||
testFailUpdateRow(b2.resultSet());
|
||||
b2.close();
|
||||
assertNull(b2.resultSet());
|
||||
|
||||
Cursor<B> c1 = create().selectFrom(TBook()).keepResultSet(KEEP_AFTER_FETCH).fetchLazy();
|
||||
while (c1.hasNext()) {
|
||||
Result<B> result = c1.fetch(1);
|
||||
assertNotNull(result.resultSet());
|
||||
assertNotNull(c1.resultSet());
|
||||
}
|
||||
|
||||
assertNotNull(c1.resultSet());
|
||||
c1.close();
|
||||
assertNull(c1.resultSet());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeepRSWithUpdateOnChange() throws Exception {
|
||||
jOOQAbstractTest.reset = false;
|
||||
|
||||
Result<B> books =
|
||||
create().selectFrom(TBook())
|
||||
.orderBy(TBook_ID())
|
||||
.keepResultSet(UPDATE_ON_CHANGE)
|
||||
.fetch();
|
||||
|
||||
assertNotNull(books.resultSet());
|
||||
for (int i = 0; i < books.size(); i++) {
|
||||
books.get(i).setValue(TBook_TITLE(), "Title " + i);
|
||||
}
|
||||
|
||||
Result<B> booksTest = getBooks();
|
||||
assertEquals(
|
||||
asList("Title 0", "Title 1", "Title 2", "Title 3"),
|
||||
booksTest.getValues(TBook_TITLE()));
|
||||
|
||||
// After closing, setting values to records should no longer have any
|
||||
// effect
|
||||
books.close();
|
||||
assertNull(books.resultSet());
|
||||
books.get(0).setValue(TBook_TITLE(), "XX");
|
||||
assertEquals("Title 0", getBook(1).getValue(TBook_TITLE()));
|
||||
}
|
||||
}
|
||||
@ -121,6 +121,7 @@ import org.jooq.test._.testcases.GroupByTests;
|
||||
import org.jooq.test._.testcases.InsertUpdateTests;
|
||||
import org.jooq.test._.testcases.JDBCTests;
|
||||
import org.jooq.test._.testcases.JoinTests;
|
||||
import org.jooq.test._.testcases.KeepResultSetTests;
|
||||
import org.jooq.test._.testcases.LoaderTests;
|
||||
import org.jooq.test._.testcases.MetaDataTests;
|
||||
import org.jooq.test._.testcases.OrderByTests;
|
||||
@ -2214,6 +2215,21 @@ public abstract class jOOQAbstractTest<
|
||||
new BenchmarkTests(this).testBenchmarkSelect();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeepRSWithCloseAfterFetch() throws Exception {
|
||||
new KeepResultSetTests(this).testKeepRSWithCloseAfterFetch();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeepRSWithKeepAfterFetch() throws Exception {
|
||||
new KeepResultSetTests(this).testKeepRSWithKeepAfterFetch();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeepRSWithUpdateOnChange() throws Exception {
|
||||
new KeepResultSetTests(this).testKeepRSWithUpdateOnChange();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeepStatement() throws Exception {
|
||||
new StatementTests(this).testKeepStatement();
|
||||
|
||||
109
jOOQ/src/main/java/org/jooq/KeepResultSetMode.java
Normal file
109
jOOQ/src/main/java/org/jooq/KeepResultSetMode.java
Normal file
@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Copyright (c) 2009-2013, Lukas Eder, lukas.eder@gmail.com
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed to you under the Apache License, Version 2.0
|
||||
* (the "License"); You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* . Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* . Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* . Neither the name "jOOQ" nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.jooq;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
|
||||
/**
|
||||
* A {@link ResultQuery}'s execution mode with respect to keeping open JDBC
|
||||
* {@link ResultSet} references after fetching.
|
||||
* <p>
|
||||
* This mode type is used with
|
||||
* {@link ResultQuery#keepResultSet(KeepResultSetMode)} to indicate how to deal
|
||||
* with JDBC {@link ResultSet} references after fetching them into jOOQ
|
||||
* {@link Result} objects.
|
||||
* <p>
|
||||
* See the various modes for details.
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
public enum KeepResultSetMode {
|
||||
|
||||
/**
|
||||
* Close the JDBC {@link ResultSet} after consuming it (this is the
|
||||
* default).
|
||||
*/
|
||||
CLOSE_AFTER_FETCH,
|
||||
|
||||
/**
|
||||
* Keep the JDBC {@link ResultSet} after consuming it.
|
||||
* <p>
|
||||
* Client code must assure that the {@link ResultSet} is closed explicitly
|
||||
* to free database resources. Closing can be done through any of these
|
||||
* methods:
|
||||
* <ul>
|
||||
* <li>{@link ResultSet#close()}</li>
|
||||
* <li>{@link Result#close()}</li>
|
||||
* <li>{@link Cursor#close()}</li>
|
||||
* </ul>
|
||||
*/
|
||||
KEEP_AFTER_FETCH,
|
||||
|
||||
/**
|
||||
* Keep the JDBC {@link ResultSet} after consuming it, updating the
|
||||
* <code>ResultSet</code> at every change of a {@link Record}.
|
||||
* <p>
|
||||
* TODO: More details here
|
||||
* <p>
|
||||
* Client code must assure that the {@link ResultSet} is closed explicitly
|
||||
* to free database resources. Closing can be done through any of these
|
||||
* methods:
|
||||
* <ul>
|
||||
* <li>{@link ResultSet#close()}</li>
|
||||
* <li>{@link Result#close()}</li>
|
||||
* <li>{@link Cursor#close()}</li>
|
||||
* </ul>
|
||||
*/
|
||||
UPDATE_ON_CHANGE,
|
||||
|
||||
/**
|
||||
* Keep the JDBC {@link ResultSet} after consuming it, updating the
|
||||
* <code>ResultSet</code> at every call to {@link Record#store()}, or
|
||||
* {@link Result#store()} (<strong>This is not yet supported</strong>).
|
||||
* <p>
|
||||
* TODO: More details here
|
||||
* <p>
|
||||
* Client code must assure that the {@link ResultSet} is closed explicitly
|
||||
* to free database resources. Closing can be done through any of these
|
||||
* methods:
|
||||
* <ul>
|
||||
* <li>{@link ResultSet#close()}</li>
|
||||
* <li>{@link Result#close()}</li>
|
||||
* <li>{@link Cursor#close()}</li>
|
||||
* </ul>
|
||||
*/
|
||||
UPDATE_ON_STORE
|
||||
}
|
||||
@ -989,6 +989,18 @@ public interface ResultQuery<R extends Record> extends Query {
|
||||
@Override
|
||||
ResultQuery<R> keepStatement(boolean keepStatement);
|
||||
|
||||
/**
|
||||
* Indicate how to deal with the JDBC {@link ResultSet} when fetching data
|
||||
* into jOOQ {@link Result} or {@link Cursor} objects.
|
||||
* <p>
|
||||
* TODO: More info here.
|
||||
* <p>
|
||||
* <strong>Note:</strong> If JDBC <code>ResultSet</code> references are kept
|
||||
* open after fetching data through jOOQ, you must explicitly close them
|
||||
* using either {@link Result#close()}, or {@link Cursor#close()}
|
||||
*/
|
||||
ResultQuery<R> keepResultSet(KeepResultSetMode mode);
|
||||
|
||||
/**
|
||||
* Specify the maximum number of rows returned by the underlying
|
||||
* {@link Statement}.
|
||||
|
||||
@ -306,7 +306,7 @@ abstract class AbstractQuery extends AbstractQueryPart implements Query, Attacha
|
||||
finally {
|
||||
|
||||
// [#2385] Successful fetchLazy() needs to keep open resources
|
||||
if (!keepResult() || ctx.exception() != null) {
|
||||
if (!keepResultSet() || ctx.exception() != null) {
|
||||
Utils.safeClose(listener, ctx, keepStatement());
|
||||
}
|
||||
|
||||
@ -329,7 +329,7 @@ abstract class AbstractQuery extends AbstractQueryPart implements Query, Attacha
|
||||
* Default implementation to indicate whether this query should close the
|
||||
* {@link ResultSet} after execution. Subclasses may override this method.
|
||||
*/
|
||||
protected boolean keepResult() {
|
||||
protected boolean keepResultSet() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -42,9 +42,11 @@ import static org.jooq.impl.Utils.getAnnotatedMembers;
|
||||
import static org.jooq.impl.Utils.getMatchingGetter;
|
||||
import static org.jooq.impl.Utils.getMatchingMembers;
|
||||
import static org.jooq.impl.Utils.hasColumnAnnotations;
|
||||
import static org.jooq.impl.Utils.translate;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -78,6 +80,8 @@ abstract class AbstractRecord extends AbstractStore implements Record {
|
||||
|
||||
final RowImpl fields;
|
||||
final Value<?>[] values;
|
||||
transient ResultSet rs;
|
||||
transient int rsIndex;
|
||||
|
||||
AbstractRecord(Collection<? extends Field<?>> fields) {
|
||||
this(new RowImpl(fields));
|
||||
@ -289,6 +293,23 @@ abstract class AbstractRecord extends AbstractStore implements Record {
|
||||
changed(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (rs != null) {
|
||||
try {
|
||||
if (rs.getRow() != rsIndex) {
|
||||
rs.absolute(rsIndex);
|
||||
}
|
||||
|
||||
// [#1846] TODO: Add more typesafety here
|
||||
rs.updateObject(fieldsRow().indexOf(field) + 1, value);
|
||||
|
||||
// [#1846] TODO: Update only in case of KeepResultSetMode.UPDATE_ON_CHANGE
|
||||
rs.updateRow();
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw translate("Error when updating ResultSet", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -39,6 +39,9 @@ import static java.sql.ResultSet.CONCUR_UPDATABLE;
|
||||
import static java.sql.ResultSet.TYPE_SCROLL_SENSITIVE;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.concurrent.Executors.newSingleThreadExecutor;
|
||||
import static org.jooq.KeepResultSetMode.CLOSE_AFTER_FETCH;
|
||||
import static org.jooq.KeepResultSetMode.UPDATE_ON_CHANGE;
|
||||
import static org.jooq.KeepResultSetMode.UPDATE_ON_STORE;
|
||||
import static org.jooq.SQLDialect.ASE;
|
||||
import static org.jooq.SQLDialect.CUBRID;
|
||||
import static org.jooq.SQLDialect.SQLSERVER;
|
||||
@ -63,6 +66,7 @@ import org.jooq.ExecuteContext;
|
||||
import org.jooq.ExecuteListener;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.FutureResult;
|
||||
import org.jooq.KeepResultSetMode;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.RecordHandler;
|
||||
import org.jooq.RecordMapper;
|
||||
@ -88,6 +92,7 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
|
||||
private static final JooqLogger log = JooqLogger.getLogger(AbstractResultQuery.class);
|
||||
|
||||
private int maxRows;
|
||||
private KeepResultSetMode keepResultSetMode;
|
||||
private int resultSetConcurrency;
|
||||
private int resultSetType;
|
||||
private int resultSetHoldability;
|
||||
@ -136,6 +141,15 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
|
||||
return (ResultQuery<R>) super.keepStatement(k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ResultQuery<R> keepResultSet(KeepResultSetMode mode) {
|
||||
if (mode == UPDATE_ON_STORE)
|
||||
throw new UnsupportedOperationException("UPDATE_ON_STORE is not yet supported");
|
||||
|
||||
this.keepResultSetMode = mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ResultQuery<R> maxRows(int rows) {
|
||||
this.maxRows = rows;
|
||||
@ -210,6 +224,12 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
|
||||
}
|
||||
}
|
||||
|
||||
// [#1846] When updatable Results are fetched
|
||||
else if (keepResultSetMode == UPDATE_ON_CHANGE ||
|
||||
keepResultSetMode == UPDATE_ON_STORE) {
|
||||
ctx.statement(ctx.connection().prepareStatement(ctx.sql(), TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE));
|
||||
}
|
||||
|
||||
// [#1296] These dialects do not implement FOR UPDATE. But the same
|
||||
// effect can be achieved using ResultSet.CONCUR_UPDATABLE
|
||||
else if (isForUpdate() && asList(CUBRID, SQLSERVER).contains(ctx.configuration().dialect())) {
|
||||
@ -276,7 +296,7 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
|
||||
if (!many) {
|
||||
if (ctx.resultSet() != null) {
|
||||
Field<?>[] fields = getFields(ctx.resultSet().getMetaData());
|
||||
cursor = new CursorImpl<R>(ctx, listener, fields, internIndexes(fields), lazy, keepStatement(), getRecordType());
|
||||
cursor = new CursorImpl<R>(ctx, listener, fields, internIndexes(fields), keepStatement(), keepResultSet(), keepResultSetMode, getRecordType());
|
||||
|
||||
if (!lazy) {
|
||||
result = cursor.fetch();
|
||||
@ -297,7 +317,7 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
|
||||
anyResults = true;
|
||||
|
||||
Field<?>[] fields = new MetaDataFieldProvider(ctx.configuration(), ctx.resultSet().getMetaData()).getFields();
|
||||
Cursor<Record> c = new CursorImpl<Record>(ctx, listener, fields, internIndexes(fields), false, true);
|
||||
Cursor<Record> c = new CursorImpl<Record>(ctx, listener, fields, internIndexes(fields), true, false, CLOSE_AFTER_FETCH);
|
||||
results.add(c.fetch());
|
||||
|
||||
if (ctx.statement().getMoreResults()) {
|
||||
@ -328,8 +348,10 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean keepResult() {
|
||||
return lazy;
|
||||
protected final boolean keepResultSet() {
|
||||
return lazy
|
||||
|| keepResultSetMode == UPDATE_ON_CHANGE
|
||||
|| keepResultSetMode == UPDATE_ON_STORE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -35,6 +35,7 @@
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import static org.jooq.KeepResultSetMode.CLOSE_AFTER_FETCH;
|
||||
import static org.jooq.impl.Utils.fieldArray;
|
||||
import static org.jooq.util.sqlite.SQLiteDSL.rowid;
|
||||
|
||||
@ -344,7 +345,7 @@ abstract class AbstractStoreQuery<R extends Record> extends AbstractQuery implem
|
||||
ExecuteListener listener2 = new ExecuteListeners(ctx2);
|
||||
|
||||
ctx2.resultSet(rs);
|
||||
returned = new CursorImpl<R>(ctx2, listener2, fieldArray(returning), null, true, false).fetch().into(getInto());
|
||||
returned = new CursorImpl<R>(ctx2, listener2, fieldArray(returning), null, false, true, CLOSE_AFTER_FETCH).fetch().into(getInto());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
package org.jooq.impl;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static org.jooq.KeepResultSetMode.CLOSE_AFTER_FETCH;
|
||||
import static org.jooq.impl.Utils.DATA_LOCK_ROWS_FOR_UPDATE;
|
||||
|
||||
import java.io.InputStream;
|
||||
@ -66,6 +67,7 @@ import org.jooq.Cursor;
|
||||
import org.jooq.ExecuteContext;
|
||||
import org.jooq.ExecuteListener;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.KeepResultSetMode;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.RecordHandler;
|
||||
import org.jooq.RecordMapper;
|
||||
@ -84,26 +86,29 @@ class CursorImpl<R extends Record> implements Cursor<R> {
|
||||
private final ExecuteListener listener;
|
||||
private final Field<?>[] fields;
|
||||
private final boolean[] intern;
|
||||
private final boolean keepResult;
|
||||
private final boolean keepResultSet;
|
||||
private final KeepResultSetMode keepResultSetMode;
|
||||
private final boolean keepStatement;
|
||||
private final Class<? extends R> type;
|
||||
private boolean isClosed;
|
||||
|
||||
private transient CursorResultSet rs;
|
||||
private transient int rsIndex;
|
||||
private transient Iterator<R> iterator;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
CursorImpl(ExecuteContext ctx, ExecuteListener listener, Field<?>[] fields, int[] internIndexes, boolean keepResult, boolean keepStatement) {
|
||||
this(ctx, listener, fields, internIndexes, keepResult, keepStatement, (Class<? extends R>) RecordImpl.class);
|
||||
CursorImpl(ExecuteContext ctx, ExecuteListener listener, Field<?>[] fields, int[] internIndexes, boolean keepStatement, boolean keepResultSet, KeepResultSetMode keepResultSetMode) {
|
||||
this(ctx, listener, fields, internIndexes, keepStatement, keepResultSet, keepResultSetMode, (Class<? extends R>) RecordImpl.class);
|
||||
}
|
||||
|
||||
CursorImpl(ExecuteContext ctx, ExecuteListener listener, Field<?>[] fields, int[] internIndexes, boolean keepResult, boolean keepStatement, Class<? extends R> type) {
|
||||
CursorImpl(ExecuteContext ctx, ExecuteListener listener, Field<?>[] fields, int[] internIndexes, boolean keepStatement, boolean keepResultSet, KeepResultSetMode keepResultSetMode, Class<? extends R> type) {
|
||||
this.ctx = ctx;
|
||||
this.listener = (listener != null ? listener : new ExecuteListeners(ctx));
|
||||
this.fields = fields;
|
||||
this.type = type;
|
||||
this.keepResult = keepResult;
|
||||
this.keepStatement = keepStatement;
|
||||
this.keepResultSet = keepResultSet;
|
||||
this.keepResultSetMode = keepResultSetMode;
|
||||
this.rs = new CursorResultSet();
|
||||
this.intern = new boolean[fields.length];
|
||||
|
||||
@ -114,6 +119,10 @@ class CursorImpl<R extends Record> implements Cursor<R> {
|
||||
}
|
||||
}
|
||||
|
||||
final boolean closeAfterFetch() {
|
||||
return keepResultSetMode == null || keepResultSetMode == CLOSE_AFTER_FETCH;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public final Row fieldsRow() {
|
||||
@ -283,7 +292,7 @@ class CursorImpl<R extends Record> implements Cursor<R> {
|
||||
// [#1868] If this Result / Cursor was "kept" through a lazy
|
||||
// execution, we must assure that the ExecuteListener lifecycle is
|
||||
// correctly terminated.
|
||||
Utils.safeClose(listener, ctx, keepStatement, keepResult);
|
||||
Utils.safeClose(listener, ctx, keepStatement, keepResultSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1258,11 +1267,13 @@ class CursorImpl<R extends Record> implements Cursor<R> {
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private final R fetchOne() {
|
||||
R record = null;
|
||||
AbstractRecord record = null;
|
||||
|
||||
try {
|
||||
if (!isClosed && rs.next()) {
|
||||
++rsIndex;
|
||||
|
||||
// [#1296] Force a row-lock by updating the row if the
|
||||
// FOR UPDATE clause is simulated
|
||||
@ -1271,16 +1282,23 @@ class CursorImpl<R extends Record> implements Cursor<R> {
|
||||
rs.updateRow();
|
||||
}
|
||||
|
||||
record = Utils.newRecord(type, fields, ctx.configuration());
|
||||
record = (AbstractRecord) Utils.newRecord(type, fields, ctx.configuration());
|
||||
|
||||
// [#1846] Add a reference to the Cursor's ResultSet if
|
||||
// Updatable ResultSets are requested
|
||||
if (!closeAfterFetch()) {
|
||||
record.rs = rs;
|
||||
record.rsIndex = rsIndex;
|
||||
}
|
||||
|
||||
ctx.record(record);
|
||||
listener.recordStart(ctx);
|
||||
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
setValue((AbstractRecord) record, fields[i], i);
|
||||
setValue(record, fields[i], i);
|
||||
|
||||
if (intern[i]) {
|
||||
((AbstractRecord) record).getValue0(i).intern();
|
||||
record.getValue0(i).intern();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1297,11 +1315,13 @@ class CursorImpl<R extends Record> implements Cursor<R> {
|
||||
// [#1868] [#2373] [#2385] This calls through to Utils.safeClose()
|
||||
// if necessary, lazy-terminating the ExecuteListener lifecycle if
|
||||
// the result is not eager-fetched.
|
||||
if (record == null) {
|
||||
// [#1846] When fetching updatable Results, do not close the
|
||||
// Cursor's ResultSet!
|
||||
if (record == null && closeAfterFetch()) {
|
||||
CursorImpl.this.close();
|
||||
}
|
||||
|
||||
return record;
|
||||
return (R) record;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
|
||||
package org.jooq.impl;
|
||||
|
||||
import static org.jooq.KeepResultSetMode.CLOSE_AFTER_FETCH;
|
||||
import static org.jooq.SQLDialect.ASE;
|
||||
import static org.jooq.SQLDialect.CUBRID;
|
||||
import static org.jooq.SQLDialect.DB2;
|
||||
@ -561,7 +562,7 @@ class DSLContextImpl implements DSLContext, Serializable {
|
||||
ExecuteListener listener = new ExecuteListeners(ctx);
|
||||
|
||||
ctx.resultSet(rs);
|
||||
return new CursorImpl<Record>(ctx, listener, fields, null, true, false);
|
||||
return new CursorImpl<Record>(ctx, listener, fields, null, false, true, CLOSE_AFTER_FETCH);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -94,12 +94,12 @@ class ResultImpl<R extends Record> implements Result<R>, AttachableInternal {
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 6416154375799578362L;
|
||||
private static final long serialVersionUID = 6416154375799578362L;
|
||||
|
||||
private Configuration configuration;
|
||||
private ResultSet rs;
|
||||
private final Fields fields;
|
||||
private final List<R> records;
|
||||
private Configuration configuration;
|
||||
private transient ResultSet rs;
|
||||
private final Fields fields;
|
||||
private final List<R> records;
|
||||
|
||||
ResultImpl(Configuration configuration, ResultSet rs, Collection<? extends Field<?>> fields) {
|
||||
this(configuration, rs, new Fields(fields));
|
||||
@ -1198,7 +1198,7 @@ class ResultImpl<R extends Record> implements Result<R>, AttachableInternal {
|
||||
if (rs != null) {
|
||||
rs.close();
|
||||
rs = null;
|
||||
|
||||
|
||||
for (Record record : this) {
|
||||
if (record instanceof AbstractRecord) {
|
||||
((AbstractRecord) record).rs = null;
|
||||
|
||||
@ -56,6 +56,7 @@ import org.jooq.ForeignKey;
|
||||
import org.jooq.FutureResult;
|
||||
import org.jooq.GroupField;
|
||||
import org.jooq.JoinType;
|
||||
import org.jooq.KeepResultSetMode;
|
||||
import org.jooq.Operator;
|
||||
import org.jooq.Param;
|
||||
import org.jooq.QueryPart;
|
||||
@ -918,6 +919,11 @@ class SelectImpl<R extends Record> extends AbstractDelegatingQuery<Select<R>> im
|
||||
return getDelegate().maxRows(rows);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultQuery<R> keepResultSet(KeepResultSetMode mode) {
|
||||
return getDelegate().keepResultSet(mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ResultQuery<R> resultSetConcurrency(int resultSetConcurrency) {
|
||||
return getDelegate().resultSetConcurrency(resultSetConcurrency);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user