194 lines
6.9 KiB
Java
194 lines
6.9 KiB
Java
package com.andrewlalis.perfin.data.util;
|
|
|
|
import com.andrewlalis.perfin.data.pagination.Page;
|
|
import com.andrewlalis.perfin.data.pagination.PageRequest;
|
|
|
|
import java.sql.*;
|
|
import java.time.Clock;
|
|
import java.time.Instant;
|
|
import java.time.LocalDateTime;
|
|
import java.time.ZoneOffset;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
import java.util.function.Supplier;
|
|
|
|
public final class DbUtil {
|
|
private DbUtil() {}
|
|
|
|
public static void setArgs(PreparedStatement stmt, List<Object> args) {
|
|
for (int i = 0; i < args.size(); i++) {
|
|
try {
|
|
stmt.setObject(i + 1, args.get(i));
|
|
} catch (SQLException e) {
|
|
throw new UncheckedSqlException("Failed to set parameter " + (i + 1) + " to " + args.get(i), e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void setArgs(PreparedStatement stmt, Object... args) {
|
|
setArgs(stmt, List.of(args));
|
|
}
|
|
|
|
public static long getGeneratedId(PreparedStatement stmt) {
|
|
try (ResultSet rs = stmt.getGeneratedKeys()) {
|
|
if (!rs.next()) throw new SQLException("No generated keys available.");
|
|
return rs.getLong(1);
|
|
} catch (SQLException e) {
|
|
throw new UncheckedSqlException(e);
|
|
}
|
|
}
|
|
|
|
public static <T> List<T> findAll(Connection conn, String query, List<Object> args, ResultSetMapper<T> mapper) {
|
|
try (var stmt = conn.prepareStatement(query)) {
|
|
setArgs(stmt, args);
|
|
var rs = stmt.executeQuery();
|
|
List<T> results = new ArrayList<>();
|
|
while (rs.next()) {
|
|
results.add(mapper.map(rs));
|
|
}
|
|
return results;
|
|
} catch (SQLException e) {
|
|
throw new UncheckedSqlException(e);
|
|
}
|
|
}
|
|
|
|
public static <T> List<T> findAll(Connection conn, String query, ResultSetMapper<T> mapper) {
|
|
return findAll(conn, query, Collections.emptyList(), mapper);
|
|
}
|
|
|
|
public static <T> Page<T> findAll(Connection conn, String query, PageRequest pagination, List<Object> args, ResultSetMapper<T> mapper) {
|
|
List<T> items = findAll(conn, query + ' ' + pagination.toSQL(), args, mapper);
|
|
return new Page<>(items, pagination);
|
|
}
|
|
|
|
public static <T> Page<T> findAll(Connection conn, String query, PageRequest pagination, ResultSetMapper<T> mapper) {
|
|
return findAll(conn, query, pagination, Collections.emptyList(), mapper);
|
|
}
|
|
|
|
public static long count(Connection conn, String query, Object... args) {
|
|
try (var stmt = conn.prepareStatement(query)) {
|
|
setArgs(stmt, args);
|
|
var rs = stmt.executeQuery();
|
|
if (!rs.next()) throw new UncheckedSqlException("No count result available.");
|
|
return rs.getLong(1);
|
|
} catch (SQLException e) {
|
|
throw new UncheckedSqlException(e);
|
|
}
|
|
}
|
|
|
|
public static <T> Optional<T> findOne(Connection conn, String query, List<Object> args, ResultSetMapper<T> mapper) {
|
|
try (var stmt = conn.prepareStatement(query)) {
|
|
setArgs(stmt, args);
|
|
var rs = stmt.executeQuery();
|
|
if (!rs.next()) return Optional.empty();
|
|
return Optional.of(mapper.map(rs));
|
|
} catch (SQLException e) {
|
|
throw new UncheckedSqlException(e);
|
|
}
|
|
}
|
|
|
|
public static <T> Optional<T> findById(Connection conn, String query, long id, ResultSetMapper<T> mapper) {
|
|
return findOne(conn, query, List.of(id), mapper);
|
|
}
|
|
|
|
public static int update(Connection conn, String query, List<Object> args) {
|
|
try (var stmt = conn.prepareStatement(query)) {
|
|
setArgs(stmt, args);
|
|
return stmt.executeUpdate();
|
|
} catch (SQLException e) {
|
|
throw new UncheckedSqlException(e);
|
|
}
|
|
}
|
|
|
|
public static int update(Connection conn, String query, Object... args) {
|
|
return update(conn, query, List.of(args));
|
|
}
|
|
|
|
public static void updateOne(Connection conn, String query, List<Object> args) {
|
|
try (var stmt = conn.prepareStatement(query)) {
|
|
setArgs(stmt, args);
|
|
int updateCount = stmt.executeUpdate();
|
|
if (updateCount != 1) throw new UncheckedSqlException("Update count is " + updateCount + "; expected 1.");
|
|
} catch (SQLException e) {
|
|
throw new UncheckedSqlException(e);
|
|
}
|
|
}
|
|
|
|
public static void updateOne(Connection conn, String query, Object... args) {
|
|
updateOne(conn, query, List.of(args));
|
|
}
|
|
|
|
public static long insertOne(Connection conn, String query, List<Object> args) {
|
|
try (var stmt = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)) {
|
|
setArgs(stmt, args);
|
|
int result = stmt.executeUpdate();
|
|
if (result != 1) throw new UncheckedSqlException("Insert query did not update 1 row.");
|
|
return getGeneratedId(stmt);
|
|
} catch (SQLException e) {
|
|
throw new UncheckedSqlException(e);
|
|
}
|
|
}
|
|
|
|
public static long insertOne(Connection conn, String query, Object... args) {
|
|
return insertOne(conn, query, List.of(args));
|
|
}
|
|
|
|
public static Timestamp timestampFromUtcLDT(LocalDateTime utc) {
|
|
return Timestamp.from(utc.toInstant(ZoneOffset.UTC));
|
|
}
|
|
|
|
public static Timestamp timestampFromUtcNow() {
|
|
return Timestamp.from(Instant.now(Clock.systemUTC()));
|
|
}
|
|
|
|
public static Timestamp timestampFromInstant(Instant i) {
|
|
return Timestamp.from(i);
|
|
}
|
|
|
|
public static LocalDateTime utcLDTFromTimestamp(Timestamp ts) {
|
|
return ts.toInstant().atOffset(ZoneOffset.UTC).toLocalDateTime();
|
|
}
|
|
|
|
public static <T extends AutoCloseable> void useClosable(Supplier<T> supplier, ThrowableConsumer<T> consumer) {
|
|
try (T t = supplier.get()) {
|
|
consumer.accept(t);
|
|
} catch (Exception e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
public static <T> T doTransaction(Connection conn, SQLSupplier<T> supplier) {
|
|
try {
|
|
conn.setAutoCommit(false);
|
|
T result = supplier.offer();
|
|
conn.commit();
|
|
return result;
|
|
} catch (Exception e) {
|
|
try {
|
|
conn.rollback();
|
|
} catch (SQLException se) {
|
|
System.err.println("ERROR: Failed to rollback after a failed transaction!");
|
|
se.printStackTrace(System.err);
|
|
throw new UncheckedSqlException(se);
|
|
}
|
|
throw new RuntimeException(e);
|
|
} finally {
|
|
try {
|
|
conn.setAutoCommit(true);
|
|
} catch (SQLException e) {
|
|
System.err.println("ERROR: Failed to set auto-commit to true after transaction!");
|
|
e.printStackTrace(System.err);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void doTransaction(Connection conn, SQLRunnable runnable) {
|
|
doTransaction(conn, () -> {
|
|
runnable.run();
|
|
return null;
|
|
});
|
|
}
|
|
}
|