Merge pull request #3 from KaaCee/DAO

support for DB transactions (commit/rollback) in repository
This commit is contained in:
catbref 2018-06-12 08:34:17 +01:00 committed by GitHub
commit 6a2a172ee8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 107 additions and 23 deletions

View File

@ -68,6 +68,10 @@ public abstract class DB {
return local.get();
}
public static Connection getPoolConnection() throws SQLException {
return connectionPool.getConnection();
}
public static void releaseConnection() {
Connection connection = local.get();
if (connection != null)
@ -179,7 +183,12 @@ public abstract class DB {
* @throws SQLException
*/
public static ResultSet checkedExecute(String sql, Object... objects) throws SQLException {
PreparedStatement preparedStatement = DB.getConnection().prepareStatement(sql);
Connection connection = DB.getConnection();
return checkedExecute(connection, sql, objects);
}
public static ResultSet checkedExecute(Connection connection, String sql, Object... objects) throws SQLException {
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < objects.length; ++i)
// Special treatment for BigDecimals so that they retain their "scale",

View File

@ -5,6 +5,10 @@ public abstract class Repository {
protected TransactionRepository transactionRepository;
protected BlockRepository blockRepository;
public abstract void saveChanges() throws DataException ;
public abstract void discardChanges() throws DataException ;
public abstract void close() throws DataException ;
public TransactionRepository getTransactionRepository() {
return this.transactionRepository;
}

View File

@ -23,9 +23,15 @@ public class HSQLDBBlockRepository implements BlockRepository {
private static final String BLOCK_DB_COLUMNS = "version, reference, transaction_count, total_fees, "
+ "transactions_signature, height, generation, generating_balance, generator, generator_signature, AT_data, AT_fees";
public BlockData fromSignature(byte[] signature) throws DataException {
protected HSQLDBRepository repository;
public HSQLDBBlockRepository(HSQLDBRepository repository) {
this.repository = repository;
}
public BlockData fromSignature(byte[] signature) throws DataException {
try {
ResultSet rs = DB.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE signature = ?", signature);
ResultSet rs = DB.checkedExecute(repository.connection, "SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE signature = ?", signature);
return getBlockFromResultSet(rs);
} catch (SQLException e) {
throw new DataException("Error loading data from DB", e);
@ -34,7 +40,7 @@ public class HSQLDBBlockRepository implements BlockRepository {
public BlockData fromReference(byte[] reference) throws DataException {
try {
ResultSet rs = DB.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE reference = ?", reference);
ResultSet rs = DB.checkedExecute(repository.connection, "SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE height = ?", height);
return getBlockFromResultSet(rs);
} catch (SQLException e) {
throw new DataException("Error loading data from DB", e);
@ -43,7 +49,7 @@ public class HSQLDBBlockRepository implements BlockRepository {
public BlockData fromHeight(int height) throws DataException {
try {
ResultSet rs = DB.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE height = ?", height);
ResultSet rs = DB.checkedExecute(repository.connection, "SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE height = ?", height);
return getBlockFromResultSet(rs);
} catch (SQLException e) {
throw new DataException("Error loading data from DB", e);
@ -67,11 +73,11 @@ public class HSQLDBBlockRepository implements BlockRepository {
byte[] generatorSignature = DB.getResultSetBytes(rs.getBinaryStream(10));
byte[] atBytes = DB.getResultSetBytes(rs.getBinaryStream(11));
BigDecimal atFees = rs.getBigDecimal(12);
return new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance, generatorPublicKey,
generatorSignature, atBytes, atFees);
} catch (SQLException e) {
throw new DataException(e);
} catch(SQLException e) {
throw new DataException("Error extracting data from result set", e);
}
}

View File

@ -11,9 +11,13 @@ import repository.DataException;
public class HSQLDBGenesisTransactionRepository extends HSQLDBTransactionRepository {
public HSQLDBGenesisTransactionRepository(HSQLDBRepository repository) {
super(repository);
}
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creator, long timestamp, BigDecimal fee) {
try {
ResultSet rs = DB.checkedExecute("SELECT recipient, amount FROM GenesisTransactions WHERE signature = ?", signature);
ResultSet rs = DB.checkedExecute(repository.connection, "SELECT recipient, amount FROM GenesisTransactions WHERE signature = ?", signature);
if (rs == null)
return null;

View File

@ -1,11 +1,65 @@
package repository.hsqldb;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.sql.Connection;
import java.sql.SQLException;
import database.DB;
import repository.DataException;
import repository.Repository;
public class HSQLDBRepository extends Repository {
public HSQLDBRepository() {
this.transactionRepository = new HSQLDBTransactionRepository();
Connection connection;
public HSQLDBRepository() throws DataException {
try {
initialize();
} catch (SQLException e) {
throw new DataException("initialization error", e);
}
this.transactionRepository = new HSQLDBTransactionRepository(this);
}
private void initialize() throws SQLException {
connection = DB.getPoolConnection();
// start transaction
connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
connection.setAutoCommit(false);
}
@Override
public void saveChanges() throws DataException {
try {
connection.commit();
} catch (SQLException e) {
throw new DataException("commit error", e);
}
}
@Override
public void discardChanges() throws DataException {
try {
connection.rollback();
} catch (SQLException e) {
throw new DataException("rollback error", e);
}
}
// TODO prevent leaking of connections if .close() is not called before garbage collection of the repository.
// Maybe use PhantomReference to call .close() on connection after repository destruction?
@Override
public void close() throws DataException {
try {
// give connection back to the pool
connection.close();
connection = null;
} catch (SQLException e) {
throw new DataException("close error", e);
}
}
}

View File

@ -1,6 +1,7 @@
package repository.hsqldb;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
@ -55,9 +56,13 @@ public class HSQLDBSaver {
* @throws SQLException
*/
public boolean execute() throws SQLException {
String sql = this.formatInsertWithPlaceholders();
Connection connection = DB.getConnection();
return execute(connection);
}
PreparedStatement preparedStatement = DB.getConnection().prepareStatement(sql);
public boolean execute(Connection connection) throws SQLException {
String sql = this.formatInsertWithPlaceholders();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
this.bindValues(preparedStatement);

View File

@ -1,6 +1,7 @@
package repository.hsqldb;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
@ -15,15 +16,17 @@ import repository.TransactionRepository;
public class HSQLDBTransactionRepository implements TransactionRepository {
protected HSQLDBRepository repository;
private HSQLDBGenesisTransactionRepository genesisTransactionRepository;
public HSQLDBTransactionRepository() {
genesisTransactionRepository = new HSQLDBGenesisTransactionRepository();
public HSQLDBTransactionRepository(HSQLDBRepository repository) {
this.repository = repository;
genesisTransactionRepository = new HSQLDBGenesisTransactionRepository(repository);
}
public TransactionData fromSignature(byte[] signature) {
try {
ResultSet rs = DB.checkedExecute("SELECT type, reference, creator, creation, fee FROM Transactions WHERE signature = ?", signature);
ResultSet rs = DB.checkedExecute(repository.connection, "SELECT type, reference, creator, creation, fee FROM Transactions WHERE signature = ?", signature);
if (rs == null)
return null;
@ -41,7 +44,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
public TransactionData fromReference(byte[] reference) {
try {
ResultSet rs = DB.checkedExecute("SELECT type, signature, creator, creation, fee FROM Transactions WHERE reference = ?", reference);
ResultSet rs = DB.checkedExecute(repository.connection, "SELECT type, signature, creator, creation, fee FROM Transactions WHERE reference = ?", reference);
if (rs == null)
return null;
@ -75,9 +78,8 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
// in one go?
try {
ResultSet rs = DB.checkedExecute(
"SELECT height from BlockTransactions JOIN Blocks ON Blocks.signature = BlockTransactions.block_signature WHERE transaction_signature = ? LIMIT 1",
signature);
ResultSet rs = DB.checkedExecute(repository.connection, "SELECT height from BlockTransactions JOIN Blocks ON Blocks.signature = BlockTransactions.block_signature WHERE transaction_signature = ? LIMIT 1", signature);
if (rs == null)
return 0;
@ -95,7 +97,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
// Fetch block signature (if any)
try {
ResultSet rs = DB.checkedExecute("SELECT block_signature from BlockTransactions WHERE transaction_signature = ? LIMIT 1", signature);
ResultSet rs = DB.checkedExecute(repository.connection, "SELECT block_signature from BlockTransactions WHERE transaction_signature = ? LIMIT 1", signature);
if (rs == null)
return null;
@ -114,7 +116,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
.bind("creator", transactionData.getCreatorPublicKey()).bind("creation", new Timestamp(transactionData.getTimestamp())).bind("fee", transactionData.getFee())
.bind("milestone_block", null);
try {
saver.execute();
saver.execute(repository.connection);
} catch (SQLException e) {
throw new DataException(e);
}
@ -125,7 +127,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
// NOTE: The corresponding row in sub-table is deleted automatically by the database thanks to "ON DELETE CASCADE" in the sub-table's FOREIGN KEY
// definition.
try {
DB.checkedExecute("DELETE FROM Transactions WHERE signature = ?", transactionData.getSignature());
DB.checkedExecute(repository.connection, "DELETE FROM Transactions WHERE signature = ?", transaction.getSignature());
} catch (SQLException e) {
// XXX do what?
}