forked from Qortal/qortal
Merge pull request #3 from KaaCee/DAO
support for DB transactions (commit/rollback) in repository
This commit is contained in:
commit
6a2a172ee8
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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";
|
||||
|
||||
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);
|
||||
@ -70,8 +76,8 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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?
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user