Skip to content

Instantly share code, notes, and snippets.

@mnadel
Last active May 18, 2018 13:43
Show Gist options
  • Save mnadel/10224102 to your computer and use it in GitHub Desktop.
Save mnadel/10224102 to your computer and use it in GitHub Desktop.
Dropwizard + Hystrix + Hibernate
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.Callable;
/**
* A database session and transaction-aware Callable
*/
class DatabaseCallable<T> implements Callable<T>
{
private static final Logger log = LoggerFactory.getLogger(DatabaseCallable.class);
private final Callable<T> underlying;
private final DatabaseContext ctx;
DatabaseCallable(final Runnable underlying, DatabaseContext ctx) {
this(new Callable<T>() {
@Override
public T call() throws Exception {
underlying.run();
return null;
}
}, ctx);
}
DatabaseCallable(Callable<T> underlying, DatabaseContext ctx) {
Validate.notNull(underlying);
Validate.notNull(ctx);
this.underlying = underlying;
this.ctx = ctx;
}
/**
* Invoke the underlying callable within the context of a Session and, if requested,
* within the context of a Transaction.
*/
@Override
public T call() throws Exception {
try {
ctx.bind();
ctx.beginIfNeeded();
T result = underlying.call();
ctx.commitIfNeeded();
return result;
} catch (Exception e) {
ctx.rollbackIfNeeded();
log.error("Error executing database work", e);
throw e;
} finally {
ctx.unbind();
}
}
}
import org.apache.commons.lang3.Validate;
import org.hibernate.*;
import org.hibernate.context.internal.ManagedSessionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class DatabaseContext
{
private static final Logger log = LoggerFactory.getLogger(DatabaseContext.class);
private Session session;
private Transaction transaction;
private final SessionFactory sessionFactory;
private final boolean needsTransaction;
DatabaseContext(SessionFactory sessionFactory, boolean needsTransaction) {
Validate.notNull(sessionFactory);
this.sessionFactory = sessionFactory;
this.needsTransaction = needsTransaction;
}
Session bind() {
if (log.isDebugEnabled()) {
log.debug("Opening session");
}
session = sessionFactory.openSession();
ManagedSessionContext.bind(session);
return session;
}
void unbind() {
try {
if (activeTransaction()) {
if (log.isDebugEnabled()) {
log.error("Rolling back abandoned transaction");
}
transaction.rollback();
}
if (log.isDebugEnabled()) {
log.debug("Unbinding session");
}
} catch (Exception e) {
log.error("Cannot roll back abandoned transaction", e);
} finally {
try {
session.close();
} catch (Exception e) {
log.error("Error closing session", e);
}
ManagedSessionContext.unbind(sessionFactory);
}
}
void beginIfNeeded() {
if (needsTransaction) {
if (log.isDebugEnabled()) {
log.debug("Starting transaction");
}
transaction = session.beginTransaction();
}
}
void commitIfNeeded() {
if (pendingTransaction()) {
if (log.isDebugEnabled()) {
log.debug("Committing transaction");
}
transaction.commit();
}
}
void rollbackIfNeeded() {
if (pendingTransaction()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back transaction");
}
transaction.rollback();
}
}
private boolean pendingTransaction() {
return null != transaction;
}
private boolean activeTransaction() {
return pendingTransaction() && transaction.isActive();
}
}
import com.netflix.hystrix.HystrixCommand;
import java.util.concurrent.Callable;
public abstract class HystrixCommandBuilder<T>
{
protected abstract Setter createSetter();
public HystrixCommand<T> build(Callable<T> action, DatabaseContext dbCtx) {
final Callable<T> actionDelegate = null != dbCtx
? new DatabaseCallable<T>(callable, dbCtx)
: callable;
return new HystrixCommand<T>(createSetter()) {
@Override
protected T run() throws Exception {
return actionDelegate.call();
}
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment