Skip to content

Commit

Permalink
#467 apply unified style builders for all JDBC integrations
Browse files Browse the repository at this point in the history
  • Loading branch information
Vladimir Buhtoyarov committed Mar 25, 2024
1 parent bc5023a commit ea1745a
Show file tree
Hide file tree
Showing 12 changed files with 247 additions and 92 deletions.
Expand Up @@ -29,6 +29,7 @@
* @author Maxim Bartkov
* The class to build {@link SQLProxyConfiguration}
*/
@Deprecated
public final class SQLProxyConfigurationBuilder<K> {
ClientSideConfig clientSideConfig;
BucketTableSettings tableSettings;
Expand Down
Expand Up @@ -6,7 +6,6 @@

import io.github.bucket4j.distributed.jdbc.AbstractJdbcProxyManagerBuilder;
import io.github.bucket4j.distributed.jdbc.PrimaryKeyMapper;
import io.github.bucket4j.distributed.jdbc.SQLProxyConfigurationBuilder;

/**
* Entry point for MariaDB integration
Expand Down Expand Up @@ -40,7 +39,7 @@ public MariaDBSelectForUpdateBasedProxyManager<K> build() {
*
* @param primaryKeyMapper object responsible for setting primary key value in prepared statement.
*
* @return {@link SQLProxyConfigurationBuilder}
* @return this builder instance
*/
public <K2> MariaDBSelectForUpdateBasedProxyManagerBuilder<K2> primaryKeyMapper(PrimaryKeyMapper<K2> primaryKeyMapper) {
super.primaryKeyMapper = (PrimaryKeyMapper) Objects.requireNonNull(primaryKeyMapper);
Expand Down
Expand Up @@ -22,7 +22,6 @@
import io.github.bucket4j.BucketExceptions;
import io.github.bucket4j.distributed.jdbc.PrimaryKeyMapper;
import io.github.bucket4j.distributed.jdbc.SQLProxyConfiguration;
import io.github.bucket4j.distributed.jdbc.SQLProxyConfigurationBuilder;
import io.github.bucket4j.distributed.proxy.generic.select_for_update.AbstractSelectForUpdateBasedProxyManager;
import io.github.bucket4j.distributed.proxy.generic.select_for_update.LockAndGetResult;
import io.github.bucket4j.distributed.proxy.generic.select_for_update.SelectForUpdateBasedTransaction;
Expand All @@ -40,13 +39,9 @@
import java.util.Optional;

/**
* @author Maxim Bartkov
* The extension of Bucket4j library addressed to support <a href="https://www.mysql.com">MySQL</a>
* To start work with the MariaDB extension you must create a table, which will include the possibility to work with buckets
* In order to do this, your table should include the next columns: id as a PRIMARY KEY (BIGINT) and state (BLOB)
* To define column names, {@link SQLProxyConfiguration} include {@link io.github.bucket4j.distributed.jdbc.BucketTableSettings} which takes settings for the table to work with Bucket4j
* The extension of Bucket4j library addressed to support <a href="https://mariadb.org/">MariaDB</a>
*
* @see {@link SQLProxyConfigurationBuilder} to get more information how to build {@link SQLProxyConfiguration}
* <p>This implementation solves transaction/concurrency related problems via "SELECT FOR UPDATE" SQL syntax.
*
* @param <K> type of primary key
*/
Expand All @@ -71,9 +66,9 @@ public MariaDBSelectForUpdateBasedProxyManager(MariaDBSelectForUpdateBasedProxyM
}

/**
*
* @param configuration {@link SQLProxyConfiguration} configuration.
* @deprecated use {@link Bucket4jMariaDB#selectForUpdateBasedBuilder(DataSource)} instead
*/
@Deprecated
public MariaDBSelectForUpdateBasedProxyManager(SQLProxyConfiguration<K> configuration) {
super(configuration.getClientSideConfig());
this.dataSource = Objects.requireNonNull(configuration.getDataSource());
Expand Down
@@ -0,0 +1,50 @@
package io.github.bucket4j.mssql;

import java.util.Objects;

import javax.sql.DataSource;

import io.github.bucket4j.distributed.jdbc.AbstractJdbcProxyManagerBuilder;
import io.github.bucket4j.distributed.jdbc.PrimaryKeyMapper;

/**
* Entry point for MSSQL integration
*/
public class Bucket4jMSSQL {

/**
* Returns the builder for {@link MSSQLSelectForUpdateBasedProxyManager}
*
* @param dataSource
*
* @return new instance of {@link MSSQLSelectForUpdateBasedProxyManager}
*/
public static MSSQLSelectForUpdateBasedProxyManagerBuilder<Long> selectForUpdateBasedBuilder(DataSource dataSource) {
return new MSSQLSelectForUpdateBasedProxyManagerBuilder<>(dataSource, PrimaryKeyMapper.LONG);
}

public static class MSSQLSelectForUpdateBasedProxyManagerBuilder<K> extends AbstractJdbcProxyManagerBuilder<K, MSSQLSelectForUpdateBasedProxyManager<K>, MSSQLSelectForUpdateBasedProxyManagerBuilder<K>> {

public MSSQLSelectForUpdateBasedProxyManagerBuilder(DataSource dataSource, PrimaryKeyMapper<K> primaryKeyMapper) {
super(dataSource, primaryKeyMapper);
}

@Override
public MSSQLSelectForUpdateBasedProxyManager<K> build() {
return new MSSQLSelectForUpdateBasedProxyManager<>(this);
}

/**
* Specifies the type of primary key.
*
* @param primaryKeyMapper object responsible for setting primary key value in prepared statement.
*
* @return this builder instance
*/
public <K2> MSSQLSelectForUpdateBasedProxyManagerBuilder<K2> primaryKeyMapper(PrimaryKeyMapper<K2> primaryKeyMapper) {
super.primaryKeyMapper = (PrimaryKeyMapper) Objects.requireNonNull(primaryKeyMapper);
return (MSSQLSelectForUpdateBasedProxyManagerBuilder<K2>) this;
}
}

}
Expand Up @@ -20,8 +20,8 @@
package io.github.bucket4j.mssql;

import io.github.bucket4j.BucketExceptions;
import io.github.bucket4j.distributed.jdbc.PrimaryKeyMapper;
import io.github.bucket4j.distributed.jdbc.SQLProxyConfiguration;
import io.github.bucket4j.distributed.proxy.ClientSideConfig;
import io.github.bucket4j.distributed.proxy.generic.select_for_update.AbstractSelectForUpdateBasedProxyManager;
import io.github.bucket4j.distributed.proxy.generic.select_for_update.LockAndGetResult;
import io.github.bucket4j.distributed.proxy.generic.select_for_update.SelectForUpdateBasedTransaction;
Expand All @@ -37,33 +37,48 @@
import java.util.Optional;

/**
* @author Vladimir Bukhtoyarov
* The extension of Bucket4j library addressed to support "Microsoft SQL Server"
*
* <p>This implementation solves transaction/concurrency related problems via "SELECT WITH(ROWLOCK, UPDLOCK)"
* which can be considered as comparable equivalent of "SELECT FOR UPDATE" from SQL Standard syntax.
*
* @param <K> type of primary key
*/
public class MSSQLSelectForUpdateBasedProxyManager<K> extends AbstractSelectForUpdateBasedProxyManager<K> {

private final DataSource dataSource;
private final SQLProxyConfiguration<K> configuration;
private final PrimaryKeyMapper<K> primaryKeyMapper;
private final String removeSqlQuery;
private final String updateSqlQuery;
private final String insertSqlQuery;
private final String selectSqlQuery;

MSSQLSelectForUpdateBasedProxyManager(Bucket4jMSSQL.MSSQLSelectForUpdateBasedProxyManagerBuilder<K> builder) {
super(builder.getClientSideConfig());
this.dataSource = builder.getDataSource();
this.primaryKeyMapper = builder.getPrimaryKeyMapper();
this.removeSqlQuery = MessageFormat.format("DELETE FROM {0} WHERE {1} = ?", builder.getTableName(), builder.getIdColumnName());
this.updateSqlQuery = MessageFormat.format("UPDATE {0} SET {1}=? WHERE {2}=?", builder.getTableName(), builder.getStateColumnName(), builder.getIdColumnName());
this.insertSqlQuery = MessageFormat.format(
"INSERT INTO {0}({1},{2}) VALUES(?, null)",
builder.getTableName(), builder.getIdColumnName(), builder.getStateColumnName());
this.selectSqlQuery = MessageFormat.format("SELECT {0} as state FROM {1} WITH(ROWLOCK, UPDLOCK) WHERE {2} = ?", builder.getStateColumnName(), builder.getTableName(), builder.getIdColumnName());
}

/**
*
* @param configuration {@link SQLProxyConfiguration} configuration.
* @deprecated use {@link Bucket4jMSSQL#selectForUpdateBasedBuilder(DataSource)} instead
*/
@Deprecated
public MSSQLSelectForUpdateBasedProxyManager(SQLProxyConfiguration<K> configuration) {
super(configuration.getClientSideConfig());
this.dataSource = Objects.requireNonNull(configuration.getDataSource());
this.configuration = configuration;
this.primaryKeyMapper = configuration.getPrimaryKeyMapper();
this.removeSqlQuery = MessageFormat.format("DELETE FROM {0} WHERE {1} = ?", configuration.getTableName(), configuration.getIdName());
this.updateSqlQuery = MessageFormat.format("UPDATE {0} SET {1}=? WHERE {2}=?", configuration.getTableName(), configuration.getStateName(), configuration.getIdName());
this.insertSqlQuery = MessageFormat.format(
"INSERT INTO {0}({1},{2}) VALUES(?, null)",
configuration.getTableName(), configuration.getIdName(), configuration.getStateName());
this.selectSqlQuery = MessageFormat.format("SELECT {0} FROM {1} WITH(ROWLOCK, UPDLOCK) WHERE {2} = ?", configuration.getStateName(), configuration.getTableName(), configuration.getIdName());
this.selectSqlQuery = MessageFormat.format("SELECT {0} as state FROM {1} WITH(ROWLOCK, UPDLOCK) WHERE {2} = ?", configuration.getStateName(), configuration.getTableName(), configuration.getIdName());
}

@Override
Expand Down Expand Up @@ -107,10 +122,10 @@ public void commit(Optional<Long> requestTimeoutNanos) {
public LockAndGetResult tryLockAndGet(Optional<Long> requestTimeoutNanos) {
try (PreparedStatement selectStatement = connection.prepareStatement(selectSqlQuery)) {
applyTimeout(selectStatement, requestTimeoutNanos);
configuration.getPrimaryKeyMapper().set(selectStatement, 1, key);
primaryKeyMapper.set(selectStatement, 1, key);
try (ResultSet rs = selectStatement.executeQuery()) {
if (rs.next()) {
byte[] data = rs.getBytes(configuration.getStateName());
byte[] data = rs.getBytes("state");
return LockAndGetResult.locked(data);
} else {
return LockAndGetResult.notLocked();
Expand All @@ -125,7 +140,7 @@ public LockAndGetResult tryLockAndGet(Optional<Long> requestTimeoutNanos) {
public boolean tryInsertEmptyData(Optional<Long> requestTimeoutNanos) {
try (PreparedStatement insertStatement = connection.prepareStatement(insertSqlQuery)) {
applyTimeout(insertStatement, requestTimeoutNanos);
configuration.getPrimaryKeyMapper().set(insertStatement, 1, key);
primaryKeyMapper.set(insertStatement, 1, key);
return insertStatement.executeUpdate() > 0;
} catch (SQLException e) {
if (e.getErrorCode() == 1205) {
Expand All @@ -147,7 +162,7 @@ public void update(byte[] data, RemoteBucketState newState, Optional<Long> reque
try (PreparedStatement updateStatement = connection.prepareStatement(updateSqlQuery)) {
applyTimeout(updateStatement, requestTimeoutNanos);
updateStatement.setBytes(1, data);
configuration.getPrimaryKeyMapper().set(updateStatement, 2, key);
primaryKeyMapper.set(updateStatement, 2, key);
updateStatement.executeUpdate();
}
} catch (SQLException e) {
Expand All @@ -172,7 +187,7 @@ public void release() {
public void removeProxy(K key) {
try (Connection connection = dataSource.getConnection()) {
try(PreparedStatement removeStatement = connection.prepareStatement(removeSqlQuery)) {
configuration.getPrimaryKeyMapper().set(removeStatement, 1, key);
primaryKeyMapper.set(removeStatement, 1, key);
removeStatement.executeUpdate();
}
} catch (SQLException e) {
Expand Down
@@ -0,0 +1,50 @@
package io.github.bucket4j.mysql;

import java.util.Objects;

import javax.sql.DataSource;

import io.github.bucket4j.distributed.jdbc.AbstractJdbcProxyManagerBuilder;
import io.github.bucket4j.distributed.jdbc.PrimaryKeyMapper;

/**
* Entry point for MySQL integration
*/
public class Bucket4jMySQL {

/**
* Returns the builder for {@link MySQLSelectForUpdateBasedProxyManager}
*
* @param dataSource
*
* @return new instance of {@link MySQLSelectForUpdateBasedProxyManager}
*/
public static MySQLSelectForUpdateBasedProxyManagerBuilder<Long> selectForUpdateBasedBuilder(DataSource dataSource) {
return new MySQLSelectForUpdateBasedProxyManagerBuilder<>(dataSource, PrimaryKeyMapper.LONG);
}

public static class MySQLSelectForUpdateBasedProxyManagerBuilder<K> extends AbstractJdbcProxyManagerBuilder<K, MySQLSelectForUpdateBasedProxyManager<K>, MySQLSelectForUpdateBasedProxyManagerBuilder<K>> {

public MySQLSelectForUpdateBasedProxyManagerBuilder(DataSource dataSource, PrimaryKeyMapper<K> primaryKeyMapper) {
super(dataSource, primaryKeyMapper);
}

@Override
public MySQLSelectForUpdateBasedProxyManager<K> build() {
return new MySQLSelectForUpdateBasedProxyManager<>(this);
}

/**
* Specifies the type of primary key.
*
* @param primaryKeyMapper object responsible for setting primary key value in prepared statement.
*
* @return this builder instance
*/
public <K2> MySQLSelectForUpdateBasedProxyManagerBuilder<K2> primaryKeyMapper(PrimaryKeyMapper<K2> primaryKeyMapper) {
super.primaryKeyMapper = (PrimaryKeyMapper) Objects.requireNonNull(primaryKeyMapper);
return (MySQLSelectForUpdateBasedProxyManagerBuilder<K2>) this;
}
}

}

0 comments on commit ea1745a

Please sign in to comment.