first commit
This commit is contained in:
11
src/main/java/ru/di9/jdbc/AbstractEntity.java
Normal file
11
src/main/java/ru/di9/jdbc/AbstractEntity.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package ru.di9.jdbc;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class AbstractEntity<T> {
|
||||
|
||||
protected T id;
|
||||
}
|
||||
19
src/main/java/ru/di9/jdbc/DataAccessException.java
Normal file
19
src/main/java/ru/di9/jdbc/DataAccessException.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package ru.di9.jdbc;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class DataAccessException extends RuntimeException {
|
||||
|
||||
@SuppressWarnings("java:S1165")
|
||||
private String sql;
|
||||
|
||||
public DataAccessException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
public DataAccessException(String msg, String sql, Throwable cause) {
|
||||
this(msg + " | " + sql, cause);
|
||||
this.sql = sql;
|
||||
}
|
||||
}
|
||||
36
src/main/java/ru/di9/jdbc/JdbcTemplate.java
Normal file
36
src/main/java/ru/di9/jdbc/JdbcTemplate.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package ru.di9.jdbc;
|
||||
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface JdbcTemplate {
|
||||
|
||||
void execute(@Language("GenericSQL") String sql) throws DataAccessException;
|
||||
|
||||
void execute(@Language("GenericSQL") String sql, PreparedStatementProcessor psp) throws DataAccessException;
|
||||
|
||||
<T> T query(@Language("GenericSQL") String sql, ResultSetExtractor<T> rse) throws DataAccessException;
|
||||
|
||||
<T> T query(@Language("GenericSQL") String sql, PreparedStatementProcessor psp, ResultSetExtractor<T> rse)
|
||||
throws DataAccessException;
|
||||
|
||||
<T> Optional<T> queryOne(@Language("GenericSQL") String sql, ResultSetExtractor<T> rse)
|
||||
throws DataAccessException;
|
||||
|
||||
<T> Optional<T> queryOne(@Language("GenericSQL") String sql, PreparedStatementProcessor psp,
|
||||
ResultSetExtractor<T> rse) throws DataAccessException;
|
||||
|
||||
<T> List<T> queryList(@Language("GenericSQL") String sql,
|
||||
final RowMapper<T> rowMapper) throws DataAccessException;
|
||||
|
||||
<T> List<T> queryList(@Language("GenericSQL") String sql, PreparedStatementProcessor psp,
|
||||
final RowMapper<T> rowMapper) throws DataAccessException;
|
||||
|
||||
<T> T insert(@Language("GenericSQL") String sql, PreparedStatementProcessor psp,
|
||||
ResultSetExtractor<T> processGeneratedKey) throws DataAccessException;
|
||||
|
||||
void transaction(Consumer<JdbcTemplate> consumer);
|
||||
}
|
||||
169
src/main/java/ru/di9/jdbc/JdbcTemplateImpl.java
Normal file
169
src/main/java/ru/di9/jdbc/JdbcTemplateImpl.java
Normal file
@@ -0,0 +1,169 @@
|
||||
package ru.di9.jdbc;
|
||||
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class JdbcTemplateImpl implements JdbcTemplate {
|
||||
private final DataSource dataSource;
|
||||
|
||||
public JdbcTemplateImpl(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@Language("GenericSQL") String sql) throws DataAccessException {
|
||||
try (Connection connection = dataSource.getConnection();
|
||||
Statement statement = connection.createStatement()) {
|
||||
statement.execute(sql);
|
||||
} catch (SQLException e) {
|
||||
throw throwDataAccessException(sql, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@Language("GenericSQL") String sql, PreparedStatementProcessor psp) throws DataAccessException {
|
||||
try (Connection connection = dataSource.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
|
||||
psp.process(preparedStatement);
|
||||
preparedStatement.execute();
|
||||
} catch (SQLException e) {
|
||||
throw throwDataAccessException(sql, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T query(@Language("GenericSQL") String sql, ResultSetExtractor<T> rse) throws DataAccessException {
|
||||
try (Connection connection = dataSource.getConnection();
|
||||
Statement statement = connection.createStatement();
|
||||
ResultSet resultSet = statement.executeQuery(sql)) {
|
||||
return rse.extractData(resultSet);
|
||||
} catch (SQLException e) {
|
||||
throw throwDataAccessException(sql, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T query(@Language("GenericSQL") String sql, PreparedStatementProcessor psp,
|
||||
ResultSetExtractor<T> rse) throws DataAccessException {
|
||||
try (Connection connection = dataSource.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
|
||||
psp.process(preparedStatement);
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
return rse.extractData(resultSet);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw throwDataAccessException(sql, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> queryOne(@Language("GenericSQL") String sql,
|
||||
ResultSetExtractor<T> rse) throws DataAccessException {
|
||||
return query(sql, rs -> {
|
||||
if (rs.next()) {
|
||||
return Optional.ofNullable(rse.extractData(rs));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> queryOne(@Language("GenericSQL") String sql, PreparedStatementProcessor psp,
|
||||
ResultSetExtractor<T> rse) throws DataAccessException {
|
||||
return query(sql, psp, rs -> {
|
||||
if (rs.next()) {
|
||||
return Optional.ofNullable(rse.extractData(rs));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> queryList(@Language("GenericSQL") String sql,
|
||||
final RowMapper<T> rowMapper) throws DataAccessException {
|
||||
return query(sql, createResultSetExtractorList(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> queryList(@Language("GenericSQL") String sql, PreparedStatementProcessor psp,
|
||||
final RowMapper<T> rowMapper) throws DataAccessException {
|
||||
return query(sql, psp, createResultSetExtractorList(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T insert(@Language("GenericSQL") String sql, PreparedStatementProcessor psp,
|
||||
ResultSetExtractor<T> processGeneratedKey) throws DataAccessException {
|
||||
try (Connection connection = dataSource.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
|
||||
psp.process(preparedStatement);
|
||||
preparedStatement.execute();
|
||||
try (ResultSet generatedKeys = preparedStatement.getGeneratedKeys()) {
|
||||
generatedKeys.next();
|
||||
return processGeneratedKey.extractData(generatedKeys);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw throwDataAccessException(sql, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transaction(Consumer<JdbcTemplate> consumer) {
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = dataSource.getConnection();
|
||||
connection.createStatement().execute("BEGIN TRANSACTION");
|
||||
consumer.accept(new JdbcTemplateTransactional(connection));
|
||||
connection.createStatement().execute("COMMIT");
|
||||
} catch (SQLException e) {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.createStatement().execute("ROLLBACK");
|
||||
throw new DataAccessException("Error transaction", e);
|
||||
} catch (SQLException e1) {
|
||||
DataAccessException exception = new DataAccessException("Error rollback", e1);
|
||||
exception.addSuppressed(e1);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
throw new DataAccessException("Error close connection", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <T> ResultSetExtractor<List<T>> createResultSetExtractorList(final RowMapper<T> rowMapper) {
|
||||
return 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;
|
||||
};
|
||||
}
|
||||
|
||||
static DataAccessException throwDataAccessException(String sql, Exception e) {
|
||||
return new DataAccessException("Error execute SQL", sql, e);
|
||||
}
|
||||
}
|
||||
132
src/main/java/ru/di9/jdbc/JdbcTemplateTransactional.java
Normal file
132
src/main/java/ru/di9/jdbc/JdbcTemplateTransactional.java
Normal file
@@ -0,0 +1,132 @@
|
||||
package ru.di9.jdbc;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static ru.di9.jdbc.JdbcTemplateImpl.throwDataAccessException;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class JdbcTemplateTransactional implements JdbcTemplate, AutoCloseable {
|
||||
private final Connection connection;
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
connection.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String sql) throws DataAccessException {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.execute(sql);
|
||||
} catch (SQLException e) {
|
||||
throw throwDataAccessException(sql, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(String sql, PreparedStatementProcessor psp) throws DataAccessException {
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
|
||||
psp.process(preparedStatement);
|
||||
preparedStatement.execute();
|
||||
} catch (SQLException e) {
|
||||
throw throwDataAccessException(sql, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T query(String sql, ResultSetExtractor<T> rse) throws DataAccessException {
|
||||
try (Statement statement = connection.createStatement();
|
||||
ResultSet resultSet = statement.executeQuery(sql)) {
|
||||
return rse.extractData(resultSet);
|
||||
} catch (SQLException e) {
|
||||
throw throwDataAccessException(sql, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T query(String sql, PreparedStatementProcessor psp, ResultSetExtractor<T> rse) throws DataAccessException {
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
|
||||
psp.process(preparedStatement);
|
||||
try (ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
return rse.extractData(resultSet);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw throwDataAccessException(sql, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> queryOne(String sql, ResultSetExtractor<T> rse) throws DataAccessException {
|
||||
return query(sql, rs -> {
|
||||
if (rs.next()) {
|
||||
return Optional.ofNullable(rse.extractData(rs));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> queryOne(String sql, PreparedStatementProcessor psp, ResultSetExtractor<T> rse) throws DataAccessException {
|
||||
return query(sql, psp, rs -> {
|
||||
if (rs.next()) {
|
||||
return Optional.ofNullable(rse.extractData(rs));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> queryList(String sql, RowMapper<T> rowMapper) throws DataAccessException {
|
||||
return query(sql, createResultSetExtractorList(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> queryList(String sql, PreparedStatementProcessor psp, RowMapper<T> rowMapper) throws DataAccessException {
|
||||
return query(sql, psp, createResultSetExtractorList(rowMapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T insert(String sql, PreparedStatementProcessor psp, ResultSetExtractor<T> processGeneratedKey) throws DataAccessException {
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
|
||||
psp.process(preparedStatement);
|
||||
preparedStatement.execute();
|
||||
try (ResultSet generatedKeys = preparedStatement.getGeneratedKeys()) {
|
||||
generatedKeys.next();
|
||||
return processGeneratedKey.extractData(generatedKeys);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw throwDataAccessException(sql, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transaction(Consumer<JdbcTemplate> consumer) {
|
||||
consumer.accept(this);
|
||||
}
|
||||
|
||||
private <T> ResultSetExtractor<List<T>> createResultSetExtractorList(final RowMapper<T> rowMapper) {
|
||||
return 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
10
src/main/java/ru/di9/jdbc/PreparedStatementProcessor.java
Normal file
10
src/main/java/ru/di9/jdbc/PreparedStatementProcessor.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package ru.di9.jdbc;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PreparedStatementProcessor {
|
||||
|
||||
void process(PreparedStatement ps) throws SQLException;
|
||||
}
|
||||
10
src/main/java/ru/di9/jdbc/ResultSetExtractor.java
Normal file
10
src/main/java/ru/di9/jdbc/ResultSetExtractor.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package ru.di9.jdbc;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ResultSetExtractor<T> {
|
||||
|
||||
T extractData(ResultSet rs) throws SQLException, DataAccessException;
|
||||
}
|
||||
10
src/main/java/ru/di9/jdbc/ReturnGeneratedKeyHelper.java
Normal file
10
src/main/java/ru/di9/jdbc/ReturnGeneratedKeyHelper.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package ru.di9.jdbc;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class ReturnGeneratedKeyHelper {
|
||||
public static final ResultSetExtractor<Long> RETURN_GENERATED_KEY_FIRST_LONG = rs -> rs.getLong(1);
|
||||
public static final ResultSetExtractor<Integer> RETURN_GENERATED_KEY_FIRST_INT = rs -> rs.getInt(1);
|
||||
}
|
||||
9
src/main/java/ru/di9/jdbc/RowMapper.java
Normal file
9
src/main/java/ru/di9/jdbc/RowMapper.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package ru.di9.jdbc;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public interface RowMapper<T> {
|
||||
|
||||
T mapRow(ResultSet rs, int rowNum) throws SQLException;
|
||||
}
|
||||
Reference in New Issue
Block a user