Cassandra Accessor / Mapper not mapping udt field - cassandra

I am using datastax cassandra 3.1.2. I have created the following table in cassandra and inserted a record.
CREATE TYPE memory ( capacity text );
create TABLE laptop ( id uuid primary key, model text, ram frozen<memory> );
select * from laptop ;
id | model | ram
--------------------------------------+---------------+-------------------
e55cba2b-0847-40d5-ad56-ae97e793dc3e | Dell Latitude | {capacity: '8gb'}
When I am trying to fetch the capacity field from frozen type memory in Java using Cassandra Accessor with the below code:
this.cluster = Cluster.builder().addContactPoint(node).withPort(port).build();
session = cluster.connect();
MappingManager manager = new MappingManager(session);
LaptopAccessor laptopAccessor = manager.createAccessor(LaptopAccessor.class);
Result<Laptop> cp = laptopAccessor.getOne(UUID.fromString("e55cba2b-0847-40d5-ad56-ae97e793dc3e"));
System.out.println(cp.one());
It is giving ram datapoint itself is null.
id = null model = null ram = null
I was expecting that the mapper would create ram instance while mapping and map capacity field into it and return the Laptop bean.
I have the following Accessor interface:
#Accessor
interface LaptopAccessor {
#Query("SELECT ram.capacity FROM user_info.laptop where id=?")
Result<Laptop> getOne(UUID id);
}
I have the following java beans for the above table.
#Table(keyspace = "user_info", name = "laptop")
public class Laptop {
private UUID id;
private String model;
private Memory ram;
#PartitionKey
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
#Frozen
public Memory getRam() {
return ram;
}
public void setRam(Memory ram) {
this.ram = ram;
}
#Override
public String toString() {
return "id = " + id + " model = " + model + " ram = " + ram;
}
}
#UDT(keyspace = "user_info", name = "memory")
public class Memory {
private String capacity;
#Field
public String getCapacity() {
return capacity;
}
public void setCapacity(String capacity) {
this.capacity = capacity;
}
#Override
public String toString() {
return "capacity = " + capacity ;
}
}
The code works fine when I change the query to retrieve entire ram UDT. Could somebody please tell that why the mapper doesn't work when I select some field from the udt in the query?
Doesn't cassandra support this? Any workaround to fetch the UDT fields?

I think the issue is the return type on your accessor:
#Accessor
interface LaptopAccessor {
#Query("SELECT ram.capacity FROM user_info.laptop where id=?")
Result<Laptop> getOne(UUID id);
}
Since your query is only selecting ram.capacity, all the driver is getting back is a Row with a single column that is a String with a name of ram.capacity which does not map to any field in Laptop.
Instead, since it looks like all you want is the 1 row matching that query, you could change your Accessor to:
#Accessor
interface LaptopAccessor {
#Query("SELECT ram.capacity FROM user_info.laptop where id=?")
ResultSet getOne(UUID id);
}
The accessor now returns a ResultSet for which you can call one().getString(0) to get the capacity back. It's not ideal if you don't want to deal with ResultSet directly, but works well.
You shouldn't really need the whole Laptop object anyways since all you are requesting is a field of a UDT right?

Related

How to persist PanacheEntity avoiding duplicate key exception?

I want to persist an entity. I want to skip it in case it already exists in the datastore. Assume the name field is part of the primary key. Assume p1 exists in the datastore. Only p2 should be inserted. Inserting p1 produces duplicate key exception.
#Entity
public class PersonEntity extends PanacheEntity {
String name;
public PersonEntity(String name){
this.name=name;
}
public static Uni<PersonEntity> findByName(String name) {
return find("name", name).firstResult();
}
}
#QuarkusTest
public class PersonResourceTest {
#Test
#ReactiveTransactional
void persistListOfPersons() {
List<PersonEntity> persons = List.of(new PersonEntity("p1"), new PersonEntity("p2"));
Predicate<PersonEntity> personExists = entity -> {
//How to consume Uni?
Uni<PersonEntity> entityUni = PersonEntity.findByName(entity.name);
//entityUni.onItem().ifNull().continueWith(???);
//include entity in filtered stream
//return true;
//exclude entity from filtered stream
return false;
};
List<PersonEntity> filteredPersons = persons.stream().filter(personExists).toList();
PersonEntity.persist(filteredPersons);
}
}
I can't produce a valid filter predicate. I need a boolean value somehow produced by the person query. But how?
This should serve as a minimum reproducable example.

