/* * Copyright (c) 2000 Dustin Sallings <dustin@spy.net> */ package net.spy.db; import java.math.BigDecimal; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.Arrays; import net.spy.cache.SimpleCache; /** * Prepared statement for executing cached queries */ public class CachePreparedStatementStub extends GenericPreparedStatementStub { // Stored DB handle SpyDB db=null; // How long the results of this statement should be cached private long cacheTime=60*60*1000; // Query timeout private int timeout=0; // Max rows private int maxRows=0; /** * Create a CachePreparedStatement object for the given query (you * probably don't want to do this directly). */ public CachePreparedStatementStub(SpyDB d, String query, long cTime) { super(query); db=d; cacheTime=cTime; } /** * Get an integer hash code to uniquely identify this object. */ @Override public int hashCode() { int hc=0; hc+=getQuery().hashCode(); StringBuilder sb=new StringBuilder(256); for(int i=0; i<getArgs().length; i++) { sb.append(getArgs()[i]); sb.append((char)0x00); } // Hashcode of all of the args run together. hc+=sb.toString().hashCode(); return(hc); } /** * Equal if two objects have the same hash code, same query, and same * parameters. */ @Override public boolean equals(Object o) { boolean rv=false; int otherHc=o.hashCode(); if(otherHc == hashCode()) { if(o instanceof CachePreparedStatementStub) { CachePreparedStatementStub cpss=(CachePreparedStatementStub)o; String oQuery=cpss.getQuery(); Object[] otherArgs=cpss.getArgs(); if(oQuery != null && oQuery.equals(getQuery())) { // Finally, true if the args are equal. rv=Arrays.equals(getArgs(), otherArgs); } // query check } // instance check } // hash code check return(rv); } // Implemented public ResultSet executeQuery() throws SQLException { int hc=hashCode(); String key="dbcache_prepared_" + hc; SimpleCache cache=SimpleCache.getInstance(); CachedResultSet crs=(CachedResultSet)cache.get(key); if(crs==null) { crs=realExecuteQuery(); cache.store(key, crs, cacheTime*1000); } ResultSet crsret=(ResultSet)crs.newCopy(); return(crsret); } /** * Set the query timeout. * * @param to the maximum number of seconds * @throws SQLException if the number is not greater than or equal to 0 */ public void setQueryTimeout(int to) throws SQLException { if(to < 0) { throw new SQLException("Invalid value for query timeout: " + to); } timeout=to; } /** * Set the max rows for the query return. * * @param to the maximum number of rows to return * @throws SQLException if the value is not greater than or equal to 0 */ public void setMaxRows(int to) throws SQLException { if(to < 0) { throw new SQLException("Invalid value for max rows: " + to); } maxRows=to; } // OK, here's what happens when we determine that we really don't have // the data and need to come up with it. private CachedResultSet realExecuteQuery() throws SQLException { PreparedStatement pst=db.prepareStatement(getQuery()); pst.setQueryTimeout(timeout); pst.setMaxRows(maxRows); // Set allllllll the types Object[] args=getArgs(); for(int i=0; i<args.length; i++) { try { switch(getTypes()[i]) { case Types.BIT: pst.setBoolean(i+1, ((Boolean)args[i]).booleanValue()); break; case Types.DATE: pst.setDate(i+1, (Date)args[i]); break; case Types.DOUBLE: pst.setDouble(i+1, ((Double)args[i]).doubleValue()); break; case Types.FLOAT: pst.setFloat(i+1, ((Float)args[i]).floatValue()); break; case Types.INTEGER: pst.setInt(i+1, ((Integer)args[i]).intValue()); break; case Types.BIGINT: pst.setLong(i+1, ((Long)args[i]).longValue()); break; case Types.TINYINT: pst.setShort(i+1, (short)((Integer)args[i]).intValue()); break; case Types.NULL: pst.setNull(i+1, ((DBNull)args[i]).getType()); break; case Types.OTHER: pst.setObject(i+1, args[i]); break; case Types.VARCHAR: pst.setString(i+1, (String)args[i]); break; case Types.TIME: pst.setTime(i+1, (Time)args[i]); break; case Types.TIMESTAMP: pst.setTimestamp(i+1, (Timestamp)args[i]); break; case Types.DECIMAL: pst.setBigDecimal(i+1, (BigDecimal)args[i]); break; default: throw new SQLException("Whoops, type " + getTypes()[i] + " (" + TypeNames.getTypeName(getTypes()[i]) + ") seems to have been overlooked."); } } catch (NullPointerException ex) { getLogger().error( "error with %s in type %s at param position %d", args[i], getTypes()[i], i); throw ex; } } // OK, at this point, all the arguments should be set, proceed to // execute the query. return(new CachedResultSet(pst.executeQuery())); } // Implemented (sorta) public int executeUpdate() throws SQLException { throw new SQLException("Illegal? This operation makes no sense!"); } @Override public void close() throws SQLException { super.close(); db=null; } }