unit of work refactoring

This commit is contained in:
Athou
2014-12-12 08:57:50 +01:00
parent ccf18758fb
commit 2e475c35cc
7 changed files with 67 additions and 124 deletions

View File

@@ -5,17 +5,26 @@ import org.hibernate.SessionFactory;
import org.hibernate.Transaction; import org.hibernate.Transaction;
import org.hibernate.context.internal.ManagedSessionContext; import org.hibernate.context.internal.ManagedSessionContext;
public abstract class UnitOfWork<T> { public class UnitOfWork {
private SessionFactory sessionFactory; @FunctionalInterface
public static interface SessionRunner {
public UnitOfWork(SessionFactory sessionFactory) { public void runInSession();
this.sessionFactory = sessionFactory;
} }
protected abstract T runInSession() throws Exception; @FunctionalInterface
public static interface SessionRunnerReturningValue<T> {
public T runInSession();
}
public T run() { public static void run(SessionFactory sessionFactory, SessionRunner sessionRunner) {
run(sessionFactory, () -> {
sessionRunner.runInSession();
return null;
});
}
public static <T> T run(SessionFactory sessionFactory, SessionRunnerReturningValue<T> sessionRunner) {
final Session session = sessionFactory.openSession(); final Session session = sessionFactory.openSession();
if (ManagedSessionContext.hasBind(sessionFactory)) { if (ManagedSessionContext.hasBind(sessionFactory)) {
throw new IllegalStateException("Already in a unit of work!"); throw new IllegalStateException("Already in a unit of work!");
@@ -25,11 +34,11 @@ public abstract class UnitOfWork<T> {
ManagedSessionContext.bind(session); ManagedSessionContext.bind(session);
session.beginTransaction(); session.beginTransaction();
try { try {
t = runInSession(); t = sessionRunner.runInSession();
commitTransaction(session); commitTransaction(session);
} catch (Exception e) { } catch (Exception e) {
rollbackTransaction(session); rollbackTransaction(session);
this.<RuntimeException> rethrow(e); UnitOfWork.<RuntimeException> rethrow(e);
} }
} finally { } finally {
session.close(); session.close();
@@ -38,14 +47,14 @@ public abstract class UnitOfWork<T> {
return t; return t;
} }
private void rollbackTransaction(Session session) { private static void rollbackTransaction(Session session) {
final Transaction txn = session.getTransaction(); final Transaction txn = session.getTransaction();
if (txn != null && txn.isActive()) { if (txn != null && txn.isActive()) {
txn.rollback(); txn.rollback();
} }
} }
private void commitTransaction(Session session) { private static void commitTransaction(Session session) {
final Transaction txn = session.getTransaction(); final Transaction txn = session.getTransaction();
if (txn != null && txn.isActive()) { if (txn != null && txn.isActive()) {
txn.commit(); txn.commit();
@@ -53,7 +62,7 @@ public abstract class UnitOfWork<T> {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private <E extends Exception> void rethrow(Exception e) throws E { private static <E extends Exception> void rethrow(Exception e) throws E {
throw (E) e; throw (E) e;
} }

View File

@@ -68,12 +68,7 @@ public class FeedRefreshTaskGiver implements Managed {
public void run() { public void run() {
while (!executor.isShutdown()) { while (!executor.isShutdown()) {
try { try {
FeedRefreshContext context = new UnitOfWork<FeedRefreshContext>(sessionFactory) { FeedRefreshContext context = UnitOfWork.run(sessionFactory, () -> queues.take());
@Override
protected FeedRefreshContext runInSession() throws Exception {
return queues.take();
}
}.run();
if (context != null) { if (context != null) {
feedRefreshed.mark(); feedRefreshed.mark();
worker.updateFeed(context); worker.updateFeed(context);

View File

@@ -120,12 +120,7 @@ public class FeedRefreshUpdater implements Managed {
if (!lastEntries.contains(cacheKey)) { if (!lastEntries.contains(cacheKey)) {
log.debug("cache miss for {}", entry.getUrl()); log.debug("cache miss for {}", entry.getUrl());
if (subscriptions == null) { if (subscriptions == null) {
subscriptions = new UnitOfWork<List<FeedSubscription>>(sessionFactory) { subscriptions = UnitOfWork.run(sessionFactory, () -> feedSubscriptionDAO.findByFeed(feed));
@Override
protected List<FeedSubscription> runInSession() throws Exception {
return feedSubscriptionDAO.findByFeed(feed);
}
}.run();
} }
ok &= addEntry(feed, entry, subscriptions); ok &= addEntry(feed, entry, subscriptions);
entryCacheMiss.mark(); entryCacheMiss.mark();
@@ -190,12 +185,7 @@ public class FeedRefreshUpdater implements Managed {
locked1 = lock1.tryLock(1, TimeUnit.MINUTES); locked1 = lock1.tryLock(1, TimeUnit.MINUTES);
locked2 = lock2.tryLock(1, TimeUnit.MINUTES); locked2 = lock2.tryLock(1, TimeUnit.MINUTES);
if (locked1 && locked2) { if (locked1 && locked2) {
boolean inserted = new UnitOfWork<Boolean>(sessionFactory) { boolean inserted = UnitOfWork.run(sessionFactory, () -> feedUpdateService.addEntry(feed, entry, subscriptions));
@Override
protected Boolean runInSession() throws Exception {
return feedUpdateService.addEntry(feed, entry, subscriptions);
}
}.run();
if (inserted) { if (inserted) {
entryInserted.mark(); entryInserted.mark();
} }

View File

@@ -17,8 +17,6 @@ import com.commafeed.backend.dao.FeedEntryDAO;
import com.commafeed.backend.dao.FeedEntryDAO.FeedCapacity; import com.commafeed.backend.dao.FeedEntryDAO.FeedCapacity;
import com.commafeed.backend.dao.FeedEntryStatusDAO; import com.commafeed.backend.dao.FeedEntryStatusDAO;
import com.commafeed.backend.dao.UnitOfWork; import com.commafeed.backend.dao.UnitOfWork;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedEntryStatus;
/** /**
* Contains utility methods for cleaning the database * Contains utility methods for cleaning the database
@@ -42,13 +40,7 @@ public class DatabaseCleaningService {
long total = 0; long total = 0;
int deleted = 0; int deleted = 0;
do { do {
deleted = new UnitOfWork<Integer>(sessionFactory) { deleted = UnitOfWork.run(sessionFactory, () -> feedDAO.delete(feedDAO.findWithoutSubscriptions(1)));
@Override
protected Integer runInSession() throws Exception {
List<Feed> feeds = feedDAO.findWithoutSubscriptions(1);
return feedDAO.delete(feeds);
};
}.run();
total += deleted; total += deleted;
log.info("removed {} feeds without subscriptions", total); log.info("removed {} feeds without subscriptions", total);
} while (deleted != 0); } while (deleted != 0);
@@ -61,12 +53,7 @@ public class DatabaseCleaningService {
long total = 0; long total = 0;
int deleted = 0; int deleted = 0;
do { do {
deleted = new UnitOfWork<Integer>(sessionFactory) { deleted = UnitOfWork.run(sessionFactory, () -> feedEntryContentDAO.deleteWithoutEntries(BATCH_SIZE));
@Override
protected Integer runInSession() throws Exception {
return feedEntryContentDAO.deleteWithoutEntries(BATCH_SIZE);
}
}.run();
total += deleted; total += deleted;
log.info("removed {} contents without entries", total); log.info("removed {} contents without entries", total);
} while (deleted != 0); } while (deleted != 0);
@@ -77,13 +64,8 @@ public class DatabaseCleaningService {
public long cleanEntriesForFeedsExceedingCapacity(final int maxFeedCapacity) { public long cleanEntriesForFeedsExceedingCapacity(final int maxFeedCapacity) {
long total = 0; long total = 0;
while (true) { while (true) {
List<FeedCapacity> feeds = new UnitOfWork<List<FeedCapacity>>(sessionFactory) { List<FeedCapacity> feeds = UnitOfWork.run(sessionFactory,
@Override () -> feedEntryDAO.findFeedsExceedingCapacity(maxFeedCapacity, BATCH_SIZE));
protected List<FeedCapacity> runInSession() throws Exception {
return feedEntryDAO.findFeedsExceedingCapacity(maxFeedCapacity, BATCH_SIZE);
}
}.run();
if (feeds.isEmpty()) { if (feeds.isEmpty()) {
break; break;
} }
@@ -92,12 +74,8 @@ public class DatabaseCleaningService {
long remaining = feed.getCapacity() - maxFeedCapacity; long remaining = feed.getCapacity() - maxFeedCapacity;
do { do {
final long rem = remaining; final long rem = remaining;
int deleted = new UnitOfWork<Integer>(sessionFactory) { int deleted = UnitOfWork.run(sessionFactory,
@Override () -> feedEntryDAO.deleteOldEntries(feed.getId(), Math.min(BATCH_SIZE, rem)));
protected Integer runInSession() throws Exception {
return feedEntryDAO.deleteOldEntries(feed.getId(), Math.min(BATCH_SIZE, rem));
};
}.run();
total += deleted; total += deleted;
remaining -= deleted; remaining -= deleted;
log.info("removed {} entries for feeds exceeding capacity", total); log.info("removed {} entries for feeds exceeding capacity", total);
@@ -113,13 +91,8 @@ public class DatabaseCleaningService {
long total = 0; long total = 0;
int deleted = 0; int deleted = 0;
do { do {
deleted = new UnitOfWork<Integer>(sessionFactory) { deleted = UnitOfWork.run(sessionFactory,
@Override () -> feedEntryStatusDAO.delete(feedEntryStatusDAO.getOldStatuses(olderThan, BATCH_SIZE)));
protected Integer runInSession() throws Exception {
List<FeedEntryStatus> list = feedEntryStatusDAO.getOldStatuses(olderThan, BATCH_SIZE);
return feedEntryStatusDAO.delete(list);
}
}.run();
total += deleted; total += deleted;
log.info("cleaned {} old read statuses", total); log.info("cleaned {} old read statuses", total);
} while (deleted != 0); } while (deleted != 0);

View File

@@ -44,15 +44,10 @@ public class StartupService implements Managed {
@Override @Override
public void start() throws Exception { public void start() throws Exception {
updateSchema(); updateSchema();
new UnitOfWork<Void>(sessionFactory) { long count = UnitOfWork.run(sessionFactory, () -> userDAO.count());
@Override if (count == 0) {
protected Void runInSession() throws Exception { UnitOfWork.run(sessionFactory, () -> initialData());
if (userDAO.count() == 0) { }
initialData();
}
return null;
}
}.run();
} }
private void updateSchema() { private void updateSchema() {

View File

@@ -32,23 +32,12 @@ public class CustomCssServlet extends HttpServlet {
protected void doGet(final HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { protected void doGet(final HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/css"); resp.setContentType("text/css");
final Optional<User> user = new UnitOfWork<Optional<User>>(sessionFactory) { final Optional<User> user = UnitOfWork.run(sessionFactory, () -> new SessionHelper(req).getLoggedInUser());
@Override
protected Optional<User> runInSession() throws Exception {
return new SessionHelper(req).getLoggedInUser();
}
}.run();
if (!user.isPresent()) { if (!user.isPresent()) {
return; return;
} }
UserSettings settings = new UnitOfWork<UserSettings>(sessionFactory) { UserSettings settings = UnitOfWork.run(sessionFactory, () -> userSettingsDAO.findByUser(user.get()));
@Override
protected UserSettings runInSession() {
return userSettingsDAO.findByUser(user.get());
}
}.run();
if (settings == null || settings.getCustomCss() == null) { if (settings == null || settings.getCustomCss() == null) {
return; return;
} }

View File

@@ -51,17 +51,11 @@ public class NextUnreadServlet extends HttpServlet {
final String categoryId = req.getParameter(PARAM_CATEGORYID); final String categoryId = req.getParameter(PARAM_CATEGORYID);
String orderParam = req.getParameter(PARAM_READINGORDER); String orderParam = req.getParameter(PARAM_READINGORDER);
final Optional<User> user = new UnitOfWork<Optional<User>>(sessionFactory) { SessionHelper sessionHelper = new SessionHelper(req);
@Override Optional<User> user = sessionHelper.getLoggedInUser();
protected Optional<User> runInSession() throws Exception { if (user.isPresent()) {
SessionHelper sessionHelper = new SessionHelper(req); UnitOfWork.run(sessionFactory, () -> userService.performPostLoginActivities(user.get()));
Optional<User> loggedInUser = sessionHelper.getLoggedInUser(); }
if (loggedInUser.isPresent()) {
userService.performPostLoginActivities(loggedInUser.get());
}
return loggedInUser;
}
}.run();
if (!user.isPresent()) { if (!user.isPresent()) {
resp.sendRedirect(resp.encodeRedirectURL(config.getApplicationSettings().getPublicUrl())); resp.sendRedirect(resp.encodeRedirectURL(config.getApplicationSettings().getPublicUrl()));
return; return;
@@ -69,32 +63,31 @@ public class NextUnreadServlet extends HttpServlet {
final ReadingOrder order = StringUtils.equals(orderParam, "asc") ? ReadingOrder.asc : ReadingOrder.desc; final ReadingOrder order = StringUtils.equals(orderParam, "asc") ? ReadingOrder.asc : ReadingOrder.desc;
FeedEntryStatus status = new UnitOfWork<FeedEntryStatus>(sessionFactory) { FeedEntryStatus status = UnitOfWork.run(
@Override sessionFactory,
protected FeedEntryStatus runInSession() throws Exception { () -> {
FeedEntryStatus status = null; FeedEntryStatus s = null;
if (StringUtils.isBlank(categoryId) || CategoryREST.ALL.equals(categoryId)) { if (StringUtils.isBlank(categoryId) || CategoryREST.ALL.equals(categoryId)) {
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user.get()); List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user.get());
List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user.get(), subs, true, null, null, 0, 1, List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user.get(), subs, true, null, null, 0, 1,
order, true, false, null); order, true, false, null);
status = Iterables.getFirst(statuses, null); s = Iterables.getFirst(statuses, null);
} else { } else {
FeedCategory category = feedCategoryDAO.findById(user.get(), Long.valueOf(categoryId)); FeedCategory category = feedCategoryDAO.findById(user.get(), Long.valueOf(categoryId));
if (category != null) { if (category != null) {
List<FeedCategory> children = feedCategoryDAO.findAllChildrenCategories(user.get(), category); List<FeedCategory> children = feedCategoryDAO.findAllChildrenCategories(user.get(), category);
List<FeedSubscription> subscriptions = feedSubscriptionDAO.findByCategories(user.get(), children); List<FeedSubscription> subscriptions = feedSubscriptionDAO.findByCategories(user.get(), children);
List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user.get(), subscriptions, true, null, List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user.get(), subscriptions, true, null,
null, 0, 1, order, true, false, null); null, 0, 1, order, true, false, null);
status = Iterables.getFirst(statuses, null); s = Iterables.getFirst(statuses, null);
}
} }
} if (s != null) {
if (status != null) { s.setRead(true);
status.setRead(true); feedEntryStatusDAO.saveOrUpdate(s);
feedEntryStatusDAO.saveOrUpdate(status); }
} return s;
return status; });
}
}.run();
if (status == null) { if (status == null) {
resp.sendRedirect(resp.encodeRedirectURL(config.getApplicationSettings().getPublicUrl())); resp.sendRedirect(resp.encodeRedirectURL(config.getApplicationSettings().getPublicUrl()));
@@ -103,5 +96,4 @@ public class NextUnreadServlet extends HttpServlet {
resp.sendRedirect(resp.encodeRedirectURL(url)); resp.sendRedirect(resp.encodeRedirectURL(url));
} }
} }
} }