0

I'm working on mapping two tables with minimal differences in Cassandra, however Kundera is failing to map my model correctly (I have it configured to validate mappings against tables on EntityManager creation). Given the following compound key (structured according to these directions, since paging is desired and additionally using the Datastax Driver:

CQL table creates have the following primary key for both tables:

PRIMARY KEY ((key1, key2, key3, key4, key5, key6, key7, key8, key9, key10, key11), "clusteringKey")

PartitionKey:

@Embeddable
public class PartitionKey {
    @Column
    private key1
    //repeat for 11 more keys
}

ClusteringKey:

@Embeddable
public class ClusteringKey {
    @Embedded
    private PartitionKey key;
    @Column
    private UUID clusteringKey;
}

Properties load for CQL3:

public static EntityManagerFactory getEntityManagerFactory() throws IOException {
    if(entityManagerFactory == null) {
        entityManagerFactory = Persistence.createEntityManagerFactory("cassandra_pu",getProperties());
    }
    return entityManagerFactory;
}

public static Properties getProperties() throws IOException {
    if(properties == null) {
        properties = new Properties();
        properties.load(Application.class.getResourceAsStream("/application.properties"));
        properties.put(CassandraConstants.CQL_VERSION, CassandraConstants.CQL_VERSION_3_0);
    }
    return properties;
}

I have attempted two models so far.

The first case:

SuperRecord:

@MappedSuperClass
public abstract class SuperRecord {
    @EmbeddedId
    private ClusteringKey clusteringkey;
    //Additional Fields
}
//extended by StagingRecord, ProductionRecord

While ClusteringKey itself maps properly, nothing related to PartitionKey maps at all.

In my second attempt:

SuperRecord:

@MappedSuperClass
public abstract class SuperRecord {
    //Common fields excluding keys
}

StagingRecord:

@Entity
public class StagingRecord extends SuperRecord {
    @EmbeddedId
    private ClusteringKey key;
}

ProductionRecord:

@Entity
public class ProductionRecord extends SuperRecord {
    @EmbeddedId
    private ClusteringKey key;

    @Column(name="solr_query")
    private String solrQuery;
}

In this attempt, while my clustering key maps, my partitionkey maps as a singular binary Object, rather than its constituent columns as desired.

What is preventing my PartitionKey from appropriately mapping, and how do I fix it?

Edit:

After distributing the superclass fields, I found that the @MappedSuperclass is not a factor in my issue; only the nested @Embeddeds. Additionally, if I were to merge the PartitionKey and ClusteringKey classes, the mapping will pass validation (though it would fail to correctly build the token method signature in the generated CQL for pagination, since my model no longer matches the expectation for that functionality).

Brandon McKenzie
  • 1,655
  • 11
  • 26

2 Answers2

3

I tried with your first model using following classes.

PartitionKey:

@Embeddable
public class PartitionKey {

    @Column
    private String key1;

    @Column
    private String key2;

    @Column
    private String key3;

    //setters and getters 

}

ClusteringKey:

@Embeddable
public class ClusteringKey {

    @Embedded
    private PartitionKey key;

    @Column
    private UUID clusteringKey;

    //setters and getters 
}

SuperRecord:

@MappedSuperclass
public abstract class SuperRecord
{
    @EmbeddedId
    private ClusteringKey clusteringkey;

    private String additionColumn;

    //setters and getters 
}

ProductionRecord:

@Entity
public class ProductionRecord extends SuperRecord {

    @Column(name="solr_query")
    private String solrQuery;

    //setters and getters 
}

Useful part of testcase:

    Map<String, String> props = new HashMap<>();
    props.put(CassandraConstants.CQL_VERSION, CassandraConstants.CQL_VERSION_3_0);

    emf = Persistence.createEntityManagerFactory("cass_pu", props);
    ProductionRecord pr = new ProductionRecord();
    pr.setSolrQuery("some solr query");
    pr.setAdditionColumn("col1");

    ClusteringKey ck = new ClusteringKey();
    ck.setClusteringKey(UUID.randomUUID());

    PartitionKey pk = new PartitionKey();
    pk.setKey1("k1");
    pk.setKey2("k2");
    pk.setKey3("k3");

    ck.setKey(pk);

    pr.setClusteringkey(ck);

    em.persist(pr);

It's working fine.

Make sure you enabled CQL3.

Dev
  • 13,492
  • 19
  • 81
  • 174
  • How does one check? When launching cqlsh I see: [cqlsh 5.0.1 | Cassandra 2.1.11.908 | DSE 4.8.2 | CQL spec 3.2.1 | **Native protocol v3**]. That means I'm using CQL3, right? The guy managing the cluster says that he thinks that CQL3 is enabled, but I'd like to confirm. – Brandon McKenzie Jun 07 '16 at 14:03
  • Also, added `props.put(CassandraConstants.CQL_VERSION, CassandraConstants.CQL_VERSION_3_0);` to my properties loader. Unfortunately, I got a schema mismatch exception; `ClusteringKey`'s UUID component mapped fine, but `PartitionKey` mapped as a Byte object named `key`, rather than mapping its constituent columns. – Brandon McKenzie Jun 07 '16 at 14:06
  • Are you adding cql version property while creating entity manager factory: `Map props = new HashMap<>(); props.put(CassandraConstants.CQL_VERSION, CassandraConstants.CQL_VERSION_3_0); emf = Persistence.createEntityManagerFactory("cass_pu", props);` – Dev Jun 07 '16 at 14:19
  • Yes, I have added the relevant code to my question. – Brandon McKenzie Jun 07 '16 at 14:24
  • @BrandonMcKenzie Then it should work. I am from Kundera Developer team. You should share your entities, persistence unit and other relevant information at [Kundera-Chat](https://gitter.im/impetus-opensource/Kundera). – Dev Jun 07 '16 at 14:30
  • I popped in and posted my entities, as well as made some edits to my question based on some findings I made toying around with this. Sadly, I've come up against a deadline, so I've switched up my mapping to one that passes validation and used native queries and token() to page through results. In any case, thanks for the help! – Brandon McKenzie Jun 08 '16 at 14:50
  • yes @BrandonMcKenzie I checked your comments at gitter. I did not find any issue with the entities and persistence unit. We were not able to replicate your issue at our end. We are also busy with some other projects so not able to communicate quickly. We will try it again and will get back to you with the findings. – Dev Jun 08 '16 at 19:09
  • @BrandonMcKenzie try one thing (_if you find some time_): modify kundera.ddl.auto.prepare property to **create/update**. Let Kundera create tables and insert the record. Make sure you changed the name of Entity or in the `@table` annotation other in insert it will delete tables first. – Dev Jun 09 '16 at 10:03
0

I ran up against a deadline, so I found myself implementing the paging myself; the model ultimately took the form as originally stated in the question: SuperRecord is a @MappedSuperClass for StagingRecord and ProductionRecord. However, I merged ClusteringKey and PartitionKey into a single class that contains all fields, which fixed the mapping issue.

Unfortunately, this meant that I could not take advantage of Kundera's pagination features, as the generated CQL's token() function would only be generated with a single parameter rather than all 11 (which is by design, the model in the question was supposed to result in the correct CQL output).

Ultimately, I implemented paging myself via NativeQueries and the token() function, inserting all necessary fields by hand.

Brandon McKenzie
  • 1,655
  • 11
  • 26