From c963c4bd6053b23e63cd7dbc4326a0cdc8fed7d1 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sun, 3 Jan 2021 16:00:34 +0300 Subject: [PATCH 1/3] JdbcTemplate: refactoring --- .../java/ghast/database/JdbcOperations.java | 8 +- .../java/ghast/database/JdbcTemplate.java | 122 +++++++++--------- .../java/ghast/database/JdbcTemplateTest.java | 90 ++++++++----- 3 files changed, 122 insertions(+), 98 deletions(-) diff --git a/src/main/java/ghast/database/JdbcOperations.java b/src/main/java/ghast/database/JdbcOperations.java index 89d12ca..9d70f56 100644 --- a/src/main/java/ghast/database/JdbcOperations.java +++ b/src/main/java/ghast/database/JdbcOperations.java @@ -9,15 +9,11 @@ public interface JdbcOperations { T query(String sql, ResultSetExtractor rse) throws DataAccessException; - List query(String sql, RowMapper rowMapper) throws DataAccessException; - - T queryForObject(String sql, RowMapper rowMapper) throws DataAccessException; + List queryList(String sql, RowMapper rowMapper) throws DataAccessException; Map queryForMap(String sql) throws DataAccessException; - List> queryForList(String sql) throws DataAccessException; + List> queryForMapList(String sql) throws DataAccessException; int update(String sql) throws DataAccessException; - - int delete(String sql) throws DataAccessException; } diff --git a/src/main/java/ghast/database/JdbcTemplate.java b/src/main/java/ghast/database/JdbcTemplate.java index c9e629a..52ed3a4 100644 --- a/src/main/java/ghast/database/JdbcTemplate.java +++ b/src/main/java/ghast/database/JdbcTemplate.java @@ -6,17 +6,16 @@ import lombok.NoArgsConstructor; import lombok.Setter; import javax.sql.DataSource; +import java.sql.Date; import java.sql.*; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; @NoArgsConstructor @Getter @Setter public class JdbcTemplate implements JdbcOperations { + private static final String DBG_SQL_INFO = "Execute SQL: {0}"; private DataSource dataSource; public JdbcTemplate(DataSource dataSource) { @@ -25,7 +24,7 @@ public class JdbcTemplate implements JdbcOperations { @Override public void execute(String sql) throws DataAccessException { - XLog.debug("Execute SQL: {0}", sql); + XLog.debug(DBG_SQL_INFO, sql); Connection connection = openConnection(); Statement statement = null; @@ -42,7 +41,7 @@ public class JdbcTemplate implements JdbcOperations { @Override public T query(String sql, ResultSetExtractor rse) throws DataAccessException { - XLog.debug("Execute SQL: {0}", sql); + XLog.debug(DBG_SQL_INFO, sql); Connection connection = openConnection(); Statement statement = null; @@ -61,71 +60,63 @@ public class JdbcTemplate implements JdbcOperations { } @Override - public List query(String sql, final RowMapper rowMapper) throws DataAccessException { + public List queryList(String sql, final RowMapper rowMapper) throws DataAccessException { return query(sql, rs -> { - List resultList = new ArrayList<>(); + List resultList; int rowNum = 0; - while (rs.next()) { - resultList.add(rowMapper.mapRow(rs, rowNum++)); + if (rs.next()) { + resultList = new ArrayList<>(); + + do { + resultList.add(rowMapper.mapRow(rs, rowNum++)); + } while (rs.next()); + } else { + resultList = Collections.emptyList(); } + return resultList; }); } @Override - public T queryForObject(String sql, RowMapper rowMapper) throws DataAccessException { + public Map queryForMap(String sql) throws DataAccessException { return query(sql, rs -> { if (rs.next()) { - T resultObj = rowMapper.mapRow(rs, 0); + ResultSetMetaData metaData = rs.getMetaData(); + int columnCount = metaData.getColumnCount(); - if (rs.next()) { - throw new IncorrectResultSizeDataAccessException(1); - } - - return resultObj; + return rowToMap(columnCount, metaData, rs); } else { - throw new EmptyResultDataAccessException(1); + return Collections.emptyMap(); } }); } @Override - public Map queryForMap(String sql) throws DataAccessException { - return queryForObject(sql, (rs, rowNum) -> { - ResultSetMetaData metaData = rs.getMetaData(); - int columnCount = metaData.getColumnCount(); - Map resultMap = new LinkedHashMap<>(columnCount); + public List> queryForMapList(String sql) throws DataAccessException { + return query(sql, rs -> { + List> resultList; - for (int i = 1; i <= columnCount; i++) { - String key = lookupColumnName(metaData, i); - Object value = getResultSetRawValue(rs, i); - resultMap.put(key, value); + if (rs.next()) { + resultList = new ArrayList<>(); + + ResultSetMetaData metaData = rs.getMetaData(); + int columnCount = metaData.getColumnCount(); + + do { + resultList.add(rowToMap(columnCount, metaData, rs)); + } while (rs.next()); + } else { + resultList = Collections.emptyList(); } - return resultMap; - }); - } - - @Override - public List> queryForList(String sql) throws DataAccessException { - return query(sql, (rs, rowNum) -> { - ResultSetMetaData metaData = rs.getMetaData(); - int columnCount = metaData.getColumnCount(); - Map resultMap = new LinkedHashMap<>(columnCount); - - for (int i = 1; i <= columnCount; i++) { - String key = lookupColumnName(metaData, i); - Object value = getResultSetRawValue(rs, i); - resultMap.put(key, value); - } - - return resultMap; + return resultList; }); } @Override public int update(String sql) throws DataAccessException { - XLog.debug("Execute SQL: {0}", sql); + XLog.debug(DBG_SQL_INFO, sql); Connection connection = openConnection(); Statement statement = null; @@ -143,16 +134,10 @@ public class JdbcTemplate implements JdbcOperations { } } - @Override - public int delete(String sql) throws DataAccessException { - return update(sql); - } - private Connection openConnection() { try { return getDataSource().getConnection(); - } - catch (SQLException ex) { + } catch (SQLException ex) { throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex); } } @@ -165,13 +150,26 @@ public class JdbcTemplate implements JdbcOperations { return name; } + private Map rowToMap(int columnCount, ResultSetMetaData metaData, ResultSet rs) throws SQLException { + Map rowMap = new LinkedHashMap<>(columnCount); + + for (int i = 1; i <= columnCount; i++) { + String key = lookupColumnName(metaData, i); + Object value = getResultSetRawValue(rs, i); + rowMap.put(key, value); + } + + return rowMap; + } + private Object getResultSetRawValue(ResultSet resultSet, int index) throws SQLException { Object obj = resultSet.getObject(index); - String className = null; - if (obj != null) { - className = obj.getClass().getName(); + if (obj == null) { + return null; } + String className = obj.getClass().getName(); + if (obj instanceof Blob) { Blob blob = (Blob) obj; obj = blob.getBytes(1, (int) blob.length()); @@ -180,18 +178,16 @@ public class JdbcTemplate implements JdbcOperations { obj = clob.getSubString(1, (int) clob.length()); } else if ("oracle.sql.TIMESTAMP".equals(className) || "oracle.sql.TIMESTAMPTZ".equals(className)) { obj = resultSet.getTimestamp(index); - } else if (className != null && className.startsWith("oracle.sql.DATE")) { + } else if (className.startsWith("oracle.sql.DATE")) { String metaDataClassName = resultSet.getMetaData().getColumnClassName(index); if ("java.sql.Timestamp".equals(metaDataClassName) || "oracle.sql.TIMESTAMP".equals(metaDataClassName)) { obj = resultSet.getTimestamp(index); - } - else { + } else { obj = resultSet.getDate(index); } - } else if (obj instanceof Date) { - if ("java.sql.Timestamp".equals(resultSet.getMetaData().getColumnClassName(index))) { - obj = resultSet.getTimestamp(index); - } + } else if (obj instanceof Date + && "java.sql.Timestamp".equals(resultSet.getMetaData().getColumnClassName(index))) { + obj = resultSet.getTimestamp(index); } return obj; diff --git a/src/test/java/ghast/database/JdbcTemplateTest.java b/src/test/java/ghast/database/JdbcTemplateTest.java index c5ff0d2..bbdc8bc 100644 --- a/src/test/java/ghast/database/JdbcTemplateTest.java +++ b/src/test/java/ghast/database/JdbcTemplateTest.java @@ -84,7 +84,7 @@ class JdbcTemplateTest { } @Test - void testQuery_Single() { + void testQuery_Simple_Single() { String sql = MessageFormat.format("SELECT {2} FROM {0} WHERE {1} LIKE ''{3}''", TABLE_NAME, COLUMN_NAME, COLUMN_VALUE, DATA[0][0]); @@ -100,44 +100,30 @@ class JdbcTemplateTest { } @Test - void testQuery_List() { + void testQuery_Simple_List() { String sql = MessageFormat.format("SELECT {2} FROM {0} WHERE {1} LIKE ''{3}'' OR {1} LIKE ''{4}''", TABLE_NAME, COLUMN_NAME, COLUMN_VALUE, DATA[0][0], DATA[1][0]); - List listValues = jdbcTemplate.query(sql, (rs, rowNum) -> rs.getInt(1)); + List listValues = jdbcTemplate.queryList(sql, (rs, rowNum) -> rs.getInt(1)); assertIterableEquals(Lists.newArrayList(DATA[0][1], DATA[1][1]), listValues); } @Test - void testQueryForObject() { - class Player { - String name; - int value; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Player)) return false; - Player player = (Player) o; - return new EqualsBuilder().append(value, player.value).append(name, player.name).isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37).append(name).append(value).toHashCode(); - } - } - + void testQuery_Object_Single() { String sql = MessageFormat.format("SELECT {1}, {2} FROM {0} WHERE {1} LIKE ''{3}''", TABLE_NAME, COLUMN_NAME, COLUMN_VALUE, DATA[0][0]); - Player actualPlayer = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> { - Player player0 = new Player(); - player0.name = rs.getString(COLUMN_NAME); - player0.value = rs.getInt(COLUMN_VALUE); + Player actualPlayer = jdbcTemplate.query(sql, rs -> { + if (rs.next()) { + Player player0 = new Player(); + player0.name = rs.getString(COLUMN_NAME); + player0.value = rs.getInt(COLUMN_VALUE); - return player0; + return player0; + } else { + return null; + } }); Player expectedPlayer = new Player(); @@ -147,6 +133,34 @@ class JdbcTemplateTest { assertEquals(expectedPlayer, actualPlayer); } + @Test + void testQuery_Object_List() { + String sql = MessageFormat.format("SELECT {1}, {2} FROM {0}", + TABLE_NAME, COLUMN_NAME, COLUMN_VALUE); + + List actualPlayers = jdbcTemplate.queryList(sql, (rs, num) -> { + Player player0 = new Player(); + player0.name = rs.getString(COLUMN_NAME); + player0.value = rs.getInt(COLUMN_VALUE); + + return player0; + }); + + + List expectedPlayers = Stream.of(DATA) + .map(datum -> { + Player player1 = new Player(); + player1.name = (String) datum[0]; + player1.value = (int) datum[1]; + + return player1; + }) + .collect(Collectors.toList()); + + + assertIterableEquals(expectedPlayers, actualPlayers); + } + @Test void testQueryForMap() { String sql = MessageFormat.format("SELECT {1}, {2} FROM {0} WHERE {1} LIKE ''{3}''", @@ -162,11 +176,11 @@ class JdbcTemplateTest { } @Test - void testQueryForList() { + void testQueryForMapList() { String sql = MessageFormat.format("SELECT {1}, {2} FROM {0}", TABLE_NAME, COLUMN_NAME, COLUMN_VALUE); - List> actualMapList = jdbcTemplate.queryForList(sql); + List> actualMapList = jdbcTemplate.queryForMapList(sql); List> expectedMapList = Stream.of(DATA) .map(datum -> ImmutableMap.of(COLUMN_NAME, datum[0], COLUMN_VALUE, datum[1])) @@ -211,6 +225,24 @@ class JdbcTemplateTest { jdbcTemplate.execute("DROP TABLE IF EXISTS " + TABLE_NAME); } + class Player { + String name; + int value; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Player)) return false; + Player player = (Player) o; + return new EqualsBuilder().append(value, player.value).append(name, player.name).isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37).append(name).append(value).toHashCode(); + } + } + @Nested class JdbcTemplateTest_ExecuteTestCase { From f82169130826c0b12981eeb352d6a24074cb2a46 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sun, 3 Jan 2021 16:07:40 +0300 Subject: [PATCH 2/3] JdbcTemplate: add queryOne method --- src/main/java/ghast/database/JdbcOperations.java | 3 +++ src/main/java/ghast/database/JdbcTemplate.java | 11 +++++++++++ src/test/java/ghast/database/JdbcTemplateTest.java | 12 ++++++++++++ 3 files changed, 26 insertions(+) diff --git a/src/main/java/ghast/database/JdbcOperations.java b/src/main/java/ghast/database/JdbcOperations.java index 9d70f56..6a4aa50 100644 --- a/src/main/java/ghast/database/JdbcOperations.java +++ b/src/main/java/ghast/database/JdbcOperations.java @@ -2,6 +2,7 @@ package ghast.database; import java.util.List; import java.util.Map; +import java.util.Optional; public interface JdbcOperations { @@ -9,6 +10,8 @@ public interface JdbcOperations { T query(String sql, ResultSetExtractor rse) throws DataAccessException; + Optional queryOne(String sql, ResultSetExtractor rse) throws DataAccessException; + List queryList(String sql, RowMapper rowMapper) throws DataAccessException; Map queryForMap(String sql) throws DataAccessException; diff --git a/src/main/java/ghast/database/JdbcTemplate.java b/src/main/java/ghast/database/JdbcTemplate.java index 52ed3a4..3b91032 100644 --- a/src/main/java/ghast/database/JdbcTemplate.java +++ b/src/main/java/ghast/database/JdbcTemplate.java @@ -59,6 +59,17 @@ public class JdbcTemplate implements JdbcOperations { } } + @Override + public Optional queryOne(String sql, ResultSetExtractor rse) throws DataAccessException { + return query(sql, rs -> { + if (rs.next()) { + return Optional.ofNullable(rse.extractData(rs)); + } else { + return Optional.empty(); + } + }); + } + @Override public List queryList(String sql, final RowMapper rowMapper) throws DataAccessException { return query(sql, rs -> { diff --git a/src/test/java/ghast/database/JdbcTemplateTest.java b/src/test/java/ghast/database/JdbcTemplateTest.java index bbdc8bc..97a3f97 100644 --- a/src/test/java/ghast/database/JdbcTemplateTest.java +++ b/src/test/java/ghast/database/JdbcTemplateTest.java @@ -17,6 +17,7 @@ import java.sql.SQLException; import java.text.MessageFormat; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.StringJoiner; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -99,6 +100,17 @@ class JdbcTemplateTest { assertEquals(DATA[0][1], value); } + @Test + void testQuery_Simple_Optional() { + String sql = MessageFormat.format("SELECT {2} FROM {0} WHERE {1} LIKE ''{3}''", + TABLE_NAME, COLUMN_NAME, COLUMN_VALUE, DATA[0][0]); + + Optional optValue = jdbcTemplate.queryOne(sql, rs -> rs.getInt(1)); + + assertTrue(optValue.isPresent()); + assertEquals(DATA[0][1], optValue.get()); + } + @Test void testQuery_Simple_List() { String sql = MessageFormat.format("SELECT {2} FROM {0} WHERE {1} LIKE ''{3}'' OR {1} LIKE ''{4}''", From 3037aed8ef070ae58f73b2a4dcf84c8cb27ed0a2 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sun, 3 Jan 2021 16:09:24 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=D1=83=D0=B1=D1=80=D0=B0=D0=BD=20=D0=BB?= =?UTF-8?q?=D0=B8=D1=88=D0=BD=D0=B8=D0=B9=20=D0=BA=D0=BE=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/ghast/database/DataAccessException.java | 9 --------- .../ghast/database/EmptyResultDataAccessException.java | 8 -------- .../database/IncorrectResultSizeDataAccessException.java | 8 -------- 3 files changed, 25 deletions(-) delete mode 100644 src/main/java/ghast/database/EmptyResultDataAccessException.java delete mode 100644 src/main/java/ghast/database/IncorrectResultSizeDataAccessException.java diff --git a/src/main/java/ghast/database/DataAccessException.java b/src/main/java/ghast/database/DataAccessException.java index 5c686b5..445a104 100644 --- a/src/main/java/ghast/database/DataAccessException.java +++ b/src/main/java/ghast/database/DataAccessException.java @@ -8,15 +8,6 @@ public class DataAccessException extends RuntimeException { private String sql; - public DataAccessException(String msg) { - super(msg); - } - - public DataAccessException(String msg, String sql) { - this(msg); - this.sql = sql; - } - public DataAccessException(String msg, Throwable cause) { super(msg, cause); } diff --git a/src/main/java/ghast/database/EmptyResultDataAccessException.java b/src/main/java/ghast/database/EmptyResultDataAccessException.java deleted file mode 100644 index 0d42ee1..0000000 --- a/src/main/java/ghast/database/EmptyResultDataAccessException.java +++ /dev/null @@ -1,8 +0,0 @@ -package ghast.database; - -public class EmptyResultDataAccessException extends IncorrectResultSizeDataAccessException { - - public EmptyResultDataAccessException(int expectedSize) { - super(expectedSize); - } -} diff --git a/src/main/java/ghast/database/IncorrectResultSizeDataAccessException.java b/src/main/java/ghast/database/IncorrectResultSizeDataAccessException.java deleted file mode 100644 index 5acd10d..0000000 --- a/src/main/java/ghast/database/IncorrectResultSizeDataAccessException.java +++ /dev/null @@ -1,8 +0,0 @@ -package ghast.database; - -public class IncorrectResultSizeDataAccessException extends DataAccessException { - - public IncorrectResultSizeDataAccessException(int expectedSize) { - super("Incorrect result size: expected " + expectedSize); - } -}