blob: 2ca7ecef743ffabe10e0670bfe691242d278da3e [file]
/*
* Copyright DataStax, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.datastax.oss.driver.mapper;
import static com.datastax.oss.driver.assertions.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.assertj.core.data.Offset.offset;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.BoundStatement;
import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.mapper.annotations.ClusteringColumn;
import com.datastax.oss.driver.api.mapper.annotations.Computed;
import com.datastax.oss.driver.api.mapper.annotations.CqlName;
import com.datastax.oss.driver.api.mapper.annotations.Dao;
import com.datastax.oss.driver.api.mapper.annotations.DaoFactory;
import com.datastax.oss.driver.api.mapper.annotations.DaoKeyspace;
import com.datastax.oss.driver.api.mapper.annotations.Delete;
import com.datastax.oss.driver.api.mapper.annotations.Entity;
import com.datastax.oss.driver.api.mapper.annotations.GetEntity;
import com.datastax.oss.driver.api.mapper.annotations.Insert;
import com.datastax.oss.driver.api.mapper.annotations.Mapper;
import com.datastax.oss.driver.api.mapper.annotations.PartitionKey;
import com.datastax.oss.driver.api.mapper.annotations.Query;
import com.datastax.oss.driver.api.mapper.annotations.Select;
import com.datastax.oss.driver.api.mapper.annotations.SetEntity;
import com.datastax.oss.driver.api.mapper.annotations.Update;
import com.datastax.oss.driver.api.mapper.entity.saving.NullSavingStrategy;
import com.datastax.oss.driver.api.testinfra.ccm.CcmRule;
import com.datastax.oss.driver.api.testinfra.session.SessionRule;
import com.datastax.oss.driver.categories.ParallelizableTests;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
@Category(ParallelizableTests.class)
public class ComputedIT {
private static final CcmRule CCM_RULE = CcmRule.getInstance();
private static final SessionRule<CqlSession> SESSION_RULE = SessionRule.builder(CCM_RULE).build();
@ClassRule
public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE);
private static TestMapper mapper;
private static AtomicInteger keyProvider = new AtomicInteger();
@BeforeClass
public static void setup() {
CqlSession session = SESSION_RULE.session();
for (String query :
ImmutableList.of(
"CREATE TABLE computed_entity(id int, c_id int, v int, primary key (id, c_id))")) {
session.execute(
SimpleStatement.builder(query).setExecutionProfile(SESSION_RULE.slowProfile()).build());
}
mapper = new ComputedIT_TestMapperBuilder(session).build();
}
@Test
public void should_not_include_computed_values_in_insert() {
ComputedDao computedDao = mapper.computedDao(SESSION_RULE.keyspace());
int key = keyProvider.incrementAndGet();
ComputedEntity entity = new ComputedEntity(key, 1, 2);
computedDao.save(entity);
ComputedEntity retrievedValue = computedDao.findById(key, 1);
assertThat(retrievedValue.getId()).isEqualTo(key);
assertThat(retrievedValue.getcId()).isEqualTo(1);
assertThat(retrievedValue.getV()).isEqualTo(2);
}
@Test
public void should_return_computed_values_in_select() {
ComputedDao computedDao = mapper.computedDao(SESSION_RULE.keyspace());
int key = keyProvider.incrementAndGet();
long time = System.currentTimeMillis() - 1000;
ComputedEntity entity = new ComputedEntity(key, 1, 2);
computedDao.saveWithTime(entity, 3600, time);
ComputedEntity retrievedValue = computedDao.findById(key, 1);
assertThat(retrievedValue.getId()).isEqualTo(key);
assertThat(retrievedValue.getcId()).isEqualTo(1);
assertThat(retrievedValue.getV()).isEqualTo(2);
assertThat(retrievedValue.getTtl()).isCloseTo(3600, offset(10));
assertThat(retrievedValue.getWritetime()).isEqualTo(time);
}
@Test
public void should_not_include_computed_values_in_delete() {
// should not be the case since delete operates on primary key..
ComputedDao computedDao = mapper.computedDao(SESSION_RULE.keyspace());
int key = keyProvider.incrementAndGet();
ComputedEntity entity = new ComputedEntity(key, 1, 2);
computedDao.save(entity);
// retrieve values so computed values are present.
ComputedEntity retrievedValue = computedDao.findById(key, 1);
computedDao.delete(retrievedValue);
assertThat(computedDao.findById(key, 1)).isNull();
}
@Test
public void should_not_include_computed_values_in_SetEntity() {
ComputedDao computedDao = mapper.computedDao(SESSION_RULE.keyspace());
int key = keyProvider.incrementAndGet();
CqlSession session = SESSION_RULE.session();
PreparedStatement preparedStatement =
session.prepare("INSERT INTO computed_entity (id, c_id, v) VALUES (?, ?, ?)");
BoundStatementBuilder builder = preparedStatement.boundStatementBuilder();
ComputedEntity entity = new ComputedEntity(key, 1, 2);
BoundStatement statement = computedDao.set(builder, entity).build();
session.execute(statement);
// retrieve values to ensure was successful.
ComputedEntity retrievedValue = computedDao.findById(key, 1);
assertThat(retrievedValue.getId()).isEqualTo(key);
assertThat(retrievedValue.getcId()).isEqualTo(1);
assertThat(retrievedValue.getV()).isEqualTo(2);
}
@Test
public void should_return_computed_values_in_GetEntity() {
ComputedDao computedDao = mapper.computedDao(SESSION_RULE.keyspace());
int key = keyProvider.incrementAndGet();
long time = System.currentTimeMillis() - 1000;
ComputedEntity entity = new ComputedEntity(key, 1, 2);
computedDao.saveWithTime(entity, 3600, time);
CqlSession session = SESSION_RULE.session();
/*
* Query with the computed values included.
*
* Since the mapper expects the result name to match the property name, we used aliasing
* here.
*
* In the case of ttl(v), since we annotated ttl as @CqlName("myttl") we expect myttl to
* be the alias.
*/
ResultSet result =
session.execute(
SimpleStatement.newInstance(
"select id, c_id, v, writetime(v) as writetime, ttl(v) as myttl from "
+ "computed_entity where "
+ "id=? and "
+ "c_id=? limit 1",
key,
1));
assertThat(result.getAvailableWithoutFetching()).isEqualTo(1);
ComputedEntity retrievedValue = computedDao.get(result.one());
assertThat(retrievedValue.getId()).isEqualTo(key);
assertThat(retrievedValue.getcId()).isEqualTo(1);
assertThat(retrievedValue.getV()).isEqualTo(2);
// these should be set
assertThat(retrievedValue.getTtl()).isCloseTo(3600, offset(10));
assertThat(retrievedValue.getWritetime()).isEqualTo(time);
}
@Test
public void should_fail_if_alias_does_not_match_cqlName() {
ComputedDao computedDao = mapper.computedDao(SESSION_RULE.keyspace());
int key = keyProvider.incrementAndGet();
long time = System.currentTimeMillis() - 1000;
ComputedEntity entity = new ComputedEntity(key, 1, 2);
computedDao.saveWithTime(entity, 3600, time);
CqlSession session = SESSION_RULE.session();
/*
* Query with the computed values included.
*
* Since the mapper expects the result name to match the property name, we used aliasing
* here and used the wrong name for the alias 'notwritetime' which does not map to the cqlName.
*/
ResultSet result =
session.execute(
SimpleStatement.newInstance(
"select id, c_id, v, writetime(v) as notwritetime, ttl(v) as myttl from "
+ "computed_entity where "
+ "id=? and "
+ "c_id=? limit 1",
key,
1));
// should raise an exception as 'writetime' is not found in result set.
Throwable t = catchThrowable(() -> computedDao.get(result.one()));
assertThat(t)
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("writetime is not a column in this row");
}
@Test
public void should_return_computed_values_in_query() {
ComputedDao computedDao = mapper.computedDao(SESSION_RULE.keyspace());
int key = keyProvider.incrementAndGet();
long time = System.currentTimeMillis() - 1000;
ComputedEntity entity = new ComputedEntity(key, 1, 2);
computedDao.saveWithTime(entity, 3600, time);
ComputedEntity retrievedValue = computedDao.findByIdQuery(key, 1);
assertThat(retrievedValue.getId()).isEqualTo(key);
assertThat(retrievedValue.getcId()).isEqualTo(1);
assertThat(retrievedValue.getV()).isEqualTo(2);
// these should be set
assertThat(retrievedValue.getTtl()).isCloseTo(3600, offset(10));
assertThat(retrievedValue.getWritetime()).isEqualTo(time);
}
@Entity
public static class ComputedEntity {
@PartitionKey private int id;
@ClusteringColumn private int cId;
private int v;
@Computed("writetime(v)")
private long writetime;
// use CqlName to ensure it is used for the alias.
@CqlName("myttl")
@Computed("ttl(v)")
private int ttl;
public ComputedEntity() {}
public ComputedEntity(int id, int cId, int v) {
this.id = id;
this.cId = cId;
this.v = v;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getcId() {
return cId;
}
public void setcId(int cId) {
this.cId = cId;
}
public int getV() {
return v;
}
public void setV(int v) {
this.v = v;
}
public long getWritetime() {
return writetime;
}
public void setWritetime(long writetime) {
this.writetime = writetime;
}
public int getTtl() {
return ttl;
}
public void setTtl(int ttl) {
this.ttl = ttl;
}
}
@Dao
public interface ComputedDao {
@Select
ComputedEntity findById(int id, int cId);
@Insert(nullSavingStrategy = NullSavingStrategy.SET_TO_NULL)
void save(ComputedEntity entity);
@Insert(
ttl = ":ttl",
timestamp = ":writeTime",
nullSavingStrategy = NullSavingStrategy.SET_TO_NULL)
void saveWithTime(ComputedEntity entity, int ttl, long writeTime);
@Delete
void delete(ComputedEntity entity);
@SetEntity(nullSavingStrategy = NullSavingStrategy.SET_TO_NULL)
BoundStatementBuilder set(BoundStatementBuilder builder, ComputedEntity computedEntity);
@GetEntity
ComputedEntity get(Row row);
@Query(
value =
"select id, c_id, v, ttl(v) as myttl, writetime(v) as writetime from "
+ "${qualifiedTableId} WHERE id = :id and "
+ "c_id = :cId",
nullSavingStrategy = NullSavingStrategy.SET_TO_NULL)
ComputedEntity findByIdQuery(int id, int cId);
@Update(nullSavingStrategy = NullSavingStrategy.SET_TO_NULL)
void update(ComputedEntity entity);
}
@Mapper
public interface TestMapper {
@DaoFactory
ComputedDao computedDao(@DaoKeyspace CqlIdentifier keyspace);
}
}