Skip to content

Instantly share code, notes, and snippets.

@vbezhenar
Created January 21, 2016 13:58
Show Gist options
  • Save vbezhenar/5625ba87e5bb8f862668 to your computer and use it in GitHub Desktop.
Save vbezhenar/5625ba87e5bb8f862668 to your computer and use it in GitHub Desktop.
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import java.sql.SQLException;
import java.util.Random;
public class EntityManagerUtil {
@FunctionalInterface
public interface TransactionalAction {
void action(EntityManager entityManager) throws Exception;
}
@FunctionalInterface
public interface TransactionalFunction<T> {
T apply(EntityManager entityManager) throws Exception;
}
@FunctionalInterface
public interface RetryRequired {
boolean required(Exception e);
}
@FunctionalInterface
public interface RetryIntervalFunction {
int interval(int currentRetryNumber, int maxRetryCount);
}
public static final int DEFAULT_RETRY_COUNT = 10;
private static EntityManagerFactory entityManagerFactory;
public static void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
EntityManagerUtil.entityManagerFactory = entityManagerFactory;
}
public static <T> T txFunction(TransactionalFunction<T> function,
RetryRequired retryRequired,
int maxRetryCount, RetryIntervalFunction retryIntervalFunction) {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
int retryNumber = 0;
while (true) {
T result = null;
Exception exception = null;
try {
result = function.apply(entityManager);
} catch (Exception e) {
exception = e;
entityManager.getTransaction().setRollbackOnly();
}
try {
if (entityManager.getTransaction().getRollbackOnly()) {
entityManager.getTransaction().rollback();
} else {
entityManager.getTransaction().commit();
}
entityManager.close();
} catch (Exception e) {
if (exception != null) {
exception.addSuppressed(e);
} else {
exception = e;
}
}
if (exception != null) {
if (retryNumber >= maxRetryCount) {
throw runtimeException(exception);
}
if (retryRequired.required(exception)) {
try {
Thread.sleep(retryIntervalFunction.interval(retryNumber, maxRetryCount));
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
throw runtimeException(exception);
}
retryNumber++;
} else {
throw runtimeException(exception);
}
} else {
return result;
}
}
}
public static <T> T txFunction(TransactionalFunction<T> function) {
return txFunction(function,
EntityManagerUtil::defaultRetryRequired,
DEFAULT_RETRY_COUNT, EntityManagerUtil::defaultRetryInterval);
}
public static void txAction(TransactionalAction action) {
txFunction(new TransactionActionWrapper(action),
EntityManagerUtil::defaultRetryRequired,
DEFAULT_RETRY_COUNT, EntityManagerUtil::defaultRetryInterval);
}
private static RuntimeException runtimeException(Exception e) {
return e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e.getMessage(), e);
}
private static final Random DEFAULT_RETRY_INTERVAL_RANDOM = new Random();
public static int defaultRetryInterval(int currentRetryNumber, int maxRetryCount) {
return (maxRetryCount - currentRetryNumber) * 1000 + DEFAULT_RETRY_INTERVAL_RANDOM.nextInt(1000);
}
public static boolean defaultRetryRequired(Throwable exception) {
if (exception == null) {
return false;
}
if (exception instanceof SQLException) {
SQLException sqlException = (SQLException) exception;
if ("40P01".equals(sqlException.getSQLState())) {
return true;
}
}
if (defaultRetryRequired(exception.getCause())) {
return true;
}
for (Throwable suppressedException : exception.getSuppressed()) {
if (defaultRetryRequired(suppressedException)) {
return true;
}
}
return false;
}
public static class TransactionActionWrapper implements TransactionalFunction<Void> {
private final TransactionalAction action;
public TransactionActionWrapper(TransactionalAction action) {
this.action = action;
}
@Override
public Void apply(EntityManager entityManager) throws Exception {
action.action(entityManager);
return null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment