Cassandra Db insertion issue Via springboot - java

Am trying to insert values into my Cassandra database but am getting this error. Referred to data type mapping here everything seems to be ok... http://itdoc.hitachi.co.jp/manuals/3020/30203V0300e/BV030040.HTM
reactor.core.Exceptions$ErrorCallbackNotImplemented:
com.datastax.oss.driver.api.core.type.codec.CodecNotFoundException:
Codec not found for requested operation: [UUID <-> java.lang.String]
Caused by:
com.datastax.oss.driver.api.core.type.codec.CodecNotFoundException:
Codec not found for requested operation: [UUID <-> java.lang.String]
Here is the entity object
package com.sams.pricing.prism.data.processor.entity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.cassandra.core.cql.PrimaryKeyType;
import org.springframework.data.cassandra.core.mapping.Column;
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.core.mapping.Table;
import java.sql.Timestamp;
//import java.time.Instant;
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
#Table(value = "retail_lifecycle")
#EntityScan
#ToString
public class PricingLifeCycleEntity {
#PrimaryKeyColumn(name = "itemnbr", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
private Integer itemNbr;
#PrimaryKeyColumn(name = "clubnbr", ordinal = 1, type = PrimaryKeyType.CLUSTERED)
private Short locationNbr;
#PrimaryKeyColumn(name = "price", ordinal = 2, type = PrimaryKeyType.PARTITIONED)
private Double price;
#PrimaryKeyColumn(name = "pricestartts", ordinal = 3, type = PrimaryKeyType.CLUSTERED)
private Timestamp priceStartTs;
#PrimaryKeyColumn(name = "status", ordinal = 4, type = PrimaryKeyType.PARTITIONED)
private String status;
#Column(value = "createts")
private Timestamp createTs;
#Column(value = "currency")
private String currency;
#Column(value = "lastupdatets")
private Timestamp lastUpdateTs;
#Column(value = "priceendts")
private Timestamp priceEndTs;
#Column(value = "pricereasoncode")
private String priceReasonCode;
#Column(value = "pricereasoncodedesc")
private String priceReasonCodeDesc;
#Column(value = "pricetype")
private String priceType;
#Column(value = "pricetypedesc")
private String priceTypeDesc;
#Column(value = "statusdesc")
private String statusDesc;
#Column(value = "submissionclient")
private String submissionClient;
#Column(value = "submissionts")
private Timestamp submissionTs;
#Column(value = "submissionuserid")
private String submissionUserId;
#Column(value = "uniqueid")
private String uniqueId;
}
Here is the database
CREATE TABLE cassandra.prism.retail_lifecycle (
itemnbr INTEGER NOT NULL,
clubnbr SMALLINT NOT NULL,
price DOUBLE NOT NULL,
pricestartts TYPE_TIMESTAMP NOT NULL,
status VARCHAR NOT NULL,
createts TYPE_TIMESTAMP,
currency VARCHAR,
lastupdatets TYPE_TIMESTAMP,
priceendts TYPE_TIMESTAMP,
pricereasoncode VARCHAR,
pricereasoncodedesc VARCHAR,
pricetype VARCHAR,
pricetypedesc VARCHAR,
statusdesc VARCHAR,
submissionclient VARCHAR,
submissionts TYPE_TIMESTAMP,
submissionuserid VARCHAR,
uniqueid VARCHAR,
CONSTRAINT RETAIL_LIFECYCLE_PK PRIMARY KEY (itemnbr,clubnbr,price,pricestartts,status)
and here is the test case
#Test
public void test2() throws Exception {
MockEndpoint mock =
camelContext.getEndpoint("mock:" + Endpoints.SEDA_PROCESS_ENDPOINT, MockEndpoint.class);
AdviceWith.adviceWith(
camelContext,
Endpoints.SEDA_SEND_ENDPOINT,
// intercepting an exchange on route
r -> {
// replacing consumer with direct component
r.replaceFromWith(Endpoints.SEDA_SEND_ENDPOINT);
// mocking producer
r.mockEndpoints("seda*");
// weaving a last step to your route that will redirect the message to mock:result
r.weaveAddLast().to(mock);
});
var eventId = UUID.randomUUID().toString();
System.out.println("hereeeee "+eventId );
String message = "{\"itemNbr\":8831,\"locationNbr\":4707,\"price\":55.0,\"priceStartTs\":\"2022-11-11T03:24:14.749Z\",\"status\":\"SUCCESS\",\"createTs\":\"2022-11-11T03:24:14.749Z\",\"currency\":\"USD\",\"lastUpdateTs\":\"2022-11-11T03:24:14.749Z\",\"priceEndTs\":\"2022-11-11T03:24:14.749Z\",\"priceReasonCode\":\"TEST\",\"priceReasonCodeDesc\":\"TEST\",\"priceType\":\"TST\",\"priceTypeDesc\":\"TST\",\"statusDesc\":\"TST\",\"submissionClient\":\"TST\",\"submissionTs\":\"2022-11-11T03:24:14.749Z\",\"submissionUserId\":\"TST\",\"uniqueId\":\"121212111212112121212121\"}";
// setting expectations
mock.expectedMessageCount(1);
Map<String, Object> headers = new HashMap<>();
headers.put(PrismConstants.APP_NAME, PrismConstants.PRICING_LIFE_CYCLE_APP_NAME);
headers.put(PrismConstants.EVENT_ID, UUID.randomUUID().toString());
// invoking consumer
// we are going to send as a Json body object
producerTemplate.sendBodyAndHeaders(
Endpoints.SEDA_SEND_ENDPOINT,
message,
headers);
// asserting mock is satisfied
mock.assertIsSatisfied();
var result = mock.getExchanges().get(0).getIn();
System.out.println(result.getBody().toString());
}
Edit - DB info and schema update
Primary Key entity class
package com.sams.pricing.prism.data.processor.entity;
import lombok.*;
import org.springframework.data.cassandra.core.cql.PrimaryKeyType;
import org.springframework.data.cassandra.core.mapping.PrimaryKeyClass;
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn;
import java.sql.Timestamp;
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#PrimaryKeyClass
#ToString
public class PricingLifeCyclePrimaryKey {
#PrimaryKeyColumn(name = "itemnbr", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
private Integer itemNumber;
#PrimaryKeyColumn(name = "clubnbr", ordinal = 1, type = PrimaryKeyType.CLUSTERED)
private Short locationNumber;
#PrimaryKeyColumn(name = "price", ordinal = 2, type = PrimaryKeyType.PARTITIONED)
private Double price;
#PrimaryKeyColumn(name = "pricestartts", ordinal = 3, type = PrimaryKeyType.CLUSTERED)
private Timestamp pricestartTs;
#PrimaryKeyColumn(name = "status", ordinal = 4, type = PrimaryKeyType.PARTITIONED)
private String status;
}
Schema code for primary key

Notice that:
In your test2 you are not using Cassandra what so ever, it would be valuable if you can add the associated code.
The table schema is still unclear as you share screenshots of a JDBC tools maybe. The best would be to get the output of describe table retail_lifecycle using cqlsh to have the detailed schema.
Error 1: CodecNotFoundException
reactor.core.Exceptions$ErrorCallbackNotImplemented:
com.datastax.oss.driver.api.core.type.codec.CodecNotFoundException:
Codec not found for requested operation: [UUID <-> java.lang.String]...
This message means that in your Java Entity there is an attribute with invalid type:
either the java field is as an UUID whereas the column in the databases was rather a TEXT or a VARCHAR.
or the java field is as String and the expected column is an UUID.
Error 2: InvalidQueryException
table retail_lifecycle does not exist; nested exception is com.datastax.oss.driver.api.core.servererrors.InvalidQueryException: table retail_lifecycle does not exist
This message tells you that the table your want to write does not exist. Either the table retail_lifecycle does not exist at all or you may have not provide a keyspace in the CqlSession definition.

Fixed it by manually sending in an event ID but now getting this error - looked like it was a type mismatch.
reactor.core.Exceptions$ErrorCallbackNotImplemented:
org.springframework.data.cassandra.CassandraInvalidQueryException:
ReactivePreparedStatementCallback; CQL [INSERT INTO retail_lifecycle
(itemnbr,price,status,clubnbr,pricestartts,createts,currency,lastupdatets,priceendts,pricereasoncode,pricereasoncodedesc,pricetype,pricetypedesc,statusdesc,submissionclient,submissionts,submissionuserid,uniqueid)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)]; table retail_lifecycle
does not exist; nested exception is
com.datastax.oss.driver.api.core.servererrors.InvalidQueryException:
table retail_lifecycle does not exist Caused by:
org.springframework.data.cassandra.CassandraInvalidQueryException:
ReactivePreparedStatementCallback; CQL [INSERT INTO retail_lifecycle
(itemnbr,price,status,clubnbr,pricestartts,createts,currency,lastupdatets,priceendts,pricereasoncode,pricereasoncodedesc,pricetype,pricetypedesc,statusdesc,submissionclient,submissionts,submissionuserid,uniqueid)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)]; table retail_lifecycle
does not exist; nested exception is
com.datastax.oss.driver.api.core.servererrors.InvalidQueryException:
table retail_lifecycle does not exist

