Search code examples
javacassandraprepared-statementdatastax-java-driver

How to use prepared statement efficiently using datastax java driver in Cassandra?


I need to query one of the tables in Cassandra using Datastax Java driver. Below is the code I have which works fine -

public class TestCassandra {

        private Session session = null;
        private Cluster cluster = null;

        private static class ConnectionHolder {
            static final TestCassandra connection = new TestCassandra();
        }

        public static TestCassandra getInstance() {
            return ConnectionHolder.connection;
        }

        private TestCassandra() {
            Builder builder = Cluster.builder();
            builder.addContactPoints("127.0.0.1");

            PoolingOptions opts = new PoolingOptions();
            opts.setCoreConnectionsPerHost(HostDistance.LOCAL, opts.getCoreConnectionsPerHost(HostDistance.LOCAL));

            cluster = builder.withRetryPolicy(DowngradingConsistencyRetryPolicy.INSTANCE).withPoolingOptions(opts)
                    .withLoadBalancingPolicy(new TokenAwarePolicy(new DCAwareRoundRobinPolicy("DC2")))
                    .withReconnectionPolicy(new ConstantReconnectionPolicy(100L))
                    .build();
            session = cluster.connect();
        }

    private Set<String> getRandomUsers() {
        Set<String> userList = new HashSet<String>();

        for (int table = 0; table < 14; table++) {
            String sql = "select * from testkeyspace.test_table_" + table + ";";

            try {
                SimpleStatement query = new SimpleStatement(sql);
                query.setConsistencyLevel(ConsistencyLevel.QUORUM);
                ResultSet res = session.execute(query);

                Iterator<Row> rows = res.iterator();
                while (rows.hasNext()) {
                    Row r = rows.next();

                    String user_id = r.getString("user_id");
                    userList.add(user_id);
                }
            } catch (Exception e) {
                System.out.println("error= " + ExceptionUtils.getStackTrace(e));
            }
        }

        return userList;
    }
}

I am using above class like this in my main application -

TestCassandra.getInstance().getRandomUsers();

Is there any way I can use PreparedStatement in getRandomUsers efficiently? I guess I need to make sure that I am creating PreparedStatement only once instead of creating it multiple times. What is the best design for that in my current architecture and how can I use it?


Solution

  • You can create a cache (this is a fairly basic example to give you an idea) of the statements you need. Lets start by creating the class that will be used as a cache.

    private class StatementCache {
        Map<String, PreparedStatement> statementCache = new HashMap<>();
        public BoundStatement getStatement(String cql) {
            PreparedStatement ps = statementCache.get(cql);
            // no statement cached, create one and cache it now.
            if (ps == null) {
                ps = session.prepare(cql);
                statementCache.put(cql, ps);
            }
            return ps.bind();
        }
    }
    

    Then add an instance to your singleton:

    public class TestCassandra {
        private Session session = null;
        private Cluster cluster = null;
        private StatementCache psCache = new StatementCache();
        // rest of class...
    

    And finally use the cache from your function:

    private Set<String> getRandomUsers(String cql) {
    // lots of code.    
            try {
                SimpleStatement query = new SimpleStatement(cql);
                query.setConsistencyLevel(ConsistencyLevel.QUORUM);
                // abstract the handling of the cache to it's own class.
                // this will need some work to make sure it's thread safe
                // as currently it's not.
                ResultSet res = session.execute(psCache.getStatement(cql));