package com.moparisthebest.jdbc; import com.moparisthebest.jdbc.util.ResultSetIterable; import java.sql.*; import java.util.*; //IFJAVA8_START import java.util.stream.Stream; //IFJAVA8_END import static com.moparisthebest.jdbc.TryClose.tryClose; /** * This class caches the PreparedStatement's it creates for the strings you send in, then closes them when the close() method is called. * Since PreparedStatement is not thread-safe, this class cannot be either. Be sure to call it from only a single thread * or synchronize around it. */ public class CachingQueryMapper extends QueryMapper { protected final Map cache; protected CachingQueryMapper(Connection conn, String jndiName, Factory factory, ResultSetMapper cm, final int maxEntries) { super(conn, jndiName, factory, cm); if (maxEntries > 0) { // we want a limited cache final float loadFactor = 0.75f; // default for HashMaps // if we set the initialCapacity this way, nothing should ever need re-sized final int initialCapacity = ((int) Math.ceil(maxEntries / loadFactor)) + 1; cache = new LinkedHashMap(initialCapacity, loadFactor, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { final boolean remove = size() > maxEntries; if(remove){ //System.out.printf("closing PreparedStatement '%s' with key '%s'\n", eldest.getValue(), eldest.getKey()); tryClose(eldest.getValue()); } return remove; } }; } else cache = new HashMap(); } protected CachingQueryMapper(Connection conn, String jndiName, Factory factory, ResultSetMapper cm) { this(conn, jndiName, factory, cm, 20); // default size of 20 } public CachingQueryMapper(Connection conn, ResultSetMapper cm, final int maxEntries) { this(conn, null, null, cm, maxEntries); } public CachingQueryMapper(Connection conn, final int maxEntries) { this(conn, null, null, null, maxEntries); } public CachingQueryMapper(String jndiName, ResultSetMapper cm, final int maxEntries) { this(null, jndiName, null, cm, maxEntries); } public CachingQueryMapper(String jndiName, final int maxEntries) { this(null, jndiName, null, null, maxEntries); } public CachingQueryMapper(Factory factory, ResultSetMapper cm, final int maxEntries) { this(null, null, factory, cm, maxEntries); } public CachingQueryMapper(Factory factory, final int maxEntries) { this(null, null, factory, null, maxEntries); } public CachingQueryMapper(Connection conn, ResultSetMapper cm) { this(conn, null, null, cm); } public CachingQueryMapper(Connection conn) { this(conn, null, null, null); } public CachingQueryMapper(String jndiName, ResultSetMapper cm) { this(null, jndiName, null, cm); } public CachingQueryMapper(String jndiName) { this(null, jndiName, null, null); } public CachingQueryMapper(Factory factory, ResultSetMapper cm) { this(null, null, factory, cm); } public CachingQueryMapper(Factory factory) { this(null, null, factory, null); } /** * This could perhaps end up caching something it shouldn't, as psf could return a different PreparedStatement * for a given sql, we are going to assume this would never happen, if it could, don't use CachingQueryMapper or * override this method, whatever you want. */ protected PreparedStatement getPreparedStatement(final String sql, final PreparedStatementFactory psf, final Object... bindObjects) throws SQLException { final String preparedSql = prepareInListSql(sql, bindObjects); PreparedStatement ps = cache.get(preparedSql); if (ps == null) { //System.out.println("cache miss"); ps = psf.prepareStatement(conn, preparedSql); cache.put(preparedSql, ps); } //else System.out.println("cache hit"); return ps; } public void clearCache(boolean close) { //System.out.println("cache size: "+cache.size()); for (PreparedStatement ps : cache.values()) tryClose(ps); if (close) super.close(); else cache.clear(); } public void clearCache() { this.clearCache(false); } @Override public void close() { this.clearCache(true); } @Override public int executeUpdate(String sql, Object... bindObjects) throws SQLException { return super.executeUpdate(getPreparedStatement(sql, bindObjects), bindObjects); } @Override public boolean executeUpdateSuccess(String sql, Object... bindObjects) throws SQLException { return super.executeUpdateSuccess(getPreparedStatement(sql, bindObjects), bindObjects); } @Override public Long insertGetGeneratedKey(String sql, Object... bindObjects) throws SQLException { return super.insertGetGeneratedKey(getPreparedStatement(sql, getSingleColumnPreparedStatementFactory(), bindObjects), bindObjects); } @Override public T insertGetGeneratedKeyType(String sql, final PreparedStatementFactory psf, TypeReference typeReference, Object... bindObjects) throws SQLException { return super.insertGetGeneratedKeyType(getPreparedStatement(sql, psf, bindObjects), typeReference, bindObjects); } // these grab ResultSets from the database @Override public ResultSet toResultSet(final String sql, final PreparedStatementFactory psf, final Object... bindObjects) throws SQLException { return super.toResultSet(getPreparedStatement(sql, psf, bindObjects), bindObjects); } // DO NOT EDIT BELOW THIS LINE, OR CHANGE THIS COMMENT, CODE AUTOMATICALLY GENERATED BY genQueryMapper.sh @Override public T toObject(String sql, Class componentType, final Object... bindObjects) throws SQLException { return super.toObject(getPreparedStatement(sql, bindObjects), componentType, bindObjects); } @Override public ResultSetIterable toResultSetIterable(String sql, Class componentType, final Object... bindObjects) throws SQLException { return super.toResultSetIterable(getPreparedStatement(sql, bindObjects), componentType, bindObjects); } @Override public , V> ResultSetIterable> toResultSetIterable(String sql, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toResultSetIterable(getPreparedStatement(sql, bindObjects), componentType, mapValType, bindObjects); } //IFJAVA8_START @Override public Stream toStream(String sql, Class componentType, final Object... bindObjects) throws SQLException { return super.toStream(getPreparedStatement(sql, bindObjects), componentType, bindObjects); } //IFJAVA8_END //IFJAVA8_START @Override public , V> Stream> toStream(String sql, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toStream(getPreparedStatement(sql, bindObjects), componentType, mapValType, bindObjects); } //IFJAVA8_END @Override public , V> Map toSingleMap(String sql, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toSingleMap(getPreparedStatement(sql, bindObjects), componentType, mapValType, bindObjects); } @Override public Map toSingleMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { return super.toSingleMap(getPreparedStatement(sql, bindObjects), mapValType, bindObjects); } @Override public T toType(String sql, TypeReference typeReference, final Object... bindObjects) throws SQLException { return super.toType(getPreparedStatement(sql, bindObjects), typeReference, bindObjects); } @Override public , E> T toCollection(String sql, final Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { return super.toCollection(getPreparedStatement(sql, bindObjects), collectionType, componentType, bindObjects); } @Override public , E> T toCollection(String sql, T list, Class componentType, final Object... bindObjects) throws SQLException { return super.toCollection(getPreparedStatement(sql, bindObjects), list, componentType, bindObjects); } @Override public , K, E> T toMap(String sql, T map, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { return super.toMap(getPreparedStatement(sql, bindObjects), map, mapKeyType, componentType, bindObjects); } @Override public , K, E extends Collection, C> T toMapCollection(String sql, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { return super.toMapCollection(getPreparedStatement(sql, bindObjects), returnType, mapKeyType, collectionType, componentType, bindObjects); } @Override public , K, E extends Collection, C> T toMapCollection(String sql, T map, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { return super.toMapCollection(getPreparedStatement(sql, bindObjects), map, mapKeyType, collectionType, componentType, bindObjects); } @Override public ListIterator toListIterator(String sql, final Class type, final Object... bindObjects) throws SQLException { return super.toListIterator(getPreparedStatement(sql, bindObjects), type, bindObjects); } @Override public Iterator toIterator(String sql, final Class type, final Object... bindObjects) throws SQLException { return super.toIterator(getPreparedStatement(sql, bindObjects), type, bindObjects); } @Override public T[] toArray(String sql, final Class type, final Object... bindObjects) throws SQLException { return super.toArray(getPreparedStatement(sql, bindObjects), type, bindObjects); } @Override public List toList(String sql, Class componentType, final Object... bindObjects) throws SQLException { return super.toList(getPreparedStatement(sql, bindObjects), componentType, bindObjects); } @Override public Map toMap(String sql, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { return super.toMap(getPreparedStatement(sql, bindObjects), mapKeyType, componentType, bindObjects); } @Override public , C> Map toMapList(String sql, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { return super.toMapList(getPreparedStatement(sql, bindObjects), mapKeyType, componentType, bindObjects); } @Override public , E extends Map, V> T toCollectionMap(String sql, final Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toCollectionMap(getPreparedStatement(sql, bindObjects), collectionType, componentType, mapValType, bindObjects); } @Override public , E extends Map, V> T toCollectionMap(String sql, T list, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toCollectionMap(getPreparedStatement(sql, bindObjects), list, componentType, mapValType, bindObjects); } @Override public , K, E extends Map, V> T toMapMap(String sql, final Class returnType, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toMapMap(getPreparedStatement(sql, bindObjects), returnType, mapKeyType, componentType, mapValType, bindObjects); } @Override public , K, E extends Map, V> T toMapMap(String sql, T map, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toMapMap(getPreparedStatement(sql, bindObjects), map, mapKeyType, componentType, mapValType, bindObjects); } @Override public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(String sql, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toMapCollectionMap(getPreparedStatement(sql, bindObjects), returnType, mapKeyType, collectionType, componentType, mapValType, bindObjects); } @Override public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(String sql, T map, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toMapCollectionMap(getPreparedStatement(sql, bindObjects), map, mapKeyType, collectionType, componentType, mapValType, bindObjects); } @Override public , V> ListIterator> toListIteratorMap(String sql, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { return super.toListIteratorMap(getPreparedStatement(sql, bindObjects), type, mapValType, bindObjects); } @Override public , V> Iterator> toIteratorMap(String sql, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { return super.toIteratorMap(getPreparedStatement(sql, bindObjects), type, mapValType, bindObjects); } @Override public , V> Map[] toArrayMap(String sql, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { return super.toArrayMap(getPreparedStatement(sql, bindObjects), type, mapValType, bindObjects); } @Override public , V> List> toListMap(String sql, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toListMap(getPreparedStatement(sql, bindObjects), componentType, mapValType, bindObjects); } @Override public , V> Map> toMapMap(String sql, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toMapMap(getPreparedStatement(sql, bindObjects), mapKeyType, componentType, mapValType, bindObjects); } @Override public , V> Map>> toMapListMap(String sql, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toMapListMap(getPreparedStatement(sql, bindObjects), mapKeyType, componentType, mapValType, bindObjects); } @Override public ListIterator> toListIteratorMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { return super.toListIteratorMap(getPreparedStatement(sql, bindObjects), mapValType, bindObjects); } @Override public Iterator> toIteratorMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { return super.toIteratorMap(getPreparedStatement(sql, bindObjects), mapValType, bindObjects); } @Override public List> toListMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { return super.toListMap(getPreparedStatement(sql, bindObjects), mapValType, bindObjects); } @Override public Map> toMapMap(String sql, Class mapKeyType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toMapMap(getPreparedStatement(sql, bindObjects), mapKeyType, mapValType, bindObjects); } @Override public Map>> toMapListMap(String sql, Class mapKeyType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toMapListMap(getPreparedStatement(sql, bindObjects), mapKeyType, mapValType, bindObjects); } }