Related

How to save collection of user defined type to ScyllaDB with Spring Data Cassandra?

When I try to save entity with a list or set of user defined type, I get error:
Failed to convert from type [java.util.ImmutableCollections$Set12<?>] to type [com.datastax.oss.driver.api.core.data.UdtValue] for value '[scrubbed.entity.Article$AttachmentInfo#3337c2b5]'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [scrubbed.entity.Article$AttachmentInfo] to type [com.datastax.oss.driver.api.core.data.UdtValue]]
That's the definition that is in ScyllaDB database:
CREATE TYPE IF NOT EXISTS attachment_info (
id UUID,
fileName TEXT,
mimeType TEXT,
sizeBytes BIGINT
);
CREATE TABLE IF NOT EXISTS articles
(
id UUID,
attachments SET<frozen<attachment_info>>,
PRIMARY KEY (id)
);
Entity in Java:
#Getter
#Setter
#Table("articles")
#NoArgsConstructor
#AllArgsConstructor
#Builder(toBuilder = true)
public class Article {
#PrimaryKeyColumn(name = "id", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
private UUID id;
#Column("attachments")
#CassandraType(type = CassandraType.Name.UDT, userTypeName = "attachment_info")
private Set<AttachmentInfo> attachments = new HashSet<>();
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#UserDefinedType("attachment_info")
public static class AttachmentInfo {
private UUID id;
private String fileName;
private String mimeType;
private Long sizeBytes;
}
}
And saving to database:
var metadata = new Article.AttachmentInfo();
metadata.setFileName("Filename");
metadata.setId(UUID.randomUUID());
metadata.setMimeType("image/png");
metadata.setSizeBytes(12345L);
var article = new Article();
article.setId(UUID.randomUUID());
article.setAttachments(Set.of(metadata));
articleRepository.insert(article);
Using version 3.2.0 of spring-data-cassandra
I'm not a Spring expert (just a Scylla expert ;-)), so please forgive me if my answer doesn't work.
It seems to me that the annotation
#Column("attachments")
#CassandraType(type = CassandraType.Name.UDT, userTypeName = "attachment_info")
private Set<AttachmentInfo> attachments = new HashSet<>();
Is wrong because it tells spring-data-cassandra that the "attachments" column is a UDT, while in fact it isn't - it's a set (of UDTs).
I think that the correct annotation for a set of UDTs would be something like:
#CassandraType(type = DataType.Name.SET, typeArguments = DataType.Name.UDT)
But I'm not sure (I couldn't find an example in documentation) where to put the userTypeName. If I understand the documentation correctly, you don't need the that userTypeName explicitly because you annotated the AttachmentInfo class with a UserDefinedType annotation saying that its scylla name is "attachment_info" and that should hopefully be enough.
Moreover, according to some documentation I read, you may not need the CassandraType annotation on attachements at all, because spring-data-cassandra understands Set<AttachmentInfo> because it knows the primitive type Set, and you already annotated AttachmentInfo as a UDT.

