Repository: future-architect/uroborosql (java)

Issue:
Add SqlQuery#one, SqlQuery#findOne
I want to add a method that guarantees that only one query is fetched when fetching query results.
I think that it is good to raise an exception when you get 2 or more, or to judge by Optional.

This is often useful for enterprise use cases.

```java
Map<String, Object> one(CaseFormat caseFormat);
Map<String, Object> one();
<T> T one(Class<T> type);
Optional<Map<String, Object>> findOne(CaseFormat caseFormat);
Optional<Map<String, Object>> findOne();
<T> Optional<T> findOne(Class<T> type);
```

Pull request description:
Add SqlQuery#one, SqlQuery#findOne
fixes #198 

Added the following methods.

```java
Map<String, Object> one(CaseFormat caseFormat);
Map<String, Object> one();
<T> T one(Class<T> type);
Optional<Map<String, Object>> findOne(CaseFormat caseFormat);
Optional<Map<String, Object>> findOne();
<T> Optional<T> findOne(Class<T> type);
```

Improved so that `DataNonUniqueException` occurs when two or more results are returned.

Patch:
diff --git a/src/main/java/jp/co/future/uroborosql/SqlQueryImpl.java b/src/main/java/jp/co/future/uroborosql/SqlQueryImpl.java
index 63828c5..425dcf6 100644
--- a/src/main/java/jp/co/future/uroborosql/SqlQueryImpl.java
+++ b/src/main/java/jp/co/future/uroborosql/SqlQueryImpl.java
@@ -18,6 +18,7 @@ import jp.co.future.uroborosql.context.SqlContext;
 import jp.co.future.uroborosql.converter.EntityResultSetConverter;
 import jp.co.future.uroborosql.converter.MapResultSetConverter;
 import jp.co.future.uroborosql.converter.ResultSetConverter;
+import jp.co.future.uroborosql.exception.DataNonUniqueException;
 import jp.co.future.uroborosql.exception.DataNotFoundException;
 import jp.co.future.uroborosql.exception.UroborosqlSQLException;
 import jp.co.future.uroborosql.fluent.SqlQuery;
@@ -43,123 +44,142 @@ final class SqlQueryImpl extends AbstractSqlFluent<SqlQuery> implements SqlQuery
 	/**
 	 * {@inheritDoc}
 	 *
-	 * @see jp.co.future.uroborosql.fluent.SqlQuery#stream()
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#first()
 	 */
 	@Override