Datastax mapper complains about duplicate columns in generated insert statement

Versions: Datastax Java driver 3.1.4, Cassandra 3.10
Consider the following table:
create table object_ta
(
objid bigint,
version_date timestamp,
objecttype ascii,
primary key (objid, version_date)
);
And a mapped class:
#Table(name = "object_ta")
public class ObjectTa
{
#Column(name = "objid")
private long objid;
#Column(name = "version_date")
private Instant versionDate;
#Column(name = "objecttype")
private String objectType;
public ObjectTa()
{
}
public ObjectTa(long objid)
{
this.objid = objid;
this.versionDate = Instant.now();
}
public long getObjId()
{
return objid;
}
public void setObjId(long objid)
{
this.objid = objid;
}
public Instant getVersionDate()
{
return versionDate;
}
public void setVersionDate(Instant versionDate)
{
this.versionDate = versionDate;
}
public String getObjectType()
{
return objectType;
}
public void setObjectType(String objectType)
{
this.objectType = objectType;
}
}
After creating a mapper for this class (mm is a MappingManager for the session on mykeyspace)
final Mapper<ObjectTa> mapper = mm.mapper(ObjectTa.class);
On calling
mapper.save(new ObjectTa(1));
I get
Query preparation failed: INSERT INTO mykeyspace.object_ta
(objid,objid,version_date,objecttype) VALUES (?,?,?,?);:
com.datastax.driver.core.exceptions.InvalidQueryException: The column
names contains duplicates at
com.datastax.driver.core.Responses$Error.asException(Responses.java:136)
at
com.datastax.driver.core.SessionManager$4.apply(SessionManager.java:220)
at
com.datastax.driver.core.SessionManager$4.apply(SessionManager.java:196)
at
com.google.common.util.concurrent.Futures$ChainingListenableFuture.run(Futures.java:906)
at
com.google.common.util.concurrent.Futures$1$1.run(Futures.java:635)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at
io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Thread.java:745)
I am at a loss to understand, why the duplicate objid is generated in the query.
Thank you in advance for pointers to the problem.
Clemens
I think it is because the inconsistent use of case on the field name (objid) vs the setter/getters (getObjId). If you rename getObjId and setObjId to getObjid and setObjid respectively, I believe it might work.
In a future release, the driver mapper will allow the user to be more explicit about whether setters/getters are used (JAVA-1310) and what the naming conventions are (JAVA-1316).

Cassandra UDT mapping issue