Mapping MapOf - UserDefinedType to POJO in cassandra using Spring Data Cassandra

I have the following user defined type in cassandra:
CREATE TYPE key(
name text,
created_time timestamp
);
It goes in the following table:
CREATE TABLE keys(
id uuid,
keys map<text, frozen<list<key>>>
);
** Note that the below POJOS also contain setters/getters, snipped for readability**
Then I have the following POJO for mapping for key:
#UserDefinedType(value = "key")
public class MyKey {
#Column(value = "name")
private String name;
#Column(value = "created_time")
private Instant createdTime;
}
I have the following POJO for keys
#Table(value = "keys")
public class MyKeys {
#PrimaryKey(value = "id")
private UUID id;
#Column(value = "keys")
private Map<String, List<Key>> keys;
}
Unfortunately when I try to insert an instance of MyKeys I get the following error:
org.springframework.dao.InvalidDataAccessApiUsageException: Only primitive types are allowed inside Collections for property [keys] of type [interface java.util.Map] in entity [com.utx.dao.tables.MyKeys]
Does the Spring-Data-Cassandra mapper not work with maps of user defined types, and if not, is there a solution to my problem?

Using complex data structures in spring data for Cassandra

I'm working on setting up a DAO for Cassandra in spring.
Now I have a question regarding using composite classes multiple times in an object.
I have this class Setting:
#Table(value = "settings")
public class Setting {
#PrimaryKey
private User owner;
#Column("key")
private String key;
#Column("value")
private String value;
#Column("updated_by")
private User updatedBy;
}
And the class User:
#PrimaryKeyClass
public class User implements Serializable{
#PrimaryKeyColumn(name = "userId", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
private String id;
#PrimaryKeyColumn(name = "userIdType", ordinal = 1, type = PrimaryKeyType.PARTITIONED)
private String idType;
}
So I'm using the class User twice. Once as primary key "owner" and once as "updatedBy".
When using this in the CassandraOperations classes, it works fine as the primary key, but not again as another column.
It complains about column name userId already being used. Makes sense.
So how can I make this work?
I could use UserDefined Types perhaps?
CREATE TYPE test_keyspace.user (
id text,
id_type text
);
But how can I do that from java Annotations?
Or, how can I reuse the same class otherwise?
Considering the relatively simple data structures in Cassandra, I am ok with flattening the User class as a single String like idType.id too.
Thanks for any help!
Ok, found it here, answered by denzal.
My classes now look like:
#Table("settings")
public class Setting {
#PrimaryKeyColumn(name = "owner", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
#CassandraType(type = DataType.Name.UDT, userTypeName = "user_type")
private User owner;
#PrimaryKeyColumn(name = "key", ordinal = 1, type = PrimaryKeyType.CLUSTERED)
#CassandraType(type = DataType.Name.TEXT)
private String key;
#CassandraType(type = DataType.Name.TEXT)
private String value;
#CassandraType(type = DataType.Name.UDT, userTypeName = "user_type")
private User lastUpdatedBy;
}
And User Class:
#UserDefinedType("user_type")
public class User implements Serializable{
#CassandraType(type = DataType.Name.TEXT)
private String id;
#CassandraType(type = DataType.Name.TEXT)
private IdType idType;
}
Works nicely.

