would_like_to_be_anon
would_like_to_be_anon

Reputation: 1727

Using map to cache the data from the sql ( with <= timestamp col in it )

I would like to put all the data from the following sql in a map( because this sql is called many times and instead of going to db everytime ), but I am wondering how to implement the <= for the timestamp

EDIT:

I am using Oracle, just updated the tags, however, I am using PreparedStatement in java which caches queries, without being recompiled, but our program doesn't have a cache solution to cache the data from the table. going to the db and getting the data is taking 2 ms roundtrip, but getting the data from the HashMap would take a nano second. This query is being executed around 20,000 times and we would like to load all the data initially and put it inside the Hashmap.

END OF EDIT.

SELECT ar   
FROM table1   
WHERE fk_col1 = ?   
AND timestamp_col <= ?   
ORDER BY date DESC 

The way I did is as follows: but I am not sure, the timestamp_col in equals and hashCode is right. Could you suggest the modifications?

public class Table1Key
{
    private String fk_col1;
    private java.sql.Timestamp timestamp_col;
    //setters and getters here.
    //implementing hashCode and equals.
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((fk_col1 == null) ? 0 : fk_col1.hashCode());
        result = prime * result
                + ((timestamp_col == null) ? 0 : timestamp_col.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Table1Key other = (Table1Key) obj;
        if (fk_col1 == null) {
            if (other.fk_col1 != null)
                return false;
        } else if (!fk_col1.equals(other.fk_col1))
            return false;
        if (timestamp_col == null) {
            if (other.timestamp_col != null)
                return false;
        } else if (!timestamp_col.equals(other.timestamp_col))
            return false;
        return true;
    }
}

...

private Map<Table1Key, String> map = Functions.getHashMapInstance();

public class Functions {
...
  public static <K,V> HashMap<K,V> getHashMapInstance() {
      return new HashMap<K,V>();
  }
}

So, I would populate the map like the following:

private void populateMap() throws SQLException {
      try {
        ps = conn.prepareStatement(table1Sql);
        ps.setFetchSize(20000);
        ResultSet rs = ps.executeQuery();
        while(rs.next()) {
            Table1Key rdk = new Table1Key();
            String ar = rs.getString(1);
            rdk.setFk_col1(rs.getString(2));
            rdk.setTimestampCol(rs.getTimestamp(3));
            if(actualRateMap.get(rdk) == null) {
                actualRateMap.put(rdk, ar);
            }
        }
    } catch (SQLException e) {
        e.printStackTrace();
        throw e;
    } finally {
        ps.close();
    }
  }

//set the key here.

Table1Key tk = new Table1Key();
tk.setFk_col1(col1);
tk.setTimestampCol(timestamp);
String ar = actualRateMap.get(tk);

//My main concern here is .. this will work if the sql has timestamp_col = ?, but what if the timestamp_col is < than what is present in the map?

if(actualRate != null) {
    Logger.info("Actual Rate:"+actualRate);
}

Upvotes: 2

Views: 1863

Answers (2)

卢声远 Shengyuan Lu
卢声远 Shengyuan Lu

Reputation: 32014

HashMap doesn't do the job for your case, rather TreeMap can help.

a): RangeMap solution

Guava RangeMap is designed to handle such case:

//Initial RangeMap  
final RangeMap<java.sql.Timestamp, String> cache = TreeRangeMap.create();
...
String value = cache.get(thisTimestamp);
if(value == null){
     String queryFromDB = ...;//Query from DB
     cache.put(Range.atMost(thisTimestamp), queryFromDB);
     value = queryFromDB;
}

Of course, 'fk_coll' is problem. so you could define Map<String/*fk_coll*/, RangeMap<java.sql.Timestamp, String>> to handle the case.

b): TreeMap solution

final TreeMap<java.sql.Timestamp, String> cache = new TreeMap<>();
...
//Get least key greater than or equal to the 'thisTimestamp' 
Map.Entry<java.sql.Timestamp, String> entry = cache.ceilingEntry(thisTimestamp);
if(entry == null){
   String queryFromDB = ...;//Query from DB
   cache.put(thisTimestamp, queryFromDB);
   value = queryFromDB;
} else {
   value = entry.getValue();
}

and

HashMap<String/*fk_coll*/, TreeMap<java.sql.Timestamp, String>>

handles 'fk_coll'.

plus: evict is a problem in any cache case.

Upvotes: 2

Sami Korhonen
Sami Korhonen

Reputation: 1303

While HashMap does not specify the order of elements, TreeMap does. I didn't test the following code, but I guess it should work:

TreeMap<Timestamp, String> map = new TreeMap<Timestamp, String>(new Comparator<Timestamp>() {
    public int compare(Timestamp t1, Timestamp t2) {
       // Inverse order is required for using tailMap() as <= operator
       return t2.compareTo(t1);
    }

    public boolean equals(Object o) {
       throw new UnsupportedOperationException("This operation has not been implemented!");
    }
});
// TODO: insert data to the map
// ...

// Get the first key that is <= timestamp (remember that map is using inverse order)
TreeMap<Timestamp, String> tailMap = map.tailMap(timestamp);
Timestamp firstKey = tailMap.firstKey();
if (firstKey != null) {
  // Get the value
  String value = tailMap.get(firstKey);
}

Upvotes: 0

Related Questions