/** * Create an instance of {@link ReactiveCassandraTemplate} initialized with the given {@link ReactiveSessionFactory} * and {@link CassandraConverter}. * * @param sessionFactory {@link ReactiveSessionFactory} used to interact with Cassandra; must not be {@literal null}. * @param converter {@link CassandraConverter} used to convert between Java and Cassandra types; must not be * {@literal null}. * @see org.springframework.data.cassandra.core.convert.CassandraConverter * @see com.datastax.driver.core.Session */ public ReactiveCassandraTemplate(ReactiveSessionFactory sessionFactory, CassandraConverter converter) { this(new ReactiveCqlTemplate(sessionFactory), converter); }
/** * Construct a new {@link ReactiveCqlTemplate}, given a {@link ReactiveSessionFactory} to obtain * {@link ReactiveSession}s from. * * @param reactiveSessionFactory the {@link ReactiveSessionFactory} to obtain {@link ReactiveSession}s from, must not * be {@literal null}. */ public ReactiveCqlTemplate(ReactiveSessionFactory reactiveSessionFactory) { setSessionFactory(reactiveSessionFactory); afterPropertiesSet(); }
@Override public <T> Flux<T> execute(ReactiveSessionCallback<T> action) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); return createFlux(action).onErrorMap(translateException("ReactiveSessionCallback", getCql(action))); }
@Override public Mono<Map<String, Object>> queryForMap(String cql) throws DataAccessException { return queryForObject(cql, getColumnMapRowMapper()); }
@Override public Flux<Map<String, Object>> queryForFlux(String cql) throws DataAccessException { return query(cql, getColumnMapRowMapper()); }
@Override public Flux<Boolean> execute(String cql, Publisher<Object[]> args) throws DataAccessException { Assert.notNull(args, "Args Publisher must not be null"); return execute(newReactivePreparedStatementCreator(cql), (session, ps) -> Flux.from(args).flatMap(objects -> { if (logger.isDebugEnabled()) { logger.debug("Executing Prepared CQL Statement [{}]", cql); } BoundStatement boundStatement = newArgPreparedStatementBinder(objects).bindValues(ps); applyStatementSettings(boundStatement); return session.execute(boundStatement); }).map(ReactiveResultSet::wasApplied)); }
/** * Query using a prepared statement, reading the {@link ReactiveResultSet} with a {@link ReactiveResultSetExtractor}. * * @param psc object that can create a {@link PreparedStatement} given a {@link ReactiveSession} * @param preparedStatementBinder object that knows how to set values on the prepared statement. If this is * {@literal null}, the CQL will be assumed to contain no bind parameters. * @param rse object that will extract results * @return an arbitrary result object, as returned by the {@link ReactiveResultSetExtractor} * @throws DataAccessException if there is any problem */ public <T> Flux<T> query(ReactivePreparedStatementCreator psc, @Nullable PreparedStatementBinder preparedStatementBinder, ReactiveResultSetExtractor<T> rse) throws DataAccessException { Assert.notNull(psc, "ReactivePreparedStatementCreator must not be null"); Assert.notNull(rse, "ReactiveResultSetExtractor object must not be null"); return execute(psc, (session, ps) -> Mono.just(ps).flatMapMany(pps -> { if (logger.isDebugEnabled()) { logger.debug("Executing Prepared CQL Statement [{}]", ps.getQueryString()); } BoundStatement boundStatement = (preparedStatementBinder != null ? preparedStatementBinder.bindValues(ps) : ps.bind()); applyStatementSettings(boundStatement); return session.execute(boundStatement); }).flatMap(rse::extractData)).onErrorMap(translateException("Query", getCql(psc))); }
/** * Prepare the given CQL Statement (or {@link com.datastax.driver.core.PreparedStatement}), applying statement * settings such as fetch size, retry policy, and consistency level. * * @param stmt the CQL Statement to prepare * @see #setFetchSize(int) * @see #setRetryPolicy(RetryPolicy) * @see #setConsistencyLevel(ConsistencyLevel) */ protected void applyStatementSettings(Statement stmt) { ConsistencyLevel consistencyLevel = getConsistencyLevel(); if (consistencyLevel != null && stmt.getConsistencyLevel() == DEFAULTS.getConsistencyLevel()) { stmt.setConsistencyLevel(consistencyLevel); } int fetchSize = getFetchSize(); if (fetchSize != -1 && stmt.getFetchSize() == DEFAULTS.getFetchSize()) { stmt.setFetchSize(fetchSize); } RetryPolicy retryPolicy = getRetryPolicy(); if (retryPolicy != null && stmt.getRetryPolicy() == DEFAULTS.getRetryPolicy()) { stmt.setRetryPolicy(retryPolicy); } }
@Override public <T> Flux<T> query(Statement statement, ReactiveResultSetExtractor<T> rse) throws DataAccessException { Assert.notNull(statement, "CQL Statement must not be null"); Assert.notNull(rse, "ReactiveResultSetExtractor must not be null"); return createFlux(statement, (session, stmt) -> { if (logger.isDebugEnabled()) { logger.debug("Executing CQL Statement [{}]", statement); } return session.execute(stmt).flatMapMany(rse::extractData); }).onErrorMap(translateException("Query", statement.toString())); }
/** * Create a reusable {@link Flux} given a {@link ReactiveStatementCallback} without exception translation. * * @param callback must not be {@literal null}. * @return a reusable {@link Flux} wrapping the {@link ReactiveStatementCallback}. */ protected <T> Flux<T> createFlux(Statement statement, ReactiveStatementCallback<T> callback) { Assert.notNull(callback, "ReactiveStatementCallback must not be null"); applyStatementSettings(statement); ReactiveSession session = getSession(); return Flux.defer(() -> callback.doInStatement(session, statement)); }
@Override public Mono<ReactiveResultSet> queryForResultSet(Statement statement) throws DataAccessException { Assert.notNull(statement, "CQL Statement must not be null"); return createMono(statement, (session, executedStatement) -> { if (logger.isDebugEnabled()) { logger.debug("Executing CQL [{}]", executedStatement); } return session.execute(executedStatement); }).onErrorMap(translateException("QueryForResultSet", statement.toString())); }
@Override public <T> Flux<T> execute(String cql, ReactivePreparedStatementCallback<T> action) throws DataAccessException { return execute(newReactivePreparedStatementCreator(cql), action); }
@Override public Mono<Boolean> execute(String cql, Object... args) throws DataAccessException { return execute(cql, newArgPreparedStatementBinder(args)); }
/** * Prepare the given CQL Statement (or {@link com.datastax.driver.core.PreparedStatement}), applying statement * settings such as retry policy and consistency level. * * @param stmt the CQL Statement to prepare * @see #setRetryPolicy(RetryPolicy) * @see #setConsistencyLevel(ConsistencyLevel) */ protected void applyStatementSettings(PreparedStatement stmt) { ConsistencyLevel consistencyLevel = getConsistencyLevel(); if (consistencyLevel != null) { stmt.setConsistencyLevel(consistencyLevel); } RetryPolicy retryPolicy = getRetryPolicy(); if (retryPolicy != null) { stmt.setRetryPolicy(retryPolicy); } }
@Override public Mono<Map<String, Object>> queryForMap(String cql, Object... args) throws DataAccessException { return queryForObject(cql, getColumnMapRowMapper(), args); }
@Override public Flux<Map<String, Object>> queryForFlux(String cql, Object... args) throws DataAccessException { return query(cql, getColumnMapRowMapper(), args); }
@Override public <T> Flux<T> query(String cql, ReactiveResultSetExtractor<T> resultSetExtractor) throws DataAccessException { Assert.hasText(cql, "CQL must not be empty"); Assert.notNull(resultSetExtractor, "ReactiveResultSetExtractor must not be null"); return createFlux(new SimpleStatement(cql), (session, stmt) -> { if (logger.isDebugEnabled()) { logger.debug("Executing CQL Statement [{}]", cql); } return session.execute(stmt).flatMapMany(resultSetExtractor::extractData); }).onErrorMap(translateException("Query", cql)); }
/** * Create a reusable {@link Mono} given a {@link ReactiveStatementCallback} without exception translation. * * @param callback must not be {@literal null}. * @return a reusable {@link Mono} wrapping the {@link ReactiveStatementCallback}. */ protected <T> Mono<T> createMono(Statement statement, ReactiveStatementCallback<T> callback) { Assert.notNull(callback, "ReactiveStatementCallback must not be null"); applyStatementSettings(statement); ReactiveSession session = getSession(); return Mono.defer(() -> Mono.from(callback.doInStatement(session, statement))); }
@Override public Mono<ReactiveResultSet> queryForResultSet(String cql) throws DataAccessException { Assert.hasText(cql, "CQL must not be empty"); return createMono(new SimpleStatement(cql), (session, statement) -> { if (logger.isDebugEnabled()) { logger.debug("Executing CQL [{}]", cql); } return session.execute(statement); }).onErrorMap(translateException("QueryForResultSet", cql)); }
@Override public <T> Flux<T> execute(ReactivePreparedStatementCreator psc, ReactivePreparedStatementCallback<T> action) throws DataAccessException { Assert.notNull(psc, "ReactivePreparedStatementCreator must not be null"); Assert.notNull(action, "ReactivePreparedStatementCallback object must not be null"); return createFlux(session -> { logger.debug("Preparing statement [{}] using {}", getCql(psc), psc); return psc.createPreparedStatement(session).doOnNext(this::applyStatementSettings) .flatMapMany(ps -> action.doInPreparedStatement(session, ps)); }).onErrorMap(translateException("ReactivePreparedStatementCallback", getCql(psc))); }