@Override
  public Long insert(final Subscription subscription) {
    Long result =
        dbi.withHandle(
            new HandleCallback<Long>() {
              @Override
              public Long withHandle(Handle handle) throws Exception {
                return handle
                    .createStatement(
                        "insert into subscriptions (topic, metadata, channel) values (:topic, :metadata, :channel)")
                    .bind("topic", subscription.getTopic())
                    .bind("metadata", mapper.writeValueAsString(subscription.getMetadata()))
                    .bind("channel", subscription.getChannel())
                    .executeAndReturnGeneratedKeys(LongMapper.FIRST)
                    .first();
              }
            });

    if (!Strings.isNullOrEmpty(subscription.getMetadata().getFeed())) {
      subscriptionCache.removeFeedSubscriptions(subscription.getMetadata().getFeed());
    }
    if (!Strings.isNullOrEmpty(subscription.getTopic())) {
      subscriptionCache.removeTopicSubscriptions(subscription.getTopic());
    }

    return result;
  }
  public Set<Subscription> loadByFeed(final String feed) {
    Set<Subscription> subscriptions = subscriptionCache.loadFeedSubscriptions(feed);
    if (subscriptions != null && !subscriptions.isEmpty()) {
      return subscriptions;
    }

    return dbi.withHandle(
        new HandleCallback<Set<Subscription>>() {
          @Override
          public Set<Subscription> withHandle(Handle handle) throws Exception {
            FeedEventMetaData metadata = new FeedEventMetaData(feed);
            Set<Subscription> subscriptions =
                ImmutableSet.copyOf(
                    handle
                        .createQuery(
                            "select id, metadata, channel, topic from subscriptions where metadata = :metadata")
                        .bind("metadata", mapper.writeValueAsString(metadata))
                        .map(new SubscriptionMapper())
                        .list());

            subscriptionCache.addFeedSubscriptions(feed, subscriptions);

            return subscriptions;
          }
        });
  }
  @Override
  public Set<Subscription> loadByTopic(final String topicQuery) {
    final Set<String> topicSubQueries = decomposeTopicQuery(topicQuery);

    final Map<String, Optional<Subscription>> cachedResults =
        subscriptionCache.loadTopicSubscriptions(topicSubQueries);

    final Set<Subscription> result = new HashSet<Subscription>();

    // Iterate through the results from the cache, and remove any topics
    // that were found from the list of topics left to query, and add
    // the non-null subscriptions to the list of results
    for (String topic : cachedResults.keySet()) {
      if (cachedResults.get(topic).isPresent()) {
        result.add(cachedResults.get(topic).get());
        topicSubQueries.remove(topic);
      }
    }

    // all topics that are found in the cache will be removed, so if no
    // topic subqueries are left, we are done
    if (!topicSubQueries.isEmpty()) {

      Collection<Subscription> dbResults =
          dbi.withHandle(
              new HandleCallback<Collection<Subscription>>() {
                @Override
                public Collection<Subscription> withHandle(Handle handle) throws Exception {

                  final Set<Subscription> dbResult = new HashSet<Subscription>();

                  InClauseExpander in = new InClauseExpander(topicSubQueries);

                  Iterator<Subscription> subscriptionsIter =
                      handle
                          .createQuery(
                              "select id, metadata, channel, topic from subscriptions where topic in ("
                                  + in.getExpansion()
                                  + ")")
                          .bindNamedArgumentFinder(in)
                          .map(new SubscriptionMapper())
                          .iterator();

                  if (subscriptionsIter != null) {
                    while (subscriptionsIter.hasNext()) {
                      Subscription dbSubscription = subscriptionsIter.next();
                      dbResult.add(dbSubscription);

                      subscriptionCache.addTopicSubscriptions(
                          dbSubscription.getTopic(), Optional.of(dbSubscription));
                      topicSubQueries.remove(dbSubscription.getTopic());
                    }
                  }

                  return dbResult;
                }
              });

      // Add the database results to the results from the cache
      result.addAll(dbResults);

      // Add empty subscriptions to the cache
      if (!topicSubQueries.isEmpty()) {
        subscriptionCache.addEmptyTopicSubscriptions(topicSubQueries);
      }
    }

    return ImmutableSet.copyOf(result);
  }
 @Override
 public void cleanUp() {
   subscriptionCache.cleanUp();
 }