Skip to content

Commit

Permalink
runtime-core: Add transaction isolation level and refine API names
Browse files Browse the repository at this point in the history
  • Loading branch information
minborg committed Oct 30, 2017
1 parent e6c6ecf commit 2fd2861
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 16 deletions.
4 changes: 4 additions & 0 deletions pom.xml
Expand Up @@ -146,6 +146,10 @@
<name>Mark Schrijver (GitHub:ractoc)</name>
<timezone>Europe/Amsterdam</timezone>
</contributor>
<contributor>
<name>Ameya Ketkar (GitHub:ameyaKetkar)</name>
<timezone>America/Los_Angeles</timezone>
</contributor>
</contributors>

<!-- Distribution Details -->
Expand Down
@@ -1,6 +1,7 @@
package com.speedment.runtime.core.component.transaction;

import com.speedment.runtime.core.internal.component.transaction.DataSourceHandlerImpl;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

Expand All @@ -15,6 +16,15 @@ public interface DataSourceHandler<D, T> {

Function<? super D, ? extends T> extractor();

/**
* Returns a BiFunction that, when applied, sets a new Isolation level and
* then returns the previous Isolation level.
*
* @return a BiFunction that, when applied, sets a new Isolation level and
* then returns the previous Isolation level
*/
BiFunction<? super T, Isolation, Isolation> isolationConfigurator();

Consumer<? super T> beginner();

Consumer<? super T> rollbacker();
Expand All @@ -25,12 +35,13 @@ public interface DataSourceHandler<D, T> {

static <D, T> DataSourceHandler<D, T> of(
final Function<D, T> extractor,
final BiFunction<? super T, Isolation, Isolation> isolationConfigurator,
final Consumer<? super T> beginner,
final Consumer<? super T> committer,
final Consumer<? super T> rollbacker,
final Consumer<? super T> closer
) {
return new DataSourceHandlerImpl<>(extractor, beginner, committer, rollbacker, closer);
return new DataSourceHandlerImpl<>(extractor, isolationConfigurator, beginner, committer, rollbacker, closer);
}

}
@@ -0,0 +1,87 @@
package com.speedment.runtime.core.component.transaction;

import java.sql.Connection;

/**
*
* @author Per Minborg
*/
public enum Isolation {

// /**
// * An enum indicating that transactions are not supported.
// */
// NONE(Connection.TRANSACTION_NONE),
/**
* An Enum indicating that the default level of isolation for the
* transaction domain shall be used.
*/
DEFAULT(-1),
/**
* An Enum indicating that dirty reads, non-repeatable reads and phantom
* reads can occur. This level allows a row changed by one transaction to be
* read by another transaction before any changes in that row have been
* committed (a "dirty read"). If any of the changes are rolled back, the
* second transaction will have retrieved an invalid row.
*/
READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
/**
* An Enum indicating that dirty reads are prevented; non-repeatable reads
* and phantom reads can occur. This level only prohibits a transaction from
* reading a row with uncommitted changes in it.
*/
READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
/**
* An Enum indicating that dirty reads and non-repeatable reads are
* prevented; phantom reads can occur. This level prohibits a transaction
* from reading a row with uncommitted changes in it, and it also prohibits
* the situation where one transaction reads a row, a second transaction
* alters the row, and the first transaction rereads the row, getting
* different values the second time (a "non-repeatable read").
*/
REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
/**
* An Enum indicating that dirty reads, non-repeatable reads and phantom
* reads are prevented. This level includes the prohibitions in
* <code>TRANSACTION_REPEATABLE_READ</code> and further prohibits the
* situation where one transaction reads all rows that satisfy a
* <code>WHERE</code> condition, a second transaction inserts a row that
* satisfies that <code>WHERE</code> condition, and the first transaction
* rereads for the same condition, retrieving the additional "phantom" row
* in the second read.
*/
SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);

private final int sqlIsolationLevel;

private Isolation(int sqlIsolationlevel) {
this.sqlIsolationLevel = sqlIsolationlevel;
}

public int getSqlIsolationLevel() {
if (DEFAULT == this) {
throw new IllegalArgumentException("The DEFAULT isolation level does not have a hard coded value.");
}
return sqlIsolationLevel;
}

public static Isolation fromSqlIsolationLevel(int level) {
switch (level) {
case Connection.TRANSACTION_READ_UNCOMMITTED: {
return READ_UNCOMMITTED;
}
case Connection.TRANSACTION_READ_COMMITTED: {
return READ_COMMITTED;
}
case Connection.TRANSACTION_REPEATABLE_READ: {
return REPEATABLE_READ;
}
case Connection.TRANSACTION_SERIALIZABLE: {
return SERIALIZABLE;
}
default:
throw new IllegalArgumentException("No Isolation exists for " + level);
}
}

}
Expand Up @@ -9,8 +9,24 @@
*/
public interface Transaction {

/**
* Makes all changes made since the previous commit/rollback permanent and
* releases any transaction domain locks currently affected by this
* <code>Transaction</code> object.
*
* @throws TransactionException if an exception is thrown by the underlying
* transaction aware object (e.g. an SqlException is thrown)
*/
void commit() throws TransactionException;

/**
* Undoes all changes made in the current transaction and releases any
* transaction domain locks currently affected by this
* <code>Transaction</code> object.
*
* @throws TransactionException if an exception is thrown by the underlying
* transaction aware object (e.g. an SqlException is thrown)
*/
void rollback() throws TransactionException;

}
Expand Up @@ -20,7 +20,7 @@ public interface TransactionComponent {
* @throws IllegalStateException if there is not exactly one Dbms defined
* in the current project.
*/
TransactionHandler transactionHandler();
TransactionHandler createTransactionHandler();

/**
* Creates and returns a new TransactionHandler for the provided transaction
Expand All @@ -33,7 +33,7 @@ public interface TransactionComponent {
* determine that the provided data source can be used with transactions.
* @throws NullPointerException if the provided data source is null.
*/
<T> TransactionHandler transactionHandler(T dataSource); // <T extends TransactionCapable>
<T> TransactionHandler creaateTransactionHandler(T dataSource); // <T extends TransactionCapable>

/**
* Associates a certain data store class (e.g. Dbms) to some way of handling
Expand Down
Expand Up @@ -2,6 +2,7 @@

import com.speedment.runtime.core.exception.TransactionException;
import java.util.function.Consumer;
import java.util.function.Function;

/**
*
Expand All @@ -10,6 +11,100 @@
*/
public interface TransactionHandler {

void invoke(Consumer<Transaction> action) throws TransactionException;
/**
* Sets the {@link Isolation} level for subsequent transactions created by
* this transaction handler.
*
* @param level the new Isolation level to use
* @throws NullPointerException if the provided isolation level is null
*/
void setIsolation(Isolation level);

/**
* Returns the current isolation level used for new transactions.
*
* @return the current isolation level used for new transactions.
*/
Isolation getIsolation();

/**
* Creates a new {@link Transaction} and invokes the provided action with
* the new transaction.
* <p>
* If no TransactionException is thrown, the transaction will be
* automatically committed after the action has been invoked. Explicitly
* invoking {@link Transaction#rollback() } at the end of the action
* prevents anything from being automatically committed.
* <p>
* If a {@link TransactionException } is thrown, parts of the transaction
* that has not been previously explicitly committed using {@link Transaction#commit()
* } will be automatically rolled back.
* <p>
* NB: The transaction is only valid within the scope of the call and only
* for the current Thread.
* <p>
* EXAMPLE:
* <pre>
* {@code
* // Print out all films and languages in a sigle transaction
* txHandler.createAndAccept(tx -> {
* films.stream().forEach(System.out::println);
* languages.stream().forEach(System.out::println);
* });
* }
* </pre>
*
* @param action to be performed on the new transaction
* @throws TransactionException if the action throws an exception
* @throws NullPointerException if the provided mapper is null
*/
default void createAndAccept(Consumer<? super Transaction> action) throws TransactionException {
createAndApply(tx -> {
action.accept(tx);
return null;
});
}

/**
* Creates a new {@link Transaction } and returns the value of applying the
* given function to the new transaction.
* <p>
* If no {@link TransactionException } is thrown, the transaction will be
* automatically committed after the function has been applied. Explicitly
* invoking {@link Transaction#rollback() } at the end of the function
* prevents anything from being automatically committed.
* <p>
* If a {@link TransactionException } is thrown, parts of the transaction
* that has not been previously explicitly committed using {@link Transaction#commit()
* } will be automatically rolled back.
* <p>
* NB: The transaction is only valid within the scope of the call and only
* for the current Thread. EXAMPLE:
* <pre>
* {@code
* // Retrieve a list of all films in the English language
* // in a sigle transaction.
* List<Film> filmsInEnglish = txHandler.createAndAccept(tx ->
* languages.stream()
* .filter(Language.NAME.equal("English"))
* .flatMap(films.finderBackwardsBy(Film.LANGUAGE_ID))
* .collect(Collectors.toList())
* );
* }
* </pre>
*
* @param <R> Return type of the provided mapper
* @param mapper to apply on the new transaction
* @return the result of the mapper
* @throws TransactionException if the action throws an exception
* @throws NullPointerException if the provided mapper is null
*/
<R> R createAndApply(Function<? super Transaction, ? extends R> mapper) throws TransactionException;

// /**
// * Creates and returns a new transaction.
// *
// * @return
// */
// Transaction create();
}
@@ -1,7 +1,9 @@
package com.speedment.runtime.core.internal.component.transaction;

import com.speedment.runtime.core.component.transaction.DataSourceHandler;
import com.speedment.runtime.core.component.transaction.Isolation;
import static java.util.Objects.requireNonNull;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

Expand All @@ -12,19 +14,22 @@
public class DataSourceHandlerImpl<D, T> implements DataSourceHandler<D, T> {

private final Function<? super D, ? extends T> extractor;
private final BiFunction<? super T, Isolation, Isolation> isolationConfigurator;
private final Consumer<? super T> beginner;
private final Consumer<? super T> committer;
private final Consumer<? super T> rollbacker;
private final Consumer<? super T> closer;

public DataSourceHandlerImpl(
final Function<? super D, ? extends T> extractor,
final BiFunction<? super T, Isolation, Isolation> isolationConfigurator,
final Consumer<? super T> beginner,
final Consumer<? super T> committer,
final Consumer<? super T> rollbacker,
final Consumer<? super T> closer
) {
this.extractor = requireNonNull(extractor);
this.isolationConfigurator = requireNonNull(isolationConfigurator);
this.beginner = requireNonNull(beginner);
this.committer = requireNonNull(committer);
this.rollbacker = requireNonNull(rollbacker);
Expand All @@ -36,6 +41,11 @@ public DataSourceHandlerImpl(
return extractor;
}

@Override
public BiFunction<? super T, Isolation, Isolation> isolationConfigurator() {
return isolationConfigurator;
}

@Override
public Consumer<? super T> beginner() {
return beginner;
Expand Down

0 comments on commit 2fd2861

Please sign in to comment.