-	public Stream<Map<String, Object>> stream() {
-		return stream(agent().getDefaultMapKeyCaseFormat());
+	public Map<String, Object> first() {
+		return first(agent().getDefaultMapKeyCaseFormat());
 	}
 
 	/**
 	 * {@inheritDoc}
 	 *
-	 * @see jp.co.future.uroborosql.fluent.SqlQuery#stream(jp.co.future.uroborosql.converter.ResultSetConverter)
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#first(jp.co.future.uroborosql.utils.CaseFormat)
 	 */
 	@Override
-	public <T> Stream<T> stream(final ResultSetConverter<T> converter) {
-		try {
-			return agent().query(context(), converter);
-		} catch (SQLException e) {
-			throw new UroborosqlSQLException(e);
-		}
+	public Map<String, Object> first(final CaseFormat caseFormat) {
+		return findFirst(caseFormat).orElseThrow(() -> new DataNotFoundException("query result is empty."));
 	}
 
 	/**
 	 * {@inheritDoc}
 	 *
-	 * @see jp.co.future.uroborosql.fluent.SqlQuery#stream(jp.co.future.uroborosql.utils.CaseFormat)
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#first(Class)
 	 */
 	@Override
-	public Stream<Map<String, Object>> stream(final CaseFormat caseFormat) {
-		return stream(new MapResultSetConverter(agent.getSqlConfig().getDialect(), caseFormat));
+	public <T> T first(final Class<T> type) {
+		return findFirst(type).orElseThrow(() -> new DataNotFoundException("query result is empty."));
 	}
 
 	/**
 	 * {@inheritDoc}
 	 *
-	 * @see jp.co.future.uroborosql.fluent.SqlQuery#stream(java.lang.Class)
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#findFirst()
 	 */
 	@Override
-	public <T> Stream<T> stream(final Class<T> type) {
-		return stream(new EntityResultSetConverter<>(type, new PropertyMapperManager()));
+	public Optional<Map<String, Object>> findFirst() {
+		return findFirst(agent().getDefaultMapKeyCaseFormat());
 	}
 
 	/**
 	 * {@inheritDoc}
 	 *
-	 * @see jp.co.future.uroborosql.fluent.SqlQuery#resultSet()
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#findFirst(jp.co.future.uroborosql.utils.CaseFormat)
 	 */
 	@Override
-	public ResultSet resultSet() {
-		try {
-			return agent().query(context());
-		} catch (SQLException e) {
-			throw new UroborosqlSQLException(e);
+	public Optional<Map<String, Object>> findFirst(final CaseFormat caseFormat) {
+		try (Stream<Map<String, Object>> stream = stream(
+				new MapResultSetConverter(this.agent.getSqlConfig().getDialect(), caseFormat))) {
+			return stream.findFirst();
 		}
 	}
 
 	/**
 	 * {@inheritDoc}
 	 *
-	 * @see jp.co.future.uroborosql.fluent.SqlQuery#first()
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#findFirst(java.lang.Class)
 	 */
 	@Override
-	public Map<String, Object> first() {
-		return first(agent().getDefaultMapKeyCaseFormat());
+	public <T> Optional<T> findFirst(final Class<T> type) {
+		try (Stream<T> stream = stream(new EntityResultSetConverter<>(type, new PropertyMapperManager()))) {
+			return stream.findFirst();
+		}
 	}
 
 	/**
 	 * {@inheritDoc}
 	 *
-	 * @see jp.co.future.uroborosql.fluent.SqlQuery#first(jp.co.future.uroborosql.utils.CaseFormat)
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#one()
 	 */
 	@Override
-	public Map<String, Object> first(final CaseFormat caseFormat) {
-		return findFirst(caseFormat).orElseThrow(() -> new DataNotFoundException("query result is empty."));
+	public Map<String, Object> one() {
+		return one(agent().getDefaultMapKeyCaseFormat());
 	}
 
 	/**
 	 * {@inheritDoc}
 	 *
-	 * @see jp.co.future.uroborosql.fluent.SqlQuery#first(Class)
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#one(CaseFormat)
 	 */
 	@Override
-	public <T> T first(final Class<T> type) {
-		return findFirst(type).orElseThrow(() -> new DataNotFoundException("query result is empty."));
+	public Map<String, Object> one(final CaseFormat caseFormat) {
+		return findOne(caseFormat).orElseThrow(() -> new DataNotFoundException("query result is empty."));
 	}
 
 	/**
 	 * {@inheritDoc}
 	 *
-	 * @see jp.co.future.uroborosql.fluent.SqlQuery#findFirst()
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#one(Class)
 	 */
 	@Override
-	public Optional<Map<String, Object>> findFirst() {
-		return findFirst(agent().getDefaultMapKeyCaseFormat());
+	public <T> T one(final Class<T> type) {
+		return findOne(type).orElseThrow(() -> new DataNotFoundException("query result is empty."));
 	}
 
 	/**
 	 * {@inheritDoc}
 	 *
-	 * @see jp.co.future.uroborosql.fluent.SqlQuery#findFirst(jp.co.future.uroborosql.utils.CaseFormat)
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#findOne()
 	 */
 	@Override
-	public Optional<Map<String, Object>> findFirst(final CaseFormat caseFormat) {
+	public Optional<Map<String, Object>> findOne() {
+		return findOne(agent().getDefaultMapKeyCaseFormat());
+	}
+
+	@Override
+	public Optional<Map<String, Object>> findOne(final CaseFormat caseFormat) {
 		try (Stream<Map<String, Object>> stream = stream(
-				new MapResultSetConverter(agent.getSqlConfig().getDialect(), caseFormat))) {
-			return stream.findFirst();
+				new MapResultSetConverter(this.agent.getSqlConfig().getDialect(), caseFormat))) {
+			List<Map<String, Object>> resultList = stream.limit(2).collect(Collectors.toList());
+			if (resultList.size() > 1) {
+				throw new DataNonUniqueException("two or more query results.");
+			}
+			return resultList.stream().findFirst();
+		}
+	}
+
+	@Override
+	public <T> Optional<T> findOne(final Class<T> type) {
+		try (Stream<T> stream = stream(new EntityResultSetConverter<>(type, new PropertyMapperManager()))) {
+			List<T> resultList = stream.limit(2).collect(Collectors.toList());
+			if (resultList.size() > 1) {
+				throw new DataNonUniqueException("two or more query results.");
+			}
+			return resultList.stream().findFirst();
 		}
 	}
 
 	/**
 	 * {@inheritDoc}
 	 *
-	 * @see jp.co.future.uroborosql.fluent.SqlQuery#findFirst(java.lang.Class)
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#resultSet()
 	 */
 	@Override
-	public <T> Optional<T> findFirst(final Class<T> type) {
-		try (Stream<T> stream = stream(new EntityResultSetConverter<>(type, new PropertyMapperManager()))) {
-			return stream.findFirst();
+	public ResultSet resultSet() {
+		try {
+			return agent().query(context());
+		} catch (SQLException e) {
+			throw new UroborosqlSQLException(e);
 		}
 	}
 
@@ -198,4 +218,49 @@ final class SqlQueryImpl extends AbstractSqlFluent<SqlQuery> implements SqlQuery
 			return stream.collect(Collectors.toList());
 		}
 	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#stream()
+	 */
+	@Override
+	public Stream<Map<String, Object>> stream() {
+		return stream(agent().getDefaultMapKeyCaseFormat());
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#stream(jp.co.future.uroborosql.converter.ResultSetConverter)
+	 */
+	@Override
+	public <T> Stream<T> stream(final ResultSetConverter<T> converter) {
+		try {
+			return agent().query(context(), converter);
+		} catch (SQLException e) {
+			throw new UroborosqlSQLException(e);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#stream(jp.co.future.uroborosql.utils.CaseFormat)
+	 */
+	@Override
+	public Stream<Map<String, Object>> stream(final CaseFormat caseFormat) {
+		return stream(new MapResultSetConverter(this.agent.getSqlConfig().getDialect(), caseFormat));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 *
+	 * @see jp.co.future.uroborosql.fluent.SqlQuery#stream(java.lang.Class)
+	 */
+	@Override
+	public <T> Stream<T> stream(final Class<T> type) {
+		return stream(new EntityResultSetConverter<>(type, new PropertyMapperManager()));
+	}
+
 }
diff --git a/src/main/java/jp/co/future/uroborosql/exception/DataNonUniqueException.java b/src/main/java/jp/co/future/uroborosql/exception/DataNonUniqueException.java
new file mode 100644
index 0000000..97422a5
--- /dev/null
+++ b/src/main/java/jp/co/future/uroborosql/exception/DataNonUniqueException.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2017-present, Future Corporation
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+package jp.co.future.uroborosql.exception;
+
+/**
+ * 検索結果が2件以上で先頭行を取得しようとした場合にスローされる例外クラス
+ *
+ * @author hoshi-k
+ */
+public class DataNonUniqueException extends UroborosqlRuntimeException {
+
+	public DataNonUniqueException() {
+		super();
+	}
+
+	public DataNonUniqueException(final String message) {
+		super(message);
+	}
+
+	public DataNonUniqueException(final String message, final Throwable cause) {
+		super(message, cause);
+	}
+
+	public DataNonUniqueException(final Throwable cause) {
+		super(cause);
+	}
+
+}
diff --git a/src/main/java/jp/co/future/uroborosql/fluent/SqlQuery.java b/src/main/java/jp/co/future/uroborosql/fluent/SqlQuery.java
index fa07681..c0eef71 100644
--- a/src/main/java/jp/co/future/uroborosql/fluent/SqlQuery.java
+++ b/src/main/java/jp/co/future/uroborosql/fluent/SqlQuery.java
@@ -18,6 +18,7 @@ import java.util.stream.Stream;
 import jp.co.future.uroborosql.SqlAgent;
 import jp.co.future.uroborosql.context.SqlContext;
 import jp.co.future.uroborosql.converter.ResultSetConverter;
+import jp.co.future.uroborosql.exception.DataNonUniqueException;
 import jp.co.future.uroborosql.exception.DataNotFoundException;
 import jp.co.future.uroborosql.utils.CaseFormat;
 
@@ -29,95 +30,149 @@ import jp.co.future.uroborosql.utils.CaseFormat;
 public interface SqlQuery extends SqlFluent<SqlQuery> {
 
 	/**
-	 * ResultSetの取得（終端処理）
+	 * 検索結果の先頭行を取得（終端処理）
 	 *
-	 * @return ResultSet
-	 * @see SqlAgent#query(SqlContext)
+	 * @return 検索結果の先頭行をMapに詰めたもの. MapのキーはSnakeCase式に変換される.
+	 * @throws DataNotFoundException 検索結果が０件の場合
 	 */
-	ResultSet resultSet();
+	Map<String, Object> first();
 
 	/**
-	 * 検索結果の取得（終端処理）
+	 * 検索結果の先頭行を取得（終端処理）
 	 *
-	 * @return 検索結果のリスト. MapのキーはSnakeCaseに変換される.
-	 * @see SqlAgent#query(String)
+	 * @param caseFormat Mapのキーの変換書式
+	 * @return 検索結果の先頭行をMapに詰めたもの
+	 * @throws DataNotFoundException 検索結果が０件の場合
 	 */
-	List<Map<String, Object>> collect();
+	Map<String, Object> first(CaseFormat caseFormat);
 
 	/**
-	 * 検索結果の取得（終端処理）
+	 * 検索結果の先頭行をEntityとして取得（終端処理）
+	 *
+	 * @param <T> Entityの型
+	 * @param type 受け取りたいEntityの型
+	 * @return 検索結果の先頭行のEntity
+	 * @throws DataNotFoundException 検索結果が０件の場合
+	 */
+	<T> T first(Class<T> type);
+
+	/**
+	 * 検索結果の先頭行を取得（終端処理）
+	 *
+	 * @return 検索結果の先頭行をMapに詰めたものをOptionalとして返却. MapのキーはSnakeCase式に変換される.
+	 */
+	Optional<Map<String, Object>> findFirst();
+
+	/**
+	 * 検索結果の先頭行を取得（終端処理）
 	 *
 	 * @param caseFormat Mapのキーの変換書式
-	 * @return 検索結果のリスト
-	 * @see SqlAgent#query(String)
+	 * @return 検索結果の先頭行をMapに詰めたものをOptionalとして返却
 	 */
-	List<Map<String, Object>> collect(CaseFormat caseFormat);
+	Optional<Map<String, Object>> findFirst(CaseFormat caseFormat);
 
 	/**
-	 * 検索結果の取得（終端処理）
+	 * 検索結果の先頭行をEntityとして取得（終端処理）
 	 *
-	 * @param <T>  Entityの型
+	 * @param <T> Entityの型
 	 * @param type 受け取りたいEntityの型
-	 * @return 検索結果のリスト
-	 * @see SqlAgent#query(String)
+	 * @return 検索結果の先頭行のEntityをOptionalとして返却
 	 */
-	<T> List<T> collect(Class<T> type);
+	<T> Optional<T> findFirst(Class<T> type);
 
 	/**
 	 * 検索結果の先頭行を取得（終端処理）
 	 *
-	 * @param caseFormat Mapのキーの変換書式
-	 * @return 検索結果の先頭行をMapに詰めたもの
+	 * @return 検索結果の先頭行をMapに詰めたもの. MapのキーはSnakeCase式に変換される.
 	 * @throws DataNotFoundException 検索結果が０件の場合
+	 * @throws DataNonUniqueException 検索結果が２件以上の場合
 	 */
-	Map<String, Object> first(CaseFormat caseFormat);
+	Map<String, Object> one();
 
 	/**
 	 * 検索結果の先頭行を取得（終端処理）
 	 *
-	 * @return 検索結果の先頭行をMapに詰めたもの. MapのキーはSnakeCase式に変換される.
+	 * @param caseFormat Mapのキーの変換書式
+	 * @return 検索結果の先頭行をMapに詰めたもの
 	 * @throws DataNotFoundException 検索結果が０件の場合
+	 * @throws DataNonUniqueException 検索結果が２件以上の場合
 	 */
-	Map<String, Object> first();
+	Map<String, Object> one(CaseFormat caseFormat);
 
 	/**
 	 * 検索結果の先頭行をEntityとして取得（終端処理）
 	 *
-	 * @param <T>  Entityの型
+	 * @param <T> Entityの型
 	 * @param type 受け取りたいEntityの型
 	 * @return 検索結果の先頭行のEntity
 	 * @throws DataNotFoundException 検索結果が０件の場合
+	 * @throws DataNonUniqueException 検索結果が２件以上の場合
 	 */
-	<T> T first(Class<T> type);
+	<T> T one(Class<T> type);
 
 	/**
 	 * 検索結果の先頭行を取得（終端処理）
 	 *
-	 * @param caseFormat Mapのキーの変換書式
-	 * @return 検索結果の先頭行をMapに詰めたものをOptionalとして返却
+	 * @return 検索結果の先頭行をMapに詰めたものをOptionalとして返却. MapのキーはSnakeCase式に変換される.
 	 */
-	Optional<Map<String, Object>> findFirst(CaseFormat caseFormat);
+	Optional<Map<String, Object>> findOne();
 
 	/**
 	 * 検索結果の先頭行を取得（終端処理）
 	 *
-	 * @return 検索結果の先頭行をMapに詰めたものをOptionalとして返却. MapのキーはSnakeCase式に変換される.
+	 * @param caseFormat Mapのキーの変換書式
+	 * @return 検索結果の先頭行をMapに詰めたものをOptionalとして返却
 	 */
-	Optional<Map<String, Object>> findFirst();
+	Optional<Map<String, Object>> findOne(CaseFormat caseFormat);
 
 	/**
 	 * 検索結果の先頭行をEntityとして取得（終端処理）
 	 *
-	 * @param <T>  Entityの型
+	 * @param <T> Entityの型
 	 * @param type 受け取りたいEntityの型
 	 * @return 検索結果の先頭行のEntityをOptionalとして返却
 	 */
-	<T> Optional<T> findFirst(Class<T> type);
+	<T> Optional<T> findOne(Class<T> type);
+
+	/**
+	 * ResultSetの取得（終端処理）
+	 *
+	 * @return ResultSet
+	 * @see SqlAgent#query(SqlContext)
+	 */
+	ResultSet resultSet();
+
+	/**
+	 * 検索結果の取得（終端処理）
+	 *
+	 * @return 検索結果のリスト. MapのキーはSnakeCaseに変換される.
+	 * @see SqlAgent#query(String)
+	 */
+	List<Map<String, Object>> collect();
+
+	/**
+	 * 検索結果の取得（終端処理）
+	 *
+	 * @param caseFormat Mapのキーの変換書式
+	 * @return 検索結果のリスト
+	 * @see SqlAgent#query(String)
+	 */
+	List<Map<String, Object>> collect(CaseFormat caseFormat);
+
+	/**
+	 * 検索結果の取得（終端処理）
+	 *
+	 * @param <T> Entityの型
+	 * @param type 受け取りたいEntityの型
+	 * @return 検索結果のリスト
+	 * @see SqlAgent#query(String)
+	 */
+	<T> List<T> collect(Class<T> type);
 
 	/**
 	 * 検索結果をStreamとして取得（終端処理）
 	 *
-	 * @param <T>       Streamの型
+	 * @param <T> Streamの型
 	 * @param converter ResultSetの各行を変換するための変換器
 	 * @return 検索結果を順次取得するStream
 	 */
@@ -141,7 +196,7 @@ public interface SqlQuery extends SqlFluent<SqlQuery> {
 	/**
 	 * 検索結果をEntityのStreamとして取得（終端処理）
 	 *
-	 * @param <T>       Streamの型
+	 * @param <T> Streamの型
 	 * @param type 受け取りたいEntityの型
 	 * @return 検索結果を順次取得するStream.
 	 */


Test patch:
diff --git a/src/test/java/jp/co/future/uroborosql/SqlAgentTest.java b/src/test/java/jp/co/future/uroborosql/SqlAgentTest.java
index b5ef68d..5ce58ac 100644
--- a/src/test/java/jp/co/future/uroborosql/SqlAgentTest.java
+++ b/src/test/java/jp/co/future/uroborosql/SqlAgentTest.java
@@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory;
 import jp.co.future.uroborosql.config.SqlConfig;
 import jp.co.future.uroborosql.context.SqlContext;
 import jp.co.future.uroborosql.converter.MapResultSetConverter;
+import jp.co.future.uroborosql.exception.DataNonUniqueException;
 import jp.co.future.uroborosql.exception.UroborosqlRuntimeException;
 import jp.co.future.uroborosql.filter.AbstractSqlFilter;
 import jp.co.future.uroborosql.filter.SqlFilterManager;
@@ -344,7 +345,31 @@ public class SqlAgentTest {
 		// 事前条件
 		cleanInsert(Paths.get("src/test/resources/data/setup", "testExecuteQuery.ltsv"));
 
-		Map<String, Object> map = agent.query("example/select_product").paramList("product_id", 0, 1, 2, 3).first();
+		Map<String, Object> map = agent.query("example/select_product").paramList("product_id", 0, 1, 2, 3)
+				.first();
+		assertEquals(new BigDecimal("0"), map.get("PRODUCT_ID"));
+		assertEquals("商品名0", map.get("PRODUCT_NAME"));
+		assertEquals("ショウヒンメイゼロ", map.get("PRODUCT_KANA_NAME"));
+		assertEquals("1234567890123", map.get("JAN_CODE"));
+		assertEquals("0番目の商品", map.get("PRODUCT_DESCRIPTION"));
+	}
+
+	/**
+	 * クエリ実行処理(1件取得)のテストケース(Fluent API)。
+	 */
+	@Test
+	public void testQueryFluentOne() throws Exception {
+		// 事前条件
+		cleanInsert(Paths.get("src/test/resources/data/setup", "testExecuteQuery.ltsv"));
+		try {
+			agent.query("example/select_product").paramList("product_id", 0, 1, 2, 3).one();
+			assertTrue(false);
+		} catch (DataNonUniqueException e) {
+			// OK
+		}
+		Map<String, Object> map = agent.query("example/select_product")
+				.paramList("product_id", 0)
+				.one();
 		assertEquals(new BigDecimal("0"), map.get("PRODUCT_ID"));
 		assertEquals("商品名0", map.get("PRODUCT_NAME"));
 		assertEquals("ショウヒンメイゼロ", map.get("PRODUCT_KANA_NAME"));
@@ -367,7 +392,40 @@ public class SqlAgentTest {
 		config.getSqlAgentFactory().setDefaultMapKeyCaseFormat(CaseFormat.LOWER_SNAKE_CASE);
 		agent = config.agent();
 
-		Map<String, Object> map = agent.query("example/select_product").paramList("product_id", 0, 1, 2, 3).first();
+		Map<String, Object> map = agent.query("example/select_product").paramList("product_id", 0, 1, 2, 3)
+				.first();
+		assertEquals(new BigDecimal("0"), map.get("product_id"));
+		assertEquals("商品名0", map.get("product_name"));
+		assertEquals("ショウヒンメイゼロ", map.get("product_kana_name"));
+		assertEquals("1234567890123", map.get("jan_code"));
+		assertEquals("0番目の商品", map.get("product_description"));
+	}
+
+	/**
+	 * クエリ実行処理(1件取得)のテストケース(Fluent API)。
+	 */
+	@Test
+	public void testQueryFluentOneSetDefaultCaseFormat() throws Exception {
+		// 事前条件
+		cleanInsert(Paths.get("src/test/resources/data/setup", "testExecuteQuery.ltsv"));
+
+		// agentの設定を変更するため、setupで生成したagentをいったんクローズする
+		agent.close();
+
+		// defaultMapKeyCaseFormatの設定
+		config.getSqlAgentFactory().setDefaultMapKeyCaseFormat(CaseFormat.LOWER_SNAKE_CASE);
+		agent = config.agent();
+		try {
+			agent.query("example/select_product")
+					.paramList("product_id", 0, 1, 2, 3)
+					.one();
+			assertTrue(false);
+		} catch (DataNonUniqueException e) {
+			// OK
+		}
+		Map<String, Object> map = agent.query("example/select_product")
+				.paramList("product_id", 0)
+				.one();
 		assertEquals(new BigDecimal("0"), map.get("product_id"));
 		assertEquals("商品名0", map.get("product_name"));
 		assertEquals("ショウヒンメイゼロ", map.get("product_kana_name"));
@@ -398,6 +456,36 @@ public class SqlAgentTest {
 		assertFalse(optional.isPresent());
 	}
 
+	/**
+	 * クエリ実行処理(1件取得:Optional)のテストケース(Fluent API)。
+	 */
+	@Test
+	public void testQueryFluentFindOne() throws Exception {
+		// 事前条件
+		cleanInsert(Paths.get("src/test/resources/data/setup", "testExecuteQuery.ltsv"));
+
+		try {
+			agent.query("example/select_product")
+					.paramList("product_id", 0, 1, 2, 3).findOne();
+			assertTrue(false);
+		} catch (DataNonUniqueException e) {
+			// OK
+		}
+		Optional<Map<String, Object>> optional = agent.query("example/select_product")
+				.paramList("product_id", 0).findOne();
+		assertTrue(optional.isPresent());
+
+		Map<String, Object> map = optional.get();
+		assertEquals(new BigDecimal("0"), map.get("PRODUCT_ID"));
+		assertEquals("商品名0", map.get("PRODUCT_NAME"));
+		assertEquals("ショウヒンメイゼロ", map.get("PRODUCT_KANA_NAME"));
+		assertEquals("1234567890123", map.get("JAN_CODE"));
+		assertEquals("0番目の商品", map.get("PRODUCT_DESCRIPTION"));
+
+		optional = agent.query("example/select_product").paramList("product_id", 4).findOne();
+		assertFalse(optional.isPresent());
+	}
+
 	/**
 	 * クエリ実行処理(1件取得:Optional)のテストケース(Fluent API)。
 	 */
@@ -428,6 +516,44 @@ public class SqlAgentTest {
 		assertFalse(optional.isPresent());
 	}
 
+	/**
+	 * クエリ実行処理(1件取得:Optional)のテストケース(Fluent API)。
+	 */
+	@Test
+	public void testQueryFluentFindOneSetDefaultCaseFormat() throws Exception {
+		// 事前条件
+		cleanInsert(Paths.get("src/test/resources/data/setup", "testExecuteQuery.ltsv"));
+
+		// agentの設定を変更するため、setupで生成したagentをいったんクローズする
+		agent.close();
+
+		// defaultMapKeyCaseFormatの設定
+		config.getSqlAgentFactory().setDefaultMapKeyCaseFormat(CaseFormat.LOWER_SNAKE_CASE);
+		agent = config.agent();
+		try {
+			agent.query("example/select_product")
+					.paramList("product_id", 0, 1, 2, 3)
+					.findOne();
+			assertTrue(false);
+		} catch (DataNonUniqueException e) {
+			// OK
+		}
+		Optional<Map<String, Object>> optional = agent.query("example/select_product")
+				.paramList("product_id", 0)
+				.findOne();
+		assertTrue(optional.isPresent());
+
+		Map<String, Object> map = optional.get();
+		assertEquals(new BigDecimal("0"), map.get("product_id"));
+		assertEquals("商品名0", map.get("product_name"));
+		assertEquals("ショウヒンメイゼロ", map.get("product_kana_name"));
+		assertEquals("1234567890123", map.get("jan_code"));
+		assertEquals("0番目の商品", map.get("product_description"));
+
+		optional = agent.query("example/select_product").paramList("product_id", 4).findOne();
+		assertFalse(optional.isPresent());
+	}
+
 	/**
 	 * クエリ実行処理(1件取得)のテストケース(Fluent API)。
 	 */
@@ -445,6 +571,32 @@ public class SqlAgentTest {
 		assertEquals("0番目の商品", map.get("product_description"));
 	}
 
+	/**
+	 * クエリ実行処理(1件取得)のテストケース(Fluent API)。
+	 */
+	@Test
+	public void testQueryFluentOneCaseFormat() throws Exception {
+		// 事前条件
+		cleanInsert(Paths.get("src/test/resources/data/setup", "testExecuteQuery.ltsv"));
+
+		try {
+			agent.query("example/select_product")
+					.paramList("product_id", 0, 1, 2, 3)
+					.one(CaseFormat.LOWER_SNAKE_CASE);
+			assertTrue(false);
+		} catch (DataNonUniqueException e) {
+			// OK
+		}
+		Map<String, Object> map = agent.query("example/select_product")
+				.paramList("product_id", 0)
+				.one(CaseFormat.LOWER_SNAKE_CASE);
+		assertEquals(new BigDecimal("0"), map.get("product_id"));
+		assertEquals("商品名0", map.get("product_name"));
+		assertEquals("ショウヒンメイゼロ", map.get("product_kana_name"));
+		assertEquals("1234567890123", map.get("jan_code"));
+		assertEquals("0番目の商品", map.get("product_description"));
+	}
+
 	/**
 	 * クエリ実行処理(1件取得:Optional)のテストケース(Fluent API)。
 	 */
@@ -468,6 +620,38 @@ public class SqlAgentTest {
 		assertFalse(optional.isPresent());
 	}
 
+	/**
+	 * クエリ実行処理(1件取得:Optional)のテストケース(Fluent API)。
+	 */
+	@Test
+	public void testQueryFluentFindOneCaseFormat() throws Exception {
+		// 事前条件
+		cleanInsert(Paths.get("src/test/resources/data/setup", "testExecuteQuery.ltsv"));
+
+		try {
+			agent.query("example/select_product")
+					.paramList("product_id", 0, 1, 2, 3)
+					.findOne(CaseFormat.PASCAL_CASE);
+			assertTrue(false);
+		} catch (DataNonUniqueException e) {
+			// OK
+		}
+		Optional<Map<String, Object>> optional = agent.query("example/select_product")
+				.paramList("product_id", 0)
+				.findOne(CaseFormat.PASCAL_CASE);
+		assertTrue(optional.isPresent());
+
+		Map<String, Object> map = optional.get();
+		assertEquals(new BigDecimal("0"), map.get("ProductId"));
+		assertEquals("商品名0", map.get("ProductName"));
+		assertEquals("ショウヒンメイゼロ", map.get("ProductKanaName"));
+		assertEquals("1234567890123", map.get("JanCode"));
+		assertEquals("0番目の商品", map.get("ProductDescription"));
+
+		optional = agent.query("example/select_product").paramList("product_id", 4).findOne();
+		assertFalse(optional.isPresent());
+	}
+
 	/**
 	 * クエリ実行処理(1件取得)のテストケース(Fluent API)。
 	 */
@@ -516,6 +700,68 @@ public class SqlAgentTest {
 		assertFalse(optional.isPresent());
 	}
 
+	/**
+	 * クエリ実行処理(1件取得)のテストケース(Fluent API)。
+	 */
+	@Test
+	public void testQueryFluentOneByClass() throws Exception {
+		// 事前条件
+		cleanInsert(Paths.get("src/test/resources/data/setup", "testExecuteQuery.ltsv"));
+		try {
+			Product product = agent.query("example/select_product")
+					.paramList("product_id", 0, 1, 2, 3)
+					.one(Product.class);
+			assertTrue(false);
+		} catch (DataNonUniqueException e) {
+			// OK
+		}
+		Product product = agent.query("example/select_product")
+				.paramList("product_id", 0)
+				.one(Product.class);
+
+		assertEquals(0, product.getProductId());
+		assertEquals("商品名0", product.getProductName());
+		assertEquals("ショウヒンメイゼロ", product.getProductKanaName());
+		assertEquals("1234567890123", product.getJanCode());
+		assertEquals("0番目の商品", product.getProductDescription());
+	}
+
+	/**
+	 * クエリ実行処理(1件取得:Optional)のテストケース(Fluent API)。
+	 */
+	@Test
+	public void testQueryFluentFindOneByClass() throws Exception {
+		// 事前条件
+		cleanInsert(Paths.get("src/test/resources/data/setup", "testExecuteQuery.ltsv"));
+
+		try {
+			agent.query("example/select_product")
+					.paramList("product_id", 0, 1, 2, 3)
+					.findOne(Product.class);
+			assertTrue(false);
+		} catch (DataNonUniqueException e) {
+			// OK
+		}
+		Optional<Product> optional = agent.query("example/select_product")
+				.paramList("product_id", 0)
+				.findOne(Product.class);
+		assertTrue(optional.isPresent());
+
+		Product product = optional.get();
+
+		assertEquals(0, product.getProductId());
+		assertEquals("商品名0", product.getProductName());
+		assertEquals("ショウヒンメイゼロ", product.getProductKanaName());
+		assertEquals("1234567890123", product.getJanCode());
+		assertEquals("0番目の商品", product.getProductDescription());
+
+		optional = agent.query("example/select_product")
+				.paramList("product_id", 4)
+				.findOne(Product.class);
+
+		assertFalse(optional.isPresent());
+	}
+
 	/**
 	 * クエリ実行処理のテストケース。
 	 */