1

iam trying to use Spring data cassandra to communicate with cassandra/scyllaDB via REST api. I have entity

@Table public class Transaction { @PrimaryKeyColumn(name = "id", ordinal = 0, type = PrimaryKeyType.PARTITIONED) private String id; @PrimaryKeyColumn(name = "timestamp", ordinal = 1, type = PrimaryKeyType.CLUSTERED) private Instant timestamp; private String currency; } 

with repository

@Repository public interface TransactionRepository extends CassandraRepository<Transaction, String> { } 

and service which is called in REST controller

@Service public class TransactionServiceImpl implements TransactionService{ @Autowired private TransactionRepository transactionRepository; private Transaction getTransaction(String transactionId) { return transactionRepository.findById(transactionId) .orElseThrow(() -> new NotFoundException("Transaction with provided " + transactionId + " does not exist.")); } } 

When i call required REST endpoint, where i provide correct transactionId, Exception is thrown.

Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Cannot obtain where clauses for entity [com.example.dao.entity.Transaction] using [id123456] 

I have made some investigation, but composite key provided in entity class should be valid. What ma i doing wrong?

Model of the table:

CREATE TABLE etl.transaction ( id text, timestamp timestamp, currency ascii, PRIMARY KEY (id, timestamp) ) WITH CLUSTERING ORDER BY (timestamp ASC) AND default_time_to_live = 157680000; // 5 years in seconds 

1 Answer 1

3

I never get this error message. What will you get error message if you have setter/getter methods or use Lombok project and add @Data annotation next to @Table? (If you use IntelliJ, you have to install Lombok plugin!)

In my answer, write comment if there is any result.

@Data @Table public class Transaction { @PrimaryKeyColumn(name = "id", ordinal = 0, type = PrimaryKeyType.PARTITIONED) private String id; @PrimaryKeyColumn(name = "timestamp", ordinal = 1, type = PrimaryKeyType.CLUSTERED) private Instant timestamp; private String currency; } 

UPDATE

TL;DR: Just add @Id above the attribute if you have one only @PrimaryKeyColumn. If you use more @PrimaryKeyColumn, you have to use:

  • @PrimaryKey and @PrimaryKeyClass (and you can use findById)
public interface TestRepository extends CassandraRepository<Test, KeyClass> { } 
@Data @Table public class Test { @PrimaryKey private KeyClass id; @Data @PrimaryKeyClass public static class KeyClass { // You either give it a different name or move it in another file. Nevermind. @PrimaryKeyColumn(name = "id", type = PrimaryKeyType.PARTITIONED) private Integer id; @PrimaryKeyColumn(name = "data") private Integer data; } } 
  • or next interface:
public interface TestRepository extends CassandraRepository<Test, Integer> { Optional<Test> findByIdAndData(Integer id, Integer data); } 
@Data @Table public class Test { @PrimaryKeyColumn(name = "id", type = PrimaryKeyType.PARTITIONED) private Integer id; @PrimaryKeyColumn(name = "data") private Integer data; } 

With solo @PrimaryKeyColumn

Test data: enter image description here

-- auto-generated definition CREATE TABLE test ( id int PRIMARY KEY, data int ) WITH CACHING = {'keys': 'ALL', 'rows_per_partition': 'NONE'} AND COMPACTION = {'max_threshold': '32', 'min_threshold': '4', 'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy'} AND COMPRESSION = {'class': 'org.apache.cassandra.io.compress.LZ4Compressor', 'chunk_length_in_kb': '64'} AND DCLOCAL_READ_REPAIR_CHANCE = 0.1; 
public interface TestRepository extends CassandraRepository<Test, Integer> { } 
@Data @Table public class Test { /* * You can only use "findById" or "findallById" with @Id annotation. * @PrimaryKey contains the @Id itself, but ofc you have to configuration @PrimaryKeyClass. */ @PrimaryKeyColumn(name = "id", type = PrimaryKeyType.PARTITIONED) @Id // <-- add private Integer id; @Column("data") private Integer data; } 

Test:

@SpringBootTest class ApplicationTests { @Autowired private TestRepository testRepository; @Test void contextLoads() { var x = testRepository.findById(1).orElse(null); if(x == null) { fail(); } assertEquals((int) x.getData(), 5); } } 

Result: enter image description here


With @PrimaryKey Table: enter image description here

public interface TestRepository extends CassandraRepository<Test, KeyClass> { } 
@Data @Table public class Test { @PrimaryKey private KeyClass id; @Data @PrimaryKeyClass public static class KeyClass { // You either give it a different name or move it in another file. Nevermind. @PrimaryKeyColumn(name = "id", type = PrimaryKeyType.PARTITIONED) private Integer id; @PrimaryKeyColumn(name = "data") private Integer data; } } 

enter image description here


With JPA API

The second genetic type (what defined type of id) doesn’t matter what, on this solution.

enter image description here

enter image description here

enter image description here


If you want just query by PARTITION. CLUSTERED is exist!

Solution 1:

The second genetic type (what defined type of id) doesn’t matter what, on this solution.

enter image description here

Solution 2:

Rename id to something else and configure JPA that way.

The second genetic type (what defined type of id) doesn’t matter what, on this solution.

enter image description here

enter image description here

enter image description here

Solution with @PrimaryKeyClass

public interface TestRepository extends CassandraRepository<Test, KeyClass> { // findby[ID-1]_[ID-2](...) // ID-1 is mean: Test's "id" attribute // "_" is mean chain/access instance // ID-2 is mean: KeyClass's "id" attribute Optional<Test> findById_Id(Integer id); // Look this both: Optional<Test> findById_IdAndId_Data(Integer id, Integer data); } 
Sign up to request clarification or add additional context in comments.

3 Comments

Hi, thanks for yor answer. I have tried @Data annotastion with no efect, yes i have installed lombog plugin. Also creating setters and getters method changed nothing.
I have tried in my original implementation remove anotations PrimaryKeyColumn and replace it with PrimaryKey for id field and not anotation for timestamp field. It worked, but i dont know, if it is good approach. Documentation says that my original implementation is correct if i get it right. Also i have another entity where is two PARTITIONED keys and one CLUSTERED key and it work just fine.
Nevermind what type of CLUSTERED field. It can be Instant.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.