0

Merge branch 'feature/jdbctemplate' into develop

This commit is contained in:
2021-01-03 16:15:44 +03:00
6 changed files with 148 additions and 123 deletions

View File

@@ -8,15 +8,6 @@ public class DataAccessException extends RuntimeException {
private String sql; 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) { public DataAccessException(String msg, Throwable cause) {
super(msg, cause); super(msg, cause);
} }

View File

@@ -1,8 +0,0 @@
package ghast.database;
public class EmptyResultDataAccessException extends IncorrectResultSizeDataAccessException {
public EmptyResultDataAccessException(int expectedSize) {
super(expectedSize);
}
}

View File

@@ -1,8 +0,0 @@
package ghast.database;
public class IncorrectResultSizeDataAccessException extends DataAccessException {
public IncorrectResultSizeDataAccessException(int expectedSize) {
super("Incorrect result size: expected " + expectedSize);
}
}

View File

@@ -2,6 +2,7 @@ package ghast.database;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
public interface JdbcOperations { public interface JdbcOperations {
@@ -9,15 +10,13 @@ public interface JdbcOperations {
<T> T query(String sql, ResultSetExtractor<T> rse) throws DataAccessException; <T> T query(String sql, ResultSetExtractor<T> rse) throws DataAccessException;
<T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException; <T> Optional<T> queryOne(String sql, ResultSetExtractor<T> rse) throws DataAccessException;
<T> T queryForObject(String sql, RowMapper<T> rowMapper) throws DataAccessException; <T> List<T> queryList(String sql, RowMapper<T> rowMapper) throws DataAccessException;
Map<String, Object> queryForMap(String sql) throws DataAccessException; Map<String, Object> queryForMap(String sql) throws DataAccessException;
List<Map<String, Object>> queryForList(String sql) throws DataAccessException; List<Map<String, Object>> queryForMapList(String sql) throws DataAccessException;
int update(String sql) throws DataAccessException; int update(String sql) throws DataAccessException;
int delete(String sql) throws DataAccessException;
} }

View File

@@ -6,17 +6,16 @@ import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.sql.Date;
import java.sql.*; import java.sql.*;
import java.util.ArrayList; import java.util.*;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@NoArgsConstructor @NoArgsConstructor
@Getter @Getter
@Setter @Setter
public class JdbcTemplate implements JdbcOperations { public class JdbcTemplate implements JdbcOperations {
private static final String DBG_SQL_INFO = "Execute SQL: {0}";
private DataSource dataSource; private DataSource dataSource;
public JdbcTemplate(DataSource dataSource) { public JdbcTemplate(DataSource dataSource) {
@@ -25,7 +24,7 @@ public class JdbcTemplate implements JdbcOperations {
@Override @Override
public void execute(String sql) throws DataAccessException { public void execute(String sql) throws DataAccessException {
XLog.debug("Execute SQL: {0}", sql); XLog.debug(DBG_SQL_INFO, sql);
Connection connection = openConnection(); Connection connection = openConnection();
Statement statement = null; Statement statement = null;
@@ -42,7 +41,7 @@ public class JdbcTemplate implements JdbcOperations {
@Override @Override
public <T> T query(String sql, ResultSetExtractor<T> rse) throws DataAccessException { public <T> T query(String sql, ResultSetExtractor<T> rse) throws DataAccessException {
XLog.debug("Execute SQL: {0}", sql); XLog.debug(DBG_SQL_INFO, sql);
Connection connection = openConnection(); Connection connection = openConnection();
Statement statement = null; Statement statement = null;
@@ -61,71 +60,74 @@ public class JdbcTemplate implements JdbcOperations {
} }
@Override @Override
public <T> List<T> query(String sql, final RowMapper<T> rowMapper) throws DataAccessException { public <T> Optional<T> queryOne(String sql, ResultSetExtractor<T> rse) throws DataAccessException {
return query(sql, rs -> { return query(sql, rs -> {
List<T> resultList = new ArrayList<>(); if (rs.next()) {
int rowNum = 0; return Optional.ofNullable(rse.extractData(rs));
while (rs.next()) { } else {
resultList.add(rowMapper.mapRow(rs, rowNum++)); return Optional.empty();
} }
});
}
@Override
public <T> List<T> queryList(String sql, final RowMapper<T> rowMapper) throws DataAccessException {
return query(sql, rs -> {
List<T> resultList;
int rowNum = 0;
if (rs.next()) {
resultList = new ArrayList<>();
do {
resultList.add(rowMapper.mapRow(rs, rowNum++));
} while (rs.next());
} else {
resultList = Collections.emptyList();
}
return resultList; return resultList;
}); });
} }
@Override @Override
public <T> T queryForObject(String sql, RowMapper<T> rowMapper) throws DataAccessException { public Map<String, Object> queryForMap(String sql) throws DataAccessException {
return query(sql, rs -> { return query(sql, rs -> {
if (rs.next()) { if (rs.next()) {
T resultObj = rowMapper.mapRow(rs, 0); ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
if (rs.next()) { return rowToMap(columnCount, metaData, rs);
throw new IncorrectResultSizeDataAccessException(1);
}
return resultObj;
} else { } else {
throw new EmptyResultDataAccessException(1); return Collections.emptyMap();
} }
}); });
} }
@Override @Override
public Map<String, Object> queryForMap(String sql) throws DataAccessException { public List<Map<String, Object>> queryForMapList(String sql) throws DataAccessException {
return queryForObject(sql, (rs, rowNum) -> { return query(sql, rs -> {
ResultSetMetaData metaData = rs.getMetaData(); List<Map<String, Object>> resultList;
int columnCount = metaData.getColumnCount();
Map<String, Object> resultMap = new LinkedHashMap<>(columnCount);
for (int i = 1; i <= columnCount; i++) { if (rs.next()) {
String key = lookupColumnName(metaData, i); resultList = new ArrayList<>();
Object value = getResultSetRawValue(rs, i);
resultMap.put(key, value); ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
do {
resultList.add(rowToMap(columnCount, metaData, rs));
} while (rs.next());
} else {
resultList = Collections.emptyList();
} }
return resultMap; return resultList;
});
}
@Override
public List<Map<String, Object>> queryForList(String sql) throws DataAccessException {
return query(sql, (rs, rowNum) -> {
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
Map<String, Object> 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;
}); });
} }
@Override @Override
public int update(String sql) throws DataAccessException { public int update(String sql) throws DataAccessException {
XLog.debug("Execute SQL: {0}", sql); XLog.debug(DBG_SQL_INFO, sql);
Connection connection = openConnection(); Connection connection = openConnection();
Statement statement = null; Statement statement = null;
@@ -143,16 +145,10 @@ public class JdbcTemplate implements JdbcOperations {
} }
} }
@Override
public int delete(String sql) throws DataAccessException {
return update(sql);
}
private Connection openConnection() { private Connection openConnection() {
try { try {
return getDataSource().getConnection(); return getDataSource().getConnection();
} } catch (SQLException ex) {
catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex); throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
} }
} }
@@ -165,13 +161,26 @@ public class JdbcTemplate implements JdbcOperations {
return name; return name;
} }
private Map<String, Object> rowToMap(int columnCount, ResultSetMetaData metaData, ResultSet rs) throws SQLException {
Map<String, Object> 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 { private Object getResultSetRawValue(ResultSet resultSet, int index) throws SQLException {
Object obj = resultSet.getObject(index); Object obj = resultSet.getObject(index);
String className = null; if (obj == null) {
if (obj != null) { return null;
className = obj.getClass().getName();
} }
String className = obj.getClass().getName();
if (obj instanceof Blob) { if (obj instanceof Blob) {
Blob blob = (Blob) obj; Blob blob = (Blob) obj;
obj = blob.getBytes(1, (int) blob.length()); obj = blob.getBytes(1, (int) blob.length());
@@ -180,18 +189,16 @@ public class JdbcTemplate implements JdbcOperations {
obj = clob.getSubString(1, (int) clob.length()); obj = clob.getSubString(1, (int) clob.length());
} else if ("oracle.sql.TIMESTAMP".equals(className) || "oracle.sql.TIMESTAMPTZ".equals(className)) { } else if ("oracle.sql.TIMESTAMP".equals(className) || "oracle.sql.TIMESTAMPTZ".equals(className)) {
obj = resultSet.getTimestamp(index); 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); String metaDataClassName = resultSet.getMetaData().getColumnClassName(index);
if ("java.sql.Timestamp".equals(metaDataClassName) || "oracle.sql.TIMESTAMP".equals(metaDataClassName)) { if ("java.sql.Timestamp".equals(metaDataClassName) || "oracle.sql.TIMESTAMP".equals(metaDataClassName)) {
obj = resultSet.getTimestamp(index); obj = resultSet.getTimestamp(index);
} } else {
else {
obj = resultSet.getDate(index); obj = resultSet.getDate(index);
} }
} else if (obj instanceof Date) { } else if (obj instanceof Date
if ("java.sql.Timestamp".equals(resultSet.getMetaData().getColumnClassName(index))) { && "java.sql.Timestamp".equals(resultSet.getMetaData().getColumnClassName(index))) {
obj = resultSet.getTimestamp(index); obj = resultSet.getTimestamp(index);
}
} }
return obj; return obj;

View File

@@ -17,6 +17,7 @@ import java.sql.SQLException;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.StringJoiner; import java.util.StringJoiner;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -84,7 +85,7 @@ class JdbcTemplateTest {
} }
@Test @Test
void testQuery_Single() { void testQuery_Simple_Single() {
String sql = MessageFormat.format("SELECT {2} FROM {0} WHERE {1} LIKE ''{3}''", String sql = MessageFormat.format("SELECT {2} FROM {0} WHERE {1} LIKE ''{3}''",
TABLE_NAME, COLUMN_NAME, COLUMN_VALUE, DATA[0][0]); TABLE_NAME, COLUMN_NAME, COLUMN_VALUE, DATA[0][0]);
@@ -100,44 +101,41 @@ class JdbcTemplateTest {
} }
@Test @Test
void testQuery_List() { 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<Integer> 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}''", 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]); TABLE_NAME, COLUMN_NAME, COLUMN_VALUE, DATA[0][0], DATA[1][0]);
List<Integer> listValues = jdbcTemplate.query(sql, (rs, rowNum) -> rs.getInt(1)); List<Integer> listValues = jdbcTemplate.queryList(sql, (rs, rowNum) -> rs.getInt(1));
assertIterableEquals(Lists.newArrayList(DATA[0][1], DATA[1][1]), listValues); assertIterableEquals(Lists.newArrayList(DATA[0][1], DATA[1][1]), listValues);
} }
@Test @Test
void testQueryForObject() { void testQuery_Object_Single() {
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();
}
}
String sql = MessageFormat.format("SELECT {1}, {2} FROM {0} WHERE {1} LIKE ''{3}''", String sql = MessageFormat.format("SELECT {1}, {2} FROM {0} WHERE {1} LIKE ''{3}''",
TABLE_NAME, COLUMN_NAME, COLUMN_VALUE, DATA[0][0]); TABLE_NAME, COLUMN_NAME, COLUMN_VALUE, DATA[0][0]);
Player actualPlayer = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> { Player actualPlayer = jdbcTemplate.query(sql, rs -> {
Player player0 = new Player(); if (rs.next()) {
player0.name = rs.getString(COLUMN_NAME); Player player0 = new Player();
player0.value = rs.getInt(COLUMN_VALUE); player0.name = rs.getString(COLUMN_NAME);
player0.value = rs.getInt(COLUMN_VALUE);
return player0; return player0;
} else {
return null;
}
}); });
Player expectedPlayer = new Player(); Player expectedPlayer = new Player();
@@ -147,6 +145,34 @@ class JdbcTemplateTest {
assertEquals(expectedPlayer, actualPlayer); assertEquals(expectedPlayer, actualPlayer);
} }
@Test
void testQuery_Object_List() {
String sql = MessageFormat.format("SELECT {1}, {2} FROM {0}",
TABLE_NAME, COLUMN_NAME, COLUMN_VALUE);
List<Player> 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<Player> 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 @Test
void testQueryForMap() { void testQueryForMap() {
String sql = MessageFormat.format("SELECT {1}, {2} FROM {0} WHERE {1} LIKE ''{3}''", String sql = MessageFormat.format("SELECT {1}, {2} FROM {0} WHERE {1} LIKE ''{3}''",
@@ -162,11 +188,11 @@ class JdbcTemplateTest {
} }
@Test @Test
void testQueryForList() { void testQueryForMapList() {
String sql = MessageFormat.format("SELECT {1}, {2} FROM {0}", String sql = MessageFormat.format("SELECT {1}, {2} FROM {0}",
TABLE_NAME, COLUMN_NAME, COLUMN_VALUE); TABLE_NAME, COLUMN_NAME, COLUMN_VALUE);
List<Map<String, Object>> actualMapList = jdbcTemplate.queryForList(sql); List<Map<String, Object>> actualMapList = jdbcTemplate.queryForMapList(sql);
List<Map<String, Object>> expectedMapList = Stream.of(DATA) List<Map<String, Object>> expectedMapList = Stream.of(DATA)
.map(datum -> ImmutableMap.of(COLUMN_NAME, datum[0], COLUMN_VALUE, datum[1])) .map(datum -> ImmutableMap.of(COLUMN_NAME, datum[0], COLUMN_VALUE, datum[1]))
@@ -211,6 +237,24 @@ class JdbcTemplateTest {
jdbcTemplate.execute("DROP TABLE IF EXISTS " + TABLE_NAME); 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 @Nested
class JdbcTemplateTest_ExecuteTestCase { class JdbcTemplateTest_ExecuteTestCase {