Applying UDT on Spring Data

I have created custom defined types in cassandra and I'm trying to insert the data using spring. At first i thought using entity like this would work:
#PrimaryKeyColumn(name = "appid", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
private String appid;
#PrimaryKeyColumn(name = "event", ordinal = 1, type = PrimaryKeyType.PARTITIONED, ordering = Ordering.ASCENDING)
private String event;
#PrimaryKeyColumn(name = "date", ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING)
private Date date;
#PrimaryKeyColumn(name = "userid", ordinal = 3, type = PrimaryKeyType.PARTITIONED)
private String userid;
#Column (name = "items")
#Frozen
private Map<CassandraEventParamModel,CassandraEventStatsModel> items;
And created a UDT in java like this (param from create type):
#UDT(keyspace = "ms_analytics", name = "param")
public class CassandraEventParamModel {
#Field(name = "key")
public String key;
#Field(name = "value")
public String value;
but I kept getting an error which says
Invocation of init method failed; nested exception is org.springframework.data.cassandra.mapping.VerifierMappingExceptions:
Cassandra entities must have the #Table, #Persistent or #PrimaryKeyClass Annotation
I wonder what is the proper way to deliver data using spring-data into cassandra.
Thanks

how to save an object with 'foreign object' in ormlite

This is my database:
CREATE TABLE other (
id integer primary key autoincrement not null,
texto text
);
CREATE TABLE tabela (campoid INTEGER primary key autoincrement not null,
camponome text not null, chave int,
foreign key (chave) references other(id));
and this is my classes:
#DatabaseTable(tableName = "tabela")
public class Bean {
#DatabaseField(columnName = "campoid", generatedId = true)
private int _id;
#DatabaseField(columnName = "camponome")
private String nome;
#DatabaseField(foreign = true, columnName = "chave", canBeNull = false)
private Part chave;
}
and other classe mapped
#DatabaseTable(tableName = "other")
public class Part {
#DatabaseField(generatedId = true, columnName = "id")
private Integer id;
#DatabaseField(columnName = "texto")
private String texto;
}
but when i save an Bean object, the Object 'part' does not save too :(
Helper helper = OpenHelperManager.getHelper(this, Helper.class);
Dao<Bean, Integer> dao = helper.getDao(Bean.class);
Bean firstBean = new Bean();
firstBean.setNome("first be persisted");
Part part = new Part();
part.setTexto("ANY TEXT");
firstBean.setChave(part);
dao.create(firstBean);
in my log:
07-13 00:25:26.602: D/BaseMappedStatement(3796): insert data with statement 'INSERT INTO tabela (camponome ,chave ) VALUES (?,?)' and 2 args, changed 1 rows
any idea?
but when i save an Bean object, the Object 'part' does not save too :(
Right. ORMLite does not by default persist sub-objects when you create an object. You can turn on the foreignAutoCreate = true flag which will do that for you however. See the javadocs for foreignAutoCreate.
Your chave field should be defined then as:
#DatabaseField(foreign = true, columnName = "chave", canBeNull = false,
foreignAutoCreate = true)
private Part chave;
If you want to do it by hand, you should:
partDao.create(firstBean.chave);
beanDao.create(firstBean);
You create the Part in the database first because you need the ID from the Part which will be saved into your bean.

Categories