[#5411] Add support for ResultQuery.fetchSingle(), which returns exactly one record

This commit is contained in:
lukaseder 2017-07-28 12:07:19 +02:00
parent a6464beb23
commit c0ca2539b7
4 changed files with 483 additions and 5 deletions

View File

@ -58,6 +58,7 @@ import org.jooq.exception.DataAccessException;
import org.jooq.exception.DataTypeException;
import org.jooq.exception.InvalidResultException;
import org.jooq.exception.MappingException;
import org.jooq.exception.NoDataFoundException;
import org.jooq.exception.TooManyRowsException;
import org.jooq.impl.DefaultRecordMapper;
@ -724,6 +725,276 @@ public interface ResultQuery<R extends Record> extends Query, Iterable<R> {
*/
<Z extends Record> Z fetchOneInto(Table<Z> table) throws DataAccessException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting value for a field from
* the generated result.
* <p>
* This is the same as calling {@link #fetchSingle()} and then
* {@link Record#get(Field)}
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
<T> T fetchSingle(Field<T> field) throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting value for a
* field from the generated result.
* <p>
* This is the same as calling {@link #fetchSingle()} and then
* {@link Record#get(Field, Class)}
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
<T> T fetchSingle(Field<?> field, Class<? extends T> type) throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting value for a
* field from the generated result.
* <p>
* This is the same as calling {@link #fetchSingle()} and then
* {@link Record#get(Field, Converter)}
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
<T, U> U fetchSingle(Field<T> field, Converter<? super T, ? extends U> converter) throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting value for a
* field index from the generated result.
* <p>
* This is the same as calling {@link #fetchSingle()} and then
* {@link Record#get(int)}
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
Object fetchSingle(int fieldIndex) throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting value for a
* field index from the generated result.
* <p>
* This is the same as calling {@link #fetchSingle()} and then
* {@link Record#get(int, Class)}
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
<T> T fetchSingle(int fieldIndex, Class<? extends T> type) throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting value for a
* field index from the generated result.
* <p>
* This is the same as calling {@link #fetchSingle()} and then
* {@link Record#get(int, Converter)}
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
<U> U fetchSingle(int fieldIndex, Converter<?, ? extends U> converter) throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting value for a
* field name from the generated result.
* <p>
* This is the same as calling {@link #fetchSingle()} and then
* {@link Record#get(String)}
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
Object fetchSingle(String fieldName) throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting value for a
* field name from the generated result.
* <p>
* This is the same as calling {@link #fetchSingle()} and then
* {@link Record#get(String, Class)}
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
<T> T fetchSingle(String fieldName, Class<? extends T> type) throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting value for a
* field name from the generated result.
* <p>
* This is the same as calling {@link #fetchSingle()} and then
* {@link Record#get(String, Converter)}
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
<U> U fetchSingle(String fieldName, Converter<?, ? extends U> converter) throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting value for a
* field name from the generated result.
* <p>
* This is the same as calling {@link #fetchSingle()} and then
* {@link Record#get(Name)}
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
Object fetchSingle(Name fieldName) throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting value for a
* field name from the generated result.
* <p>
* This is the same as calling {@link #fetchSingle()} and then
* {@link Record#get(Name, Class)}
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
<T> T fetchSingle(Name fieldName, Class<? extends T> type) throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting value for a
* field name from the generated result.
* <p>
* This is the same as calling {@link #fetchSingle()} and then
* {@link Record#get(Name, Converter)}
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
<U> U fetchSingle(Name fieldName, Converter<?, ? extends U> converter) throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting record.
* <p>
* The resulting record is attached to the original {@link Configuration} by
* default. Use {@link Settings#isAttachRecords()} to override this
* behaviour.
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
R fetchSingle() throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting value into a
* custom mapper callback.
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
<E> E fetchSingle(RecordMapper<? super R, E> mapper) throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting record as a name/value
* map.
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
* @see Result#intoMaps()
* @see Record#intoMap()
*/
Map<String, Object> fetchSingleMap() throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return exactly one resulting record as an array
* <p>
* You can access data like this
* <code><pre>query.fetchSingleArray()[fieldIndex]</pre></code>
*
* @return The resulting value. This is never <code>null</code>.
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
Object[] fetchSingleArray() throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Map resulting records onto a custom type.
* <p>
* This is the same as calling <code><pre>
* E result = null;
* Record r = q.fetchSingle();
*
* if (r != null)
* result = r.into(type);
* </pre></code>. See {@link Record#into(Class)} for more details
*
* @param <E> The generic entity type.
* @param type The entity type.
* @return The resulting value. This is never <code>null</code>.
* @see Record#into(Class)
* @see Result#into(Class)
* @throws DataAccessException if something went wrong executing the query
* @throws MappingException wrapping any reflection or data type conversion
* exception that might have occurred while mapping records
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
* @see DefaultRecordMapper
*/
<E> E fetchSingleInto(Class<? extends E> type) throws DataAccessException, MappingException, NoDataFoundException, TooManyRowsException;
/**
* Map resulting records onto a custom record.
* <p>
* This is the same as calling <code><pre>
* Z result = null;
* Record r = q.fetchSingle();
*
* if (r != null)
* result = r.into(table);
* </pre></code>. See {@link Record#into(Table)} for more details
* <p>
* The resulting record is attached to the original {@link Configuration} by
* default. Use {@link Settings#isAttachRecords()} to override this
* behaviour.
*
* @param <Z> The generic table record type.
* @param table The table type.
* @return The resulting value. This is never <code>null</code>.
* @see Record#into(Table)
* @see Result#into(Table)
* @throws DataAccessException if something went wrong executing the query
* @thorws NoDataFoundException if the query returned no records
* @throws TooManyRowsException if the query returned more than one record
*/
<Z extends Record> Z fetchSingleInto(Table<Z> table) throws DataAccessException, NoDataFoundException, TooManyRowsException;
/**
* Execute the query and return at most one resulting value for a

View File

@ -559,6 +559,96 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
return record == null ? null : record.into(table);
}
@Override
public final <T> T fetchSingle(Field<T> field) {
return fetchSingle().get(field);
}
@Override
public final <T> T fetchSingle(Field<?> field, Class<? extends T> type) {
return Convert.convert(fetchSingle(field), type);
}
@Override
public final <T, U> U fetchSingle(Field<T> field, Converter<? super T, ? extends U> converter) {
return Convert.convert(fetchSingle(field), converter);
}
@Override
public final Object fetchSingle(int fieldIndex) {
return fetchSingle().get(fieldIndex);
}
@Override
public final <T> T fetchSingle(int fieldIndex, Class<? extends T> type) {
return Convert.convert(fetchSingle(fieldIndex), type);
}
@Override
public final <U> U fetchSingle(int fieldIndex, Converter<?, ? extends U> converter) {
return Convert.convert(fetchSingle(fieldIndex), converter);
}
@Override
public final Object fetchSingle(String fieldName) {
return fetchSingle().get(fieldName);
}
@Override
public final <T> T fetchSingle(String fieldName, Class<? extends T> type) {
return Convert.convert(fetchSingle(fieldName), type);
}
@Override
public final <U> U fetchSingle(String fieldName, Converter<?, ? extends U> converter) {
return Convert.convert(fetchSingle(fieldName), converter);
}
@Override
public final Object fetchSingle(Name fieldName) {
return fetchSingle().get(fieldName);
}
@Override
public final <T> T fetchSingle(Name fieldName, Class<? extends T> type) {
return Convert.convert(fetchSingle(fieldName), type);
}
@Override
public final <U> U fetchSingle(Name fieldName, Converter<?, ? extends U> converter) {
return Convert.convert(fetchSingle(fieldName), converter);
}
@Override
public final R fetchSingle() {
return Tools.fetchSingle(fetchLazy());
}
@Override
public final <E> E fetchSingle(RecordMapper<? super R, E> mapper) {
return mapper.map(fetchSingle());
}
@Override
public final Map<String, Object> fetchSingleMap() {
return fetchSingle().intoMap();
}
@Override
public final Object[] fetchSingleArray() {
return fetchSingle().intoArray();
}
@Override
public final <E> E fetchSingleInto(Class<? extends E> type) {
return fetchSingle().into(type);
}
@Override
public final <Z extends Record> Z fetchSingleInto(Table<Z> table) {
return fetchSingle().into(table);
}
@Override
public final <T> Optional<T> fetchOptional(Field<T> field) {

View File

@ -124,7 +124,6 @@ import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.TableLike;
import org.jooq.WindowDefinition;
import org.jooq.exception.MappingException;
/**
* A wrapper for a {@link SelectQuery}
@ -2789,6 +2788,96 @@ final class SelectImpl<R extends Record, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
return getDelegate().fetchOneInto(table);
}
@Override
public final <T> T fetchSingle(Field<T> field) {
return getDelegate().fetchSingle(field);
}
@Override
public final <T> T fetchSingle(Field<?> field, Class<? extends T> type) {
return getDelegate().fetchSingle(field, type);
}
@Override
public final <T, U> U fetchSingle(Field<T> field, Converter<? super T, ? extends U> converter) {
return getDelegate().fetchSingle(field, converter);
}
@Override
public final Object fetchSingle(int fieldIndex) {
return getDelegate().fetchSingle(fieldIndex);
}
@Override
public final <T> T fetchSingle(int fieldIndex, Class<? extends T> type) {
return getDelegate().fetchSingle(fieldIndex, type);
}
@Override
public final <U> U fetchSingle(int fieldIndex, Converter<?, ? extends U> converter) {
return getDelegate().fetchSingle(fieldIndex, converter);
}
@Override
public final Object fetchSingle(String fieldName) {
return getDelegate().fetchSingle(fieldName);
}
@Override
public final <T> T fetchSingle(String fieldName, Class<? extends T> type) {
return getDelegate().fetchSingle(fieldName, type);
}
@Override
public final <U> U fetchSingle(String fieldName, Converter<?, ? extends U> converter) {
return getDelegate().fetchSingle(fieldName, converter);
}
@Override
public final Object fetchSingle(Name fieldName) {
return getDelegate().fetchSingle(fieldName);
}
@Override
public final <T> T fetchSingle(Name fieldName, Class<? extends T> type) {
return getDelegate().fetchSingle(fieldName, type);
}
@Override
public final <U> U fetchSingle(Name fieldName, Converter<?, ? extends U> converter) {
return getDelegate().fetchSingle(fieldName, converter);
}
@Override
public final R fetchSingle() {
return getDelegate().fetchSingle();
}
@Override
public final <E> E fetchSingle(RecordMapper<? super R, E> mapper) {
return getDelegate().fetchSingle(mapper);
}
@Override
public final Map<String, Object> fetchSingleMap() {
return getDelegate().fetchSingleMap();
}
@Override
public final Object[] fetchSingleArray() {
return getDelegate().fetchSingleArray();
}
@Override
public final <E> E fetchSingleInto(Class<? extends E> type) {
return getDelegate().fetchSingleInto(type);
}
@Override
public final <Z extends Record> Z fetchSingleInto(Table<Z> table) {
return getDelegate().fetchSingleInto(table);
}
@Override
public final <T> Optional<T> fetchOptional(Field<T> field) {
@ -3362,7 +3451,7 @@ final class SelectImpl<R extends Record, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
}
@Override
public final <K> Map<K, Result<R>> fetchGroups(RecordMapper<? super R, K> keyMapper) throws MappingException {
public final <K> Map<K, Result<R>> fetchGroups(RecordMapper<? super R, K> keyMapper) {
return getDelegate().fetchGroups(keyMapper);
}

View File

@ -217,6 +217,7 @@ import org.jooq.conf.Settings;
import org.jooq.conf.ThrowExceptions;
import org.jooq.exception.DataAccessException;
import org.jooq.exception.MappingException;
import org.jooq.exception.NoDataFoundException;
import org.jooq.exception.TooManyRowsException;
import org.jooq.impl.ResultsImpl.ResultOrRowsImpl;
import org.jooq.impl.Tools.Cache.CachedOperation;
@ -1624,11 +1625,38 @@ final class Tools {
*/
static final <R extends Record> R fetchOne(Cursor<R> cursor) throws TooManyRowsException {
try {
R record = cursor.fetchOne();
R record = cursor.fetchNext();
if (cursor.hasNext()) {
if (cursor.hasNext())
throw new TooManyRowsException("Cursor returned more than one result");
return record;
}
finally {
cursor.close();
}
}
/**
* Get the only element from a cursor, or throw an exception.
* <p>
* [#2373] This method will always close the argument cursor, as it is
* supposed to be completely consumed by this method.
*
* @param cursor The cursor
* @return The only element from the cursor
* @throws NoDataFoundException Thrown if the cursor did not return any rows
* @throws TooManyRowsException Thrown if the cursor returns more than one
* element
*/
static final <R extends Record> R fetchSingle(Cursor<R> cursor) throws NoDataFoundException, TooManyRowsException {
try {
R record = cursor.fetchNext();
if (record == null)
throw new NoDataFoundException("Cursor returned no rows");
if (cursor.hasNext())
throw new TooManyRowsException("Cursor returned more than one result");
}
return record;
}