/**
   * Load the cache entry for the given URI from the database.
   *
   * @param uri the URI of the cached resource for which to return the cache entry
   * @return an instance of KiWiCacheEntry representing the caching metadata for the given resource,
   *     or null in case there is no entry for this resource
   * @throws SQLException
   */
  public KiWiCacheEntry getCacheEntry(String uri) throws SQLException {

    KiWiCacheEntry cached = entryResourceCache.get(uri);

    // lookup element in cache first, so we can avoid reconstructing it if it is already there
    if (cached != null) {
      return cached;
    }

    PreparedStatement query = connection.getPreparedStatement("load.entry_by_uri");
    query.setString(1, uri);
    query.setMaxRows(1);

    // run the database query and if it yields a result, construct a new node; the method call will
    // take care of
    // caching the constructed node for future calls
    ResultSet result = query.executeQuery();
    try {
      if (result.next()) {
        return constructCacheEntry(result);
      } else {
        return null;
      }
    } finally {
      result.close();
    }
  }
  /**
   * Remove the given cache entry from the database. The cache entry passed as argument must be a
   * persistent instance of KiWiCacheEntry.
   *
   * @param entry
   * @throws SQLException
   */
  public void removeCacheEntry(CacheEntry entry) throws SQLException {
    if (!(entry instanceof KiWiCacheEntry) || ((KiWiCacheEntry) entry).getId() == null) {
      throw new IllegalStateException("the passed cache entry is not managed by this connection");
    }

    PreparedStatement deleteEntry = connection.getPreparedStatement("delete.entry");
    deleteEntry.setLong(1, ((KiWiCacheEntry) entry).getId());
    deleteEntry.executeUpdate();

    entryIdCache.remove(((KiWiCacheEntry) entry).getId());
    entryResourceCache.remove(entry.getResource().stringValue());
  }
  /**
   * List all cache entries in the database, regardless of expiry date.
   *
   * @return a closeable iteration with KiWiCacheEntries; needs to be released by the caller
   * @throws SQLException
   */
  public CloseableIteration<KiWiCacheEntry, SQLException> listAll() throws SQLException {
    PreparedStatement queryExpired = connection.getPreparedStatement("query.entries_all");
    final ResultSet result = queryExpired.executeQuery();

    return new ResultSetIteration<KiWiCacheEntry>(
        result,
        new ResultTransformerFunction<KiWiCacheEntry>() {
          @Override
          public KiWiCacheEntry apply(ResultSet input) throws SQLException {
            return constructCacheEntry(result);
          }
        });
  }
  /**
   * Remove the given cache entry from the database. The cache entry passed as argument must be a
   * persistent instance of KiWiCacheEntry.
   *
   * @param uri URI of the entry to delete
   * @throws SQLException
   */
  public void removeCacheEntry(String uri) throws SQLException {

    PreparedStatement deleteEntry = connection.getPreparedStatement("delete.entry_by_uri");
    deleteEntry.setString(1, uri);
    deleteEntry.executeUpdate();

    KiWiCacheEntry cached = entryResourceCache.get(uri);

    if (cached != null) {
      entryResourceCache.remove(uri);
      entryIdCache.remove(cached.getId());
    }
  }
  /**
   * Store the cache entry passed as argument in the database. In case the passed argument is not an
   * instance of KiWiCacheEntry, it will first be converted into a KiWiCacheEntry by copying the
   * fields. In this case, the stored object will not be the same instance as the object passed as
   * argument.
   *
   * @param entry the cache entry to store
   * @throws SQLException
   */
  public void storeCacheEntry(CacheEntry entry) throws SQLException {
    KiWiCacheEntry kEntry;
    if (entry instanceof KiWiCacheEntry) {
      kEntry = (KiWiCacheEntry) entry;
    } else {
      kEntry = new KiWiCacheEntry();
      kEntry.setExpiryDate(entry.getExpiryDate());
      kEntry.setLastRetrieved(entry.getLastRetrieved());
      kEntry.setUpdateCount(entry.getUpdateCount());
      kEntry.setResource(entry.getResource());
      kEntry.setTripleCount(entry.getTripleCount());
    }

    if (!(entry.getResource() instanceof KiWiResource)
        || ((KiWiResource) entry.getResource()).getId() < 0) {
      throw new IllegalStateException(
          "the resource contained in the cache entry is not a KiWiResource!");
    }

    // needed before the entry can be inserted
    connection.flushBatch();

    kEntry.setId(connection.getNextSequence());

    PreparedStatement insertEntry = connection.getPreparedStatement("store.entry");
    insertEntry.setLong(1, kEntry.getId());
    insertEntry.setTimestamp(2, new Timestamp(kEntry.getLastRetrieved().getTime()));
    insertEntry.setTimestamp(3, new Timestamp(kEntry.getExpiryDate().getTime()));
    insertEntry.setLong(4, ((KiWiNode) kEntry.getResource()).getId());
    insertEntry.setInt(5, kEntry.getUpdateCount());
    insertEntry.setInt(6, kEntry.getTripleCount());
    insertEntry.executeUpdate();

    log.debug("persisted ld-cache entry with id {}", kEntry.getId());

    entryIdCache.put(kEntry.getId(), kEntry);
    entryResourceCache.put(kEntry.getResource().stringValue(), kEntry);
  }