I am not able to map data which has UDT type.
The table is definition is the following.
CREATE TABLE IF NOT EXISTS members_data.Test(
priority int,
name text,
test_links list<frozen<TestLinks>>,
PRIMARY KEY(name)
);
The model is the following.
#JsonAutoDetect
#JsonSerialize
#Table(keyspace="members_data", caseSensitiveKeyspace=false, caseSensitiveTable=false, name="Test")
public class Test{
#Column(name="name", caseSensitive=false)
private String name;
#Column(name="priority", caseSensitive=false)
private int priority;
#Frozen
#Column(name="test_links", caseSensitive=false)
private List<TestLinks> test_links;
#JsonAutoDetect
#JsonSerialize
#UDT(keyspace = "members_data", name = "Testlinks")
public class TestLinks {
#Field(name = "test_link")
private String test_link;
#Field(name = "link_title")
private String link_title;
The mapper usage.
MappingManager manager = new MappingManager(sessionManager.getSession());
manager.udtCodec(TestLinks.class);
Mapper<Test> mapper = manager.mapper(Test.class);
Result<Test> result = mapper.map(testResultSet);
test = result.one(); //test object would be null here
the cassandra-driver-mapping is 3.1.0.
Mapper is not throwing any error and now even mapping data to model. Could someone tell me what is the problem?

NullPointerException in Custom Dstinct Mapper

i am using hazelcast 3.6.1 and implementing distinct aggregate functionality using custom mapreduce to get solr facet kind of results.
public class DistinctMapper implements Mapper<String, Employee, String, Long>{
private transient SimpleEntry<String, Employee> entry = new SimpleEntry<String, Employee>();
private static final Long ONE = Long.valueOf(1L);
private Supplier<String, Employee, String> supplier;
public DistinctMapper(Supplier<String, Employee, String> supplier) {
this.supplier = supplier;
}
#Override
public void map(String key, Employee value, Context<String, Long> context) {
System.out.println("Object "+ entry + " and key "+key);
entry.setKey(key);
entry.setValue(value);
String fieldValue = (String) supplier.apply(entry);
//getValue(value, fieldName);
if (null != fieldValue){
context.emit(fieldValue, ONE);
}
}
}
and mapper is failing with NullPointerException. and sysout statement says entry object is null.
SimpleEntry : https://github.com/hazelcast/hazelcast/blob/v3.7-EA/hazelcast/src/main/java/com/hazelcast/mapreduce/aggregation/impl/SimpleEntry.java
Can you point me the issue in the above code ? Thanks.
entry field is transient. This means that it is not serialized, so when DistinctMapperobject is deserialized on hazecalst node, it's value is null.
Removing the transient will solve the NullPointerException.
On the side note:
Why do you need this entry field? It doesn't seem to have any use.

How to convert the cassandra row to my java class?

Here is [a Link](http://stackoverflow.com/questions/32448987/how-to-retrieve-a-very-big-cassandra-table-and-delete-some-unuse-data-from-it#comment52844466_32464409) of my question before.
After I get the cassandra data row by row in my program, I'm confused by the convert between cassandra row to java class. In java class the table of cassandra is convert to a ResultSet class,when I iterator it and get the row data,it returns a NPE. In fact,I can see the Object (or the data) while debuging the program. Here is My Iterator Code:
ResultSet rs=CassandraTools.getInstance().execute(cql);
Iterator<Row> iterator = rs.iterator();
while (iterator.hasNext()) {
Row row = iterator.next();
row.getString() ---->return NPE
The CassandraTools class is:
public class CassandraTools {
private static CassandraTools instance;
private CassandraTools() {
}
public static synchronized CassandraTools getInstance() {
if (instance == null) {
instance = new CassandraTools();
instance.init();
}
return instance;
}
Cluster cluster;
Session session;
public void init() {
if (cluster == null) {
cluster = new Cluster.Builder().addContactPoint("10.16.34.96")
.build();
if (session == null) {
session = cluster.connect("uc_passport");
}
}
}
public ResultSet execute(String cql) {
ResultSet rs = session.execute(cql);
// rs.forEach(n -> {
// System.out.println(n);
// });
return rs;
}
}
SO how could I convert the data in the row to A java Class?I have read the convert class in the API of spring data cassandra,but it is complicated to use for me. Who can help?
IMHO, If you want to map the rows of Cassandra to a java class, you should try to use an Object-Datastore mapper which does these things for you.
If you try to do this by yourself, you need to handle the java-cassandra datatype mappings, validations etc all by yourself which is very hectic job.
There are few (Kundera, Hibernate OGM, etc) opensource object-datastore mappers available and you can use them. I suggest you to try Kundera and check this for getting started with Cassandra.

Resources