[#1846] [#2265] Reverted changes for 3.1

This commit is contained in:
Lukas Eder 2013-06-05 09:42:26 +02:00
parent 6f6dad4bf3
commit ce7a71b346
18 changed files with 110 additions and 1011 deletions

View File

@ -1,441 +0,0 @@
/**
* 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.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
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;
import org.jooq.Record3;
import org.jooq.Record6;
import org.jooq.Result;
import org.jooq.Select;
import org.jooq.TableRecord;
import org.jooq.UpdatableRecord;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.DefaultExecuteListener;
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);
}
/**
* This listener is used to check if a <code>SELECT</code> statement is
* issued after a call to {@link Record#refresh()}.
*/
private static class NoStatementAfterCRUDListener extends DefaultExecuteListener {
/**
* Default UID
*/
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() != 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");
}
}
}
}
}
}
private void testFailUpdateRow(ResultSet rs) {
try {
rs.updateRow();
fail();
}
catch (SQLException expected) {}
}
private void testFailRefresh(Record record) {
try {
record.refresh();
fail();
}
catch (DataAccessException expected) {}
}
@Test
public void testKeepRSWithCloseAfterFetch() throws Exception {
DSLContext create = create(new NoStatementAfterCRUDListener(Select.class, "refresh"));
Result<B> b1 = create.selectFrom(TBook()).fetch();
assertNull(b1.resultSet());
// Use plain SQL to prevent fetching of UpdatableRecord
Result<Record> b2 = create.select().from(TBook().getName()).keepResultSet(CLOSE_AFTER_FETCH).fetch();
assertNull(b2.resultSet());
// Changing a TITLE has no effect
Record r = b2.get(0);
r.setValue(TBook_TITLE(), "XX");
assertTrue(r.changed());
assertFalse(r.original().equals(r));
assertEquals(BOOK_TITLES.get(0), getBook(1).getValue(TBook_TITLE()));
testFailRefresh(r);
Cursor<Record> c1 = create.select().from(TBook().getName()).keepResultSet(CLOSE_AFTER_FETCH).fetchLazy();
assertTrue(c1.closesAfterFetch());
while (c1.hasNext()) {
Result<Record> result = c1.fetch(1);
assertNull(result.get(0).resultSet());
assertNull(result.resultSet());
assertNotNull(c1.resultSet());
}
assertNull(c1.resultSet());
}
private void testOriginalBook1(B book) {
assertEquals(BOOK_TITLES.get(0), book.getValue(TBook_TITLE()));
assertEquals(BOOK_AUTHOR_IDS.get(0), book.getValue(TBook_AUTHOR_ID()));
assertFalse(book.changed());
assertEquals(book.original(), book);
assertNotNull(book.resultSet());
}
private void testModifiedBook1(B book) {
book.setValue(TBook_TITLE(), "XX");
book.setValue(TBook_AUTHOR_ID(), 15);
assertEquals("XX", book.getValue(TBook_TITLE()));
assertEquals(15, (int) book.getValue(TBook_AUTHOR_ID()));
assertTrue(book.changed());
assertFalse(book.original().equals(book));
}
@Test
public void testKeepRSWithKeepAfterFetch() throws Exception {
switch (dialect()) {
case SQLITE:
log.info("SKIPPING", "KeepResultSet tests");
return;
}
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());
assertNotNull(r.resultSet());
testFailUpdateRow(b2.resultSet());
testModifiedBook1(r);
B dbBook = getBook(1);
assertEquals(BOOK_TITLES.get(0), dbBook.getValue(TBook_TITLE()));
assertEquals(BOOK_AUTHOR_IDS.get(0), dbBook.getValue(TBook_AUTHOR_ID()));
// Refresh the record
r.refresh(TBook_TITLE());
assertEquals(BOOK_TITLES.get(0), r.getValue(TBook_TITLE()));
assertEquals(15, (int) r.getValue(TBook_AUTHOR_ID()));
assertTrue(r.changed());
assertFalse(r.original().equals(r));
r.refresh();
testOriginalBook1(r);
b2.close();
assertNull(b2.resultSet());
// Changing a TITLE still has no effect
testModifiedBook1(r);
// But refreshing should work through a new query (UpdatableRecord)
// For this, remove the NoSelectAfterRefreshListener
r.attach(create().configuration());
r.refresh();
testOriginalBook1(r);
// Further refreshing should again not trigger new SQL statements
r.attach(create.configuration());
testModifiedBook1(r);
r.refresh();
testOriginalBook1(r);
r.close();
Cursor<Record> c1 = create.select().from(TBook().getName()).keepResultSet(KEEP_AFTER_FETCH).fetchLazy();
assertFalse(c1.closesAfterFetch());
while (c1.hasNext()) {
Result<Record> result = c1.fetch(1);
assertNotNull(result.get(0).resultSet());
assertNotNull(result.resultSet());
assertNotNull(c1.resultSet());
}
assertNotNull(c1.resultSet());
c1.close();
assertNull(c1.resultSet());
}
@Test
public void testKeepRSWithUpdateOnChange() throws Exception {
switch (dialect()) {
// There's a critical bug in Derby. When this test is executed, the
// connection is killed
// https://issues.apache.org/jira/browse/DERBY-6228
case DERBY:
case SQLITE:
log.info("SKIPPING", "KeepResultSet tests");
return;
}
jOOQAbstractTest.reset = false;
DSLContext create = create(new NoStatementAfterCRUDListener(Select.class, "refresh"));
// Use plain SQL to prevent fetching of UpdatableRecord
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++) {
assertNotNull(books.get(i).resultSet());
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
assertNotNull(books.resultSet());
books.close();
assertNull(books.resultSet());
books.get(0).setValue(TBook_TITLE(), "XX");
assertEquals("Title 0", getBook(1).getValue(TBook_TITLE()));
}
@Test
public void testKeepRSWithUpdateOnChangeLazy() throws Exception {
switch (dialect()) {
// There's a critical bug in Derby. When this test is executed, the
// connection is killed
// https://issues.apache.org/jira/browse/DERBY-6228
case DERBY:
case SQLITE:
log.info("SKIPPING", "KeepResultSet tests");
return;
}
jOOQAbstractTest.reset = false;
DSLContext create = create(new NoStatementAfterCRUDListener(Select.class, "refresh"));
Cursor<B> books =
create.selectFrom(TBook())
.orderBy(TBook_ID())
.keepResultSet(UPDATE_ON_CHANGE)
.fetchLazy();
assertNotNull(books.resultSet());
assertFalse(books.closesAfterFetch());
while (books.hasNext()) {
B book = books.fetchOne();
assertNotNull(book.resultSet());
book.setValue(TBook_TITLE(), "Title X");
}
Result<B> booksTest = getBooks();
assertEquals(
Collections.nCopies(4, "Title X"),
booksTest.getValues(TBook_TITLE()));
// After closing, setting values to records should no longer have any
// effect
assertNotNull(books.resultSet());
assertFalse(books.isClosed());
books.close();
assertNull(books.resultSet());
assertTrue(books.isClosed());
}
@Test
public void testKeepRSWithUpdateOnChangeFetchOne() throws Exception {
switch (dialect()) {
case SQLITE:
log.info("SKIPPING", "KeepResultSet tests");
return;
}
jOOQAbstractTest.reset = false;
DSLContext create = create(new NoStatementAfterCRUDListener(Select.class, "refresh"));
Record book =
create.select()
.from(TBook().getName())
.where(TBook_ID().eq(1))
.keepResultSet(UPDATE_ON_CHANGE)
.fetchOne();
assertNotNull(book.resultSet());
assertFalse(book.changed());
assertEquals(book, book.original());
book.setValue(TBook_AUTHOR_ID(), 2);
assertEquals(2, (int) book.getValue(TBook_AUTHOR_ID()));
assertFalse(book.changed());
assertEquals(book, book.original());
book.refresh();
assertEquals(2, (int) book.getValue(TBook_AUTHOR_ID()));
assertFalse(book.changed());
assertEquals(book, book.original());
try {
book.setValue(TBook_AUTHOR_ID(), -1);
fail();
}
catch (DataAccessException expected) {}
assertEquals(2, (int) book.getValue(TBook_AUTHOR_ID()));
assertFalse(book.changed());
assertEquals(book, book.original());
book.close();
assertNull(book.resultSet());
}
@Test
public void testKeepRSWithUpdateOnChangeDelete() throws Exception {
switch (dialect()) {
// There's a critical bug in Derby. When this test is executed, the
// connection is killed
// https://issues.apache.org/jira/browse/DERBY-6228
case DERBY:
case SQLITE:
log.info("SKIPPING", "KeepResultSet tests");
return;
}
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);
}
/*
* TODO: More tests:
* -----------------
*
* [#2265] Pull up store(), delete(), refresh() from UpdatableRecord
* - store() will perform a scan and update if UPDATE_ON_STORE is set. Otherwise: no-op
* - delete() will remove the record
*
* [#1846] Add ResultQuery.keepResultSet() with UPDATE_ON_CHANGE
* - Implement all data types from ResultSet.updateXXX() (e.g. updateInt(), etc)
* - Implement UPDATE_ON_STORE
*/
}

View File

@ -121,7 +121,6 @@ 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;
@ -2225,36 +2224,6 @@ 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 testKeepRSWithUpdateOnChangeLazy() throws Exception {
new KeepResultSetTests(this).testKeepRSWithUpdateOnChangeLazy();
}
@Test
public void testKeepRSWithUpdateOnChangeFetchOne() throws Exception {
new KeepResultSetTests(this).testKeepRSWithUpdateOnChangeFetchOne();
}
@Test
public void testKeepRSWithUpdateOnChangeDelete() throws Exception {
new KeepResultSetTests(this).testKeepRSWithUpdateOnChangeDelete();
}
@Test
public void testKeepStatement() throws Exception {
new StatementTests(this).testKeepStatement();

View File

@ -275,15 +275,6 @@ public interface Cursor<R extends Record> extends Iterable<R> {
*/
<Z extends Record> Result<Z> fetchInto(Table<Z> table) throws DataAccessException, MappingException;
/**
* Whether this <code>Cursor</code> closes itself after fetching all data.
* <p>
* By default, a <code>Cursor</code> will close itself after fetching all
* data. This behaviour can be overridden by
* {@link ResultQuery#keepResultSet(KeepResultSetMode)}, though.
*/
boolean closesAfterFetch();
/**
* Explicitly close the underlying {@link PreparedStatement} and
* {@link ResultSet}.

View File

@ -1,134 +0,0 @@
/**
* 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).
* <p>
* If not explicitly overridden by
* {@link ResultQuery#resultSetConcurrency(int)} or
* {@link ResultQuery#resultSetType(int)}, this will apply
* {@link ResultSet#CONCUR_READ_ONLY} and
* {@link ResultSet#TYPE_FORWARD_ONLY}.
*/
CLOSE_AFTER_FETCH,
/**
* Keep the JDBC {@link ResultSet} after consuming it.
* <p>
* If not explicitly overridden by
* {@link ResultQuery#resultSetConcurrency(int)} or
* {@link ResultQuery#resultSetType(int)}, this will apply
* {@link ResultSet#CONCUR_READ_ONLY} and
* {@link ResultSet#TYPE_SCROLL_SENSITIVE} (allowing for calls to
* {@link Record#refresh()} and {@link Result#refresh()}).
* <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>
* If not explicitly overridden by
* {@link ResultQuery#resultSetConcurrency(int)} or
* {@link ResultQuery#resultSetType(int)}, this will apply
* {@link ResultSet#CONCUR_UPDATABLE} and
* {@link ResultSet#TYPE_SCROLL_SENSITIVE}.
* <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>
* If not explicitly overridden by
* {@link ResultQuery#resultSetConcurrency(int)} or
* {@link ResultQuery#resultSetType(int)}, this will apply
* {@link ResultSet#CONCUR_UPDATABLE} and
* {@link ResultSet#TYPE_SCROLL_SENSITIVE}.
* <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
}

View File

@ -48,7 +48,6 @@ import java.util.Map;
import javax.persistence.Column;
import org.jooq.exception.DataAccessException;
import org.jooq.exception.DataTypeException;
import org.jooq.exception.MappingException;
import org.jooq.tools.Convert;
@ -1038,108 +1037,4 @@ public interface Record extends Attachable, Comparable<Record> {
@Override
int compareTo(Record record);
// -------------------------------------------------------------------------
// Methods related to the underlying ResultSet (if applicable)
// -------------------------------------------------------------------------
/**
* Refresh this record from the database.
* <p>
* A successful refresh results in the following:
* <ul>
* <li>{@link #valuesRow()} will have been restored to the respective values
* from the database</li>
* <li>{@link #original()} will match this record</li>
* <li>{@link #changed()} will be <code>false</code></li>
* </ul>
* <p>
* Refreshing can trigger any of the following actions:
* <ul>
* <li>Re-reading the underlying {@link #resultSet()}, if that
* <code>ResultSet</code> is available.</li>
* <li>Executing a new <code>SELECT</code> statement, if this is an
* {@link UpdatableRecord}.</li>
* <li>Failing, otherwise</li>
* </ul>
* <p>
* This is the same as calling <code>record.refresh(record.fields())</code>
*
* @throws DataAccessException This exception is thrown if
* <ul>
* <li>the underlying {@link #resultSet()} is in
* {@link ResultSet#TYPE_FORWARD_ONLY} mode, such that
* refreshing is not possible.</li> <li>something went wrong
* executing the refresh <code>SELECT</code> statement, if this
* is an {@link UpdatableRecord}.</li> <li>the record does not
* exist anymore in the database</li>
* </ul>
* @see UpdatableRecord#refresh()
* @see ResultQuery#keepResultSet(KeepResultSetMode)
*/
void refresh() throws DataAccessException;
/**
* Refresh parts of this record from the database.
* <p>
* A successful refresh results in the following:
* <ul>
* <li>{@link #valuesRow()} will have been restored to the respective values
* from the database</li>
* <li>{@link #original()} will match this record</li>
* <li>{@link #changed()} will be <code>false</code></li>
* </ul>
* <p>
* Refreshing can trigger any of the following actions:
* <ul>
* <li>Re-reading the underlying {@link #resultSet()}, if that
* <code>ResultSet</code> is available.</li>
* <li>Executing a new <code>SELECT</code> statement, if this is an
* {@link UpdatableRecord}.</li>
* <li>Failing, otherwise</li>
* </ul>
* <p>
* This is the same as calling <code>record.refresh(record.fields())</code>
*
* @throws DataAccessException This exception is thrown if
* <ul>
* <li>the underlying {@link #resultSet()} is in
* {@link ResultSet#TYPE_FORWARD_ONLY} mode, such that
* refreshing is not possible.</li> <li>something went wrong
* executing the refresh <code>SELECT</code> statement, if this
* is an {@link UpdatableRecord}.</li> <li>the record does not
* exist anymore in the database</li>
* </ul>
* @see UpdatableRecord#refresh()
* @see ResultQuery#keepResultSet(KeepResultSetMode)
*/
void refresh(Field<?>... fields) throws DataAccessException;
/**
* Close the underlying JDBC {@link ResultSet}, if applicable.
* <p>
* If this <code>Record</code> was created using
* {@link ResultQuery#keepResultSet(KeepResultSetMode)}, then it closes the
* underlying JDBC {@link ResultSet}. Otherwise, this method has no effect.
* <p>
* Note, this will close the <code>ResultSet</code> for all records that
* were fetched from the same <code>ResultQuery</code>.
*
* @throws DataAccessException If something went wrong closing the
* underlying {@link ResultSet}
* @see #resultSet()
*/
void close() throws DataAccessException;
/**
* Get the underlying JDBC {@link ResultSet}, if applicable.
* <p>
* This method returns the underlying JDBC {@link ResultSet}, if this
* <code>Record</code> was created using
* {@link ResultQuery#keepResultSet(KeepResultSetMode)}. Otherwise, this
* method returns <code>null</code>.
*
* @return The underlying JDBC <code>ResultSet</code>, or <code>null</code>
*/
ResultSet resultSet();
}

View File

@ -41,7 +41,6 @@ import java.sql.Statement;
import java.util.List;
import java.util.Map;
import org.jooq.exception.DataAccessException;
import org.jooq.exception.DataTypeException;
import org.jooq.exception.InvalidResultException;
import org.jooq.exception.MappingException;
@ -1082,33 +1081,4 @@ public interface Result<R extends Record> extends List<R>, Attachable {
* @see String#intern()
*/
Result<R> intern(String... fieldNames);
// -------------------------------------------------------------------------
// Methods related to the underlying ResultSet (if applicable)
// -------------------------------------------------------------------------
/**
* Close the underlying JDBC {@link ResultSet}, if applicable.
* <p>
* If this <code>Result</code> was created using
* {@link ResultQuery#keepResultSet(KeepResultSetMode)}, then it closes the
* underlying JDBC {@link ResultSet}. Otherwise, this method has no effect.
*
* @throws DataAccessException If something went wrong closing the
* underlying {@link ResultSet}
* @see #resultSet()
*/
void close() throws DataAccessException;
/**
* Get the underlying JDBC {@link ResultSet}, if applicable.
* <p>
* This method returns the underlying JDBC {@link ResultSet}, if this
* <code>Result</code> was created using
* {@link ResultQuery#keepResultSet(KeepResultSetMode)}. Otherwise, this
* method returns <code>null</code>.
*
* @return The underlying JDBC <code>ResultSet</code>, or <code>null</code>
*/
ResultSet resultSet();
}

View File

@ -991,18 +991,6 @@ 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}.

View File

@ -301,15 +301,63 @@ public interface UpdatableRecord<R extends UpdatableRecord<R>> extends TableReco
int delete() throws DataAccessException, DataChangedException;
/**
* {@inheritDoc}
* Refresh this record from the database.
* <p>
* A successful refresh results in the following:
* <ul>
* <li>{@link #valuesRow()} will have been restored to the respective values
* from the database</li>
* <li>{@link #original()} will match this record</li>
* <li>{@link #changed()} will be <code>false</code></li>
* </ul>
* <p>
* Refreshing can trigger any of the following actions:
* <ul>
* <li>Executing a new <code>SELECT</code> statement, if this is an
* {@link UpdatableRecord}.</li>
* <li>Failing, otherwise</li>
* </ul>
* <p>
* This is the same as calling <code>record.refresh(record.fields())</code>
*
* @throws DataAccessException This exception is thrown if
* <ul>
* <li>something went wrong executing the refresh <code>SELECT
* </code> statement, if this is an {@link UpdatableRecord}.
* </li> <li>the record does not exist anymore in the database
* </li>
* </ul>
*/
@Override
void refresh() throws DataAccessException;
/**
* {@inheritDoc}
* Refresh parts of this record from the database.
* <p>
* A successful refresh results in the following:
* <ul>
* <li>{@link #valuesRow()} will have been restored to the respective values
* from the database</li>
* <li>{@link #original()} will match this record</li>
* <li>{@link #changed()} will be <code>false</code></li>
* </ul>
* <p>
* Refreshing can trigger any of the following actions:
* <ul>
* <li>Executing a new <code>SELECT</code> statement, if this is an
* {@link UpdatableRecord}.</li>
* <li>Failing, otherwise</li>
* </ul>
* <p>
* This is the same as calling <code>record.refresh(record.fields())</code>
*
* @throws DataAccessException This exception is thrown if
* <ul>
* <li>something went wrong
* executing the refresh <code>SELECT</code> statement, if this
* is an {@link UpdatableRecord}.</li> <li>the record does not
* exist anymore in the database</li>
* </ul>
*/
@Override
void refresh(Field<?>... fields) throws DataAccessException;
/**

View File

@ -37,17 +37,14 @@
package org.jooq.impl;
import static java.util.Arrays.asList;
import static org.jooq.KeepResultSetMode.UPDATE_ON_CHANGE;
import static org.jooq.impl.Utils.getAnnotatedGetter;
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;
@ -57,16 +54,13 @@ import java.util.Map;
import org.jooq.Attachable;
import org.jooq.Converter;
import org.jooq.Field;
import org.jooq.KeepResultSetMode;
import org.jooq.Record;
import org.jooq.RecordMapper;
import org.jooq.Result;
import org.jooq.Table;
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;
/**
@ -84,9 +78,6 @@ abstract class AbstractRecord extends AbstractStore implements Record {
final RowImpl fields;
final Value<?>[] values;
transient KeepResultSetMode keepResultSetMode;
transient CursorResultSet rs;
transient int rsIndex;
AbstractRecord(Collection<? extends Field<?>> fields) {
this(new RowImpl(fields));
@ -276,53 +267,26 @@ abstract class AbstractRecord extends AbstractStore implements Record {
@Override
public final <T> void setValue(Field<T> field, T value) {
Value<T> val = getValue0(field);
UniqueKey<?> key = getPrimaryKey();
// [#1846] Execute this first to fail early, when UPDATE_ON_CHANGE fails
if (rs != null && keepResultSetMode == UPDATE_ON_CHANGE) {
int index = fieldsRow().indexOf(field);
int columnIndex = index + 1;
try {
if (rs.getRow() != rsIndex) {
rs.absolute(rsIndex);
}
// [#1846] TODO: Add more typesafety here
rs.updateObject(columnIndex, value);
// [#1846] TODO: Update only in case of KeepResultSetMode.UPDATE_ON_CHANGE
rs.updateRow();
}
catch (SQLException e) {
throw translate("Error when updating ResultSet", e);
}
setValue(index, new Value<Object>(value));
// Normal fields' changed flag is always set to true
if (key == null || !key.getFields().contains(field)) {
val.setValue(value);
}
// [#1846] In all other cases, correctly handle changed flags
// The primary key's changed flag might've been set previously
else if (val.isChanged()) {
val.setValue(value);
}
// [#979] If the primary key is being changed, all other fields' flags
// need to be set to true for in case this record is stored again, an
// INSERT statement will thus be issued
else {
UniqueKey<?> key = getPrimaryKey();
val.setValue(value, true);
// Normal fields' changed flag is always set to true
if (key == null || !key.getFields().contains(field)) {
val.setValue(value);
}
// The primary key's changed flag might've been set previously
else if (val.isChanged()) {
val.setValue(value);
}
// [#979] If the primary key is being changed, all other fields' flags
// need to be set to true for in case this record is stored again, an
// INSERT statement will thus be issued
else {
val.setValue(value, true);
if (val.isChanged()) {
changed(true);
}
if (val.isChanged()) {
changed(true);
}
}
}
@ -553,7 +517,7 @@ abstract class AbstractRecord extends AbstractStore implements Record {
@Override
public final ResultSet intoResultSet() {
ResultImpl<Record> result = new ResultImpl<Record>(configuration(), rs, fields.fields.fields);
ResultImpl<Record> result = new ResultImpl<Record>(configuration(), fields.fields.fields);
result.add(this);
return result.intoResultSet();
}
@ -690,68 +654,13 @@ abstract class AbstractRecord extends AbstractStore implements Record {
}
}
// -------------------------------------------------------------------------
// XXX: Methods related to the underlying ResultSet (if applicable)
// -------------------------------------------------------------------------
@Override
public final void refresh() {
refresh(fields.fields.fields);
}
/**
* {@inheritDoc}
* <p>
* Subclasses may override this
*/
@Override
public void refresh(Field<?>... f) {
checkRsAvailable("Cannot refresh record. No ResultSet available");
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);
}
catch (SQLException e) {
throw translate("Cannot refresh record", e);
}
}
private final void checkRsAvailable(String message) {
if (rs == null) {
throw new DataAccessException(message);
}
}
@Override
public final void close() {
try {
if (rs != null) {
rs.close();
rs = null;
}
}
catch (SQLException e) {
throw translate("Cannot close ResultSet", e);
}
}
@Override
public final ResultSet resultSet() {
return rs;
}
// ------------------------------------------------------------------------
// XXX: Object and Comparable API
// ------------------------------------------------------------------------
@Override
public String toString() {
Result<AbstractRecord> result = new ResultImpl<AbstractRecord>(configuration(), null, fields.fields.fields);
Result<AbstractRecord> result = new ResultImpl<AbstractRecord>(configuration(), fields.fields.fields);
result.add(this);
return result.toString();
}

View File

@ -35,15 +35,10 @@
*/
package org.jooq.impl;
import static java.sql.ResultSet.CONCUR_READ_ONLY;
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.KEEP_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;
@ -67,7 +62,6 @@ 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;
@ -92,7 +86,6 @@ 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;
@ -141,15 +134,6 @@ 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;
@ -224,17 +208,6 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
}
}
// [#1846] When scrollable Results are fetched
else if (keepResultSetMode == KEEP_AFTER_FETCH) {
ctx.statement(ctx.connection().prepareStatement(ctx.sql(), TYPE_SCROLL_SENSITIVE, CONCUR_READ_ONLY));
}
// [#1846] When scrollable and 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().family())) {
@ -284,7 +257,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), keepStatement(), keepResultSet(), keepResultSetMode, getRecordType());
cursor = new CursorImpl<R>(ctx, listener, fields, internIndexes(fields), keepStatement(), keepResultSet(), getRecordType());
if (!lazy) {
result = cursor.fetch();
@ -292,7 +265,7 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
}
}
else {
result = new ResultImpl<R>(ctx.configuration(), null);
result = new ResultImpl<R>(ctx.configuration());
}
}
@ -305,7 +278,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), true, false, CLOSE_AFTER_FETCH);
Cursor<Record> c = new CursorImpl<Record>(ctx, listener, fields, internIndexes(fields), true, false);
results.add(c.fetch());
if (ctx.statement().getMoreResults()) {
@ -328,10 +301,7 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
@Override
protected final boolean keepResultSet() {
return lazy
|| keepResultSetMode == KEEP_AFTER_FETCH
|| keepResultSetMode == UPDATE_ON_CHANGE
|| keepResultSetMode == UPDATE_ON_STORE;
return lazy;
}
/**
@ -496,9 +466,7 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
return c.fetchOne();
}
finally {
if (c.closesAfterFetch()) {
c.close();
}
c.close();
}
}

View File

@ -35,7 +35,6 @@
*/
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;
@ -143,7 +142,7 @@ abstract class AbstractStoreQuery<R extends Record> extends AbstractQuery implem
@Override
public final Result<R> getReturnedRecords() {
if (returned == null) {
returned = new ResultImpl<R>(configuration(), null, returning);
returned = new ResultImpl<R>(configuration(), returning);
}
return returned;
@ -345,7 +344,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, false, true, CLOSE_AFTER_FETCH).fetch().into(getInto());
returned = new CursorImpl<R>(ctx2, listener2, fieldArray(returning), null, false, true).fetch().into(getInto());
return result;
}
}

View File

@ -36,7 +36,6 @@
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;
@ -67,7 +66,6 @@ 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;
@ -90,7 +88,6 @@ class CursorImpl<R extends Record> implements Cursor<R> {
private final Field<?>[] fields;
private final boolean[] intern;
private final boolean keepResultSet;
private final KeepResultSetMode keepResultSetMode;
private final boolean keepStatement;
private final Class<? extends R> type;
private boolean isClosed;
@ -100,18 +97,17 @@ class CursorImpl<R extends Record> implements Cursor<R> {
private transient Iterator<R> iterator;
@SuppressWarnings("unchecked")
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 keepStatement, boolean keepResultSet) {
this(ctx, listener, fields, internIndexes, keepStatement, keepResultSet, (Class<? extends R>) RecordImpl.class);
}
CursorImpl(ExecuteContext ctx, ExecuteListener listener, Field<?>[] fields, int[] internIndexes, boolean keepStatement, boolean keepResultSet, KeepResultSetMode keepResultSetMode, Class<? extends R> type) {
CursorImpl(ExecuteContext ctx, ExecuteListener listener, Field<?>[] fields, int[] internIndexes, boolean keepStatement, boolean keepResultSet, Class<? extends R> type) {
this.ctx = ctx;
this.listener = (listener != null ? listener : new ExecuteListeners(ctx));
this.fields = fields;
this.type = type;
this.keepStatement = keepStatement;
this.keepResultSet = keepResultSet;
this.keepResultSetMode = keepResultSetMode;
this.rs = new CursorResultSet();
this.intern = new boolean[fields.length];
@ -122,11 +118,6 @@ class CursorImpl<R extends Record> implements Cursor<R> {
}
}
@Override
public final boolean closesAfterFetch() {
return keepResultSetMode == null || keepResultSetMode == CLOSE_AFTER_FETCH;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public final Row fieldsRow() {
@ -190,7 +181,7 @@ class CursorImpl<R extends Record> implements Cursor<R> {
// Before listener.resultStart(ctx)
iterator();
ResultImpl<R> result = new ResultImpl<R>(ctx.configuration(), closesAfterFetch() ? null : rs, fields);
ResultImpl<R> result = new ResultImpl<R>(ctx.configuration(), fields);
R record = null;
ctx.result(result);
@ -1402,14 +1393,6 @@ class CursorImpl<R extends Record> implements Cursor<R> {
record = (AbstractRecord) Utils.newRecord(type, fields, ctx.configuration());
// [#1846] Add a reference to the Cursor's ResultSet if
// Updatable ResultSets are requested
if (!closesAfterFetch()) {
record.keepResultSetMode = keepResultSetMode;
record.rs = rs;
record.rsIndex = rsIndex;
}
ctx.record(record);
listener.recordStart(ctx);
@ -1434,9 +1417,7 @@ 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.
// [#1846] When fetching updatable Results, do not close the
// Cursor's ResultSet!
if (record == null && closesAfterFetch()) {
if (record == null) {
CursorImpl.this.close();
}

View File

@ -36,7 +36,6 @@
package org.jooq.impl;
import static org.jooq.KeepResultSetMode.CLOSE_AFTER_FETCH;
import static org.jooq.conf.ParamType.INLINED;
import static org.jooq.conf.ParamType.NAMED;
import static org.jooq.impl.DSL.field;
@ -515,7 +514,7 @@ public class DefaultDSLContext implements DSLContext, Serializable {
ExecuteListener listener = new ExecuteListeners(ctx);
ctx.resultSet(rs);
return new CursorImpl<Record>(ctx, listener, fields, null, false, true, CLOSE_AFTER_FETCH);
return new CursorImpl<Record>(ctx, listener, fields, null, false, true);
}
@Override
@ -585,7 +584,7 @@ public class DefaultDSLContext implements DSLContext, Serializable {
@Override
public Result<Record> fetchFromStringData(List<String[]> data) {
if (data.size() == 0) {
return new ResultImpl<Record>(configuration, null);
return new ResultImpl<Record>(configuration);
}
else {
List<Field<?>> fields = new ArrayList<Field<?>>();
@ -594,7 +593,7 @@ public class DefaultDSLContext implements DSLContext, Serializable {
fields.add(fieldByName(String.class, name));
}
Result<Record> result = new ResultImpl<Record>(configuration, null, fields);
Result<Record> result = new ResultImpl<Record>(configuration, fields);
if (data.size() > 1) {
for (String[] values : data.subList(1, data.size())) {
@ -1456,7 +1455,7 @@ public class DefaultDSLContext implements DSLContext, Serializable {
@Override
public <R extends Record> Result<R> newResult(Table<R> table) {
return new ResultImpl<R>(configuration, null, table.fields());
return new ResultImpl<R>(configuration, table.fields());
}
// -------------------------------------------------------------------------

View File

@ -104,7 +104,7 @@ class ReferenceImpl<R extends Record, O extends Record> extends AbstractKey<R> i
@Override
public final Result<O> fetchParents(Collection<? extends R> records) {
if (records == null || records.size() == 0) {
return new ResultImpl<O>(new DefaultConfiguration(), null, key.getFields());
return new ResultImpl<O>(new DefaultConfiguration(), key.getFields());
}
else {
return fetch(records, key.getTable(), key.getFieldsArray(), getFieldsArray());
@ -114,7 +114,7 @@ class ReferenceImpl<R extends Record, O extends Record> extends AbstractKey<R> i
@Override
public final Result<R> fetchChildren(Collection<? extends O> records) {
if (records == null || records.size() == 0) {
return new ResultImpl<R>(new DefaultConfiguration(), null, getFields());
return new ResultImpl<R>(new DefaultConfiguration(), getFields());
}
else {
return fetch(records, getTable(), getFieldsArray(), key.getFieldsArray());

View File

@ -38,14 +38,12 @@ package org.jooq.impl;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static org.jooq.impl.Utils.translate;
import static org.jooq.tools.StringUtils.abbreviate;
import static org.jooq.tools.StringUtils.leftPad;
import static org.jooq.tools.StringUtils.rightPad;
import java.lang.reflect.Array;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -97,22 +95,20 @@ class ResultImpl<R extends Record> implements Result<R>, AttachableInternal {
private static final long serialVersionUID = 6416154375799578362L;
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));
ResultImpl(Configuration configuration, Collection<? extends Field<?>> fields) {
this(configuration, new Fields(fields));
}
ResultImpl(Configuration configuration, ResultSet rs, Field<?>... fields) {
this(configuration, rs, new Fields(fields));
ResultImpl(Configuration configuration, Field<?>... fields) {
this(configuration, new Fields(fields));
}
ResultImpl(Configuration configuration, ResultSet rs, Fields fields) {
ResultImpl(Configuration configuration, Fields fields) {
this.configuration = configuration;
this.fields = fields;
this.rs = rs;
this.records = new ArrayList<R>();
}
@ -863,7 +859,7 @@ class ResultImpl<R extends Record> implements Result<R>, AttachableInternal {
Result<R> result = map.get(val);
if (result == null) {
result = new ResultImpl<R>(configuration, rs, fields);
result = new ResultImpl<R>(configuration, fields);
map.put(val, result);
}
@ -915,7 +911,7 @@ class ResultImpl<R extends Record> implements Result<R>, AttachableInternal {
Result<R> result = map.get(key);
if (result == null) {
result = new ResultImpl<R>(configuration(), rs, this.fields);
result = new ResultImpl<R>(configuration(), this.fields);
map.put(key, result);
}
@ -1059,7 +1055,7 @@ class ResultImpl<R extends Record> implements Result<R>, AttachableInternal {
@Override
public final <Z extends Record> Result<Z> into(Table<Z> table) {
Result<Z> list = new ResultImpl<Z>(configuration(), rs, table.fields());
Result<Z> list = new ResultImpl<Z>(configuration(), table.fields());
for (R record : this) {
list.add(record.into(table));
@ -1192,30 +1188,6 @@ class ResultImpl<R extends Record> implements Result<R>, AttachableInternal {
return intern(fields.indexesOf(fieldNames));
}
@Override
public final void close() {
try {
if (rs != null) {
rs.close();
rs = null;
for (Record record : this) {
if (record instanceof AbstractRecord) {
((AbstractRecord) record).rs = null;
}
}
}
}
catch (SQLException e) {
throw translate("Cannot close ResultSet", e);
}
}
@Override
public final ResultSet resultSet() {
return rs;
}
/**
* A comparator for records, wrapping another comparator for &lt;T&gt;
*/

View File

@ -56,7 +56,6 @@ 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;
@ -969,11 +968,6 @@ 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);

View File

@ -334,31 +334,24 @@ public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableReco
}
}
@Override
public final void refresh() {
refresh(fields.fields.fields);
}
@Override
public final void refresh(Field<?>... f) {
if (rs != null) {
super.refresh(f);
SelectQuery<?> 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);
}
else {
// [#2265] Even if rs was previously closed, re-fetch a new ResultSet
// and assign it to this record, if requested.
SelectQuery<?> select = create().selectQuery();
select.addSelect(f);
select.addFrom(getTable());
select.keepResultSet(keepResultSetMode);
Utils.addConditions(select, this, getPrimaryKey().getFieldsArray());
if (select.execute() == 1) {
AbstractRecord record = (AbstractRecord) select.getResult().get(0);
setValues(f, record);
rs = record.rs;
rsIndex = record.rsIndex;
}
else {
throw new InvalidResultException("Exactly one row expected for refresh. Record does not exist in database.");
}
throw new InvalidResultException("Exactly one row expected for refresh. Record does not exist in database.");
}
}

View File

@ -754,9 +754,7 @@ final class Utils {
return record;
}
finally {
if (cursor.closesAfterFetch()) {
cursor.close();
}
cursor.close();
}
}