Mapping resultset to POJO's

612 views
Skip to first unread message

Amol Khanolkar

unread,
Aug 28, 2020, 6:24:05 AM8/28/20
to DataStax Java Driver for Apache Cassandra User Mailing List
Hi All
I am facing issue in converting my records to POJO while reading from cassandra

I have Entity which corresponds to 10 fileds as in my DB

Entity - A

Now I am trying to fetch 3 fields from DB and I want to reuse same entity object

    @Query("Select col1,col2,col3 from table_a where ...")
    PagingIterable<A> getRecords();

I keep getting error "**col4 is not a column in this row**" and so on for other columns
Same query works fine if I change code to ResultSet

I have Entity which corresponds to 10 fileds as in my DB. I am using Drive 4.6 with Objectmapper

Entity - A

Now I am trying to fetch 3 fields from DB and I want to reuse same entity object

    @Query("Select col1,col2,col3 from table_a where ...")
    PagingIterable<A> getRecords();

I keep getting error "**col4 is not a column in this row**" and so on for other columns
Same query works fine if I change code to ResultSet

    @Query("Select col1,col2,col3 from table_a where ...")
    ResultSet getRecords();

Howeer in this case I need to deal with Rows which is not very convenient

What is right approach to ignore other fields and get converted to POJO?

Howeer in this case I need to deal with Rows which is not very convenient

What is right approach to ignore other fields and get converted to POJO?

Regards
Amol

Olivier Michallat

unread,
Aug 28, 2020, 6:16:45 PM8/28/20
to DataStax Java Driver for Apache Cassandra User Mailing List
Hi,

There's currently no way to do partial mappings, all the entity's expected columns must be present. But what you can do is create multiple entities representing different views of the same table, using inheritance to factor the common parts:

@Entity
@CqlName("table_a")
public class ShortRow {
  @PartitionKey private int id;
  private int col1;
  private int col2;
  // getters and setters
}

@Entity
@CqlName("table_a")
public class FullRow extends ShortRow {
  private int col3;
  // getters and setters
}

@Dao
public interface TableADao {
  @Select
  FullRow getFull(int id);

  @Select
  ShortRow getShort(int id);
}
 
With this approach you don't need to write the queries yourself, each method will generate a SELECT for the exact set of columns needed.

Olivier Michallat



--
You received this message because you are subscribed to the Google Groups "DataStax Java Driver for Apache Cassandra User Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to java-driver-us...@lists.datastax.com.
To view this discussion on the web visit https://groups.google.com/a/lists.datastax.com/d/msgid/java-driver-user/252000b4-9668-415e-8377-6a85e77f1964n%40lists.datastax.com.

Ankit Sethi

unread,
Aug 28, 2020, 6:40:11 PM8/28/20
to DataStax Java Driver for Apache Cassandra User Mailing List
Hi Amol,

To share my experience with this, I ended up learning Java Poet: the code generation library used in Datastax 4.x to generate @Dao classes via a custom annotation processor that is invoked before the java-driver-mapper-processor artifact. My code generator works on the @Entity classes to generate a @Dao class with a method that looks something like:

default UnitTestTable getPartial(GettableByName source, Set<String> columnsPresent) {
UnitTestTable returnValue = new UnitTestTable()
for (String s : columnsPresent) {

if (s.equalsIgnoreCase("prop_one")) {
returnValue.setPropOne(source.get("prop_one"), String.class);
continue;
}

if (s.equalsIgnoreCase("prop_two")) {
returnValue.setPropTwo(source.get("prop_two"), Double.class);
continue;
}

...
}
return returnValue;
}

Admittedly, this would be severe overkill for many contexts; I mostly did it to take a crack at using the JavaPoet library. Additionally, if you have thousands of rows in your GettableByName and it's a very wide table, the cost of all those if checks will also add up. They are necessary because you cannot speculatively do source.get() on every column and set a null worst-case; the GettableByName method throws an exception. 

At the end of the day, it certainly works to retrieve an arbitrary subset of properties. Whether this is wise is another question!

Best,
Ankit

Amol Khanolkar

unread,
Aug 29, 2020, 9:34:17 AM8/29/20
to java-dri...@lists.datastax.com
Thanks a lot for your inputs

I think definih different POJO per each query works for me. However I see one warning printed by datastax driver

There is no ks.table or UDT: dev.tableA_mapper for the entity class: com.test.mapper.tableAMapper or metadata is out of date.

 Is there any way to avoid this warning? I see that datastax is internally treating each pojo as entity and trying to fetch metadata for the same from DB

Regards
Amol


Olivier Michallat

unread,
Aug 29, 2020, 11:25:13 AM8/29/20
to DataStax Java Driver for Apache Cassandra User Mailing List
You can disable schema validation when you build the mapper: https://docs.datastax.com/en/developer/java-driver/4.8/manual/mapper/mapper/#schema-validation

But the warning indicates a real problem: the table name guessed by the mapper does not exist, so if you build your DAO without passing a table name explicitly, the queries are guaranteed to fail. You might need to add some naming annotations if you want your entity to map to the correct table by default: https://docs.datastax.com/en/developer/java-driver/4.8/manual/mapper/entities/#naming-strategy

Olivier Michallat


Amol Khanolkar

unread,
Aug 31, 2020, 1:28:15 AM8/31/20
to java-dri...@lists.datastax.com
I am creating additional POJO which dont directly map to entity and mapping them using @Query annotation

I was wondering if there is a way for me to tell driver this is POJO and dont treat it like entity

Regards
Amol

Olivier Michallat

unread,
Aug 31, 2020, 11:26:29 AM8/31/20
to DataStax Java Driver for Apache Cassandra User Mailing List
Not with @Query. But if you use @QueryProvider, you can use EntityHelper.get to map any arbitrary row to your POJO.

Olivier Michallat


Amol Khanolkar

unread,
Aug 31, 2020, 11:27:58 AM8/31/20
to java-dri...@lists.datastax.com
Thanks that's what my original question was about.. are there any online example I can refer too.

Olivier Michallat

unread,
Aug 31, 2020, 2:03:30 PM8/31/20
to DataStax Java Driver for Apache Cassandra User Mailing List
It's pretty straightforward to adapt the code examples from the @QueryProvider docs I linked in my previous message. The only difference is that your query is a hard-coded string, so you wouldn't need the query builder.

We also have a full example project on GitHub: DataStax-Examples/object-mapper-java. It uses a few query providers, for example LoginQueryProvider, used by UserDao.login(). It has a bit more custom logic, but the principle is the same.

One thing I forgot to mention earlier: you'll still have to annotate your POJO with @Entity if you want the mapper to generate an EntityHelper implementation. And yes, that will generate a schema check and produce a warning because there is no table or UDT with that name. Currently we don't offer a way to skip validation for a specific entity, it's either all or nothing. It would be relatively easy to skip validation based on an entity-level annotation, I've created JAVA-2877 to add that.


Olivier Michallat


Amol Khanolkar

unread,
Aug 31, 2020, 2:10:28 PM8/31/20
to java-dri...@lists.datastax.com
Yeah I was looking for example where I I am fetching 3-4 nested fields from UDT and I want the to be mapped to POJO. I guess currently it's not possible. So my POJO fields do not directly map to table columns but are kind of derived from the table .

It would be great to have simple row mapper which can look at column definition from result set and just map it to POJO field purely by reflection and return Pojo or list of pojo


Regards
Amol

Olivier Michallat

unread,
Sep 2, 2020, 8:37:29 PM9/2/20
to DataStax Java Driver for Apache Cassandra User Mailing List
If all the fields belong to the same UDT, it's possible.

CREATE TYPE udt(a int, b int, c int);
CREATE TABLE t(id int PRIMARY KEY, u udt);

Here's an entity that maps to only two fields of the UDT:

@Entity
public class PartialView {
  private int a;
  private int b;
  ... // getters and setters
}

Using a query provider, you can have a DAO method that fetches a row by id, extracts the UDT and maps it:

@Dao
public interface DemoDao {
  @QueryProvider(providerClass = FindByIdProvider.class, entityHelpers = PartialView.class)
  PartialView findById(int id);
}

The provider implementation:

import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.*;

public class FindByIdProvider {

  private static final CqlIdentifier TABLE_ID = CqlIdentifier.fromCql("t");

  private final CqlSession session;
  private final EntityHelper<PartialView> helper;
  private final PreparedStatement statement;

  public FindByIdProvider(MapperContext context, EntityHelper<PartialView> helper) {
    session = context.getSession();
    // SELECT u FROM t WHERE id = ?
    String query =
        selectFrom(context.getKeyspaceId(), TABLE_ID)
            .column("u")
            .whereColumn("id").isEqualTo(bindMarker())
            .asCql();
    statement = session.prepare(query);
    this.helper = helper;
  }

  public PartialView findById(int id) {
    Row row = session.execute(statement.bind(id)).one();
    UdtValue u;
    if (row == null || (u = row.getUdtValue("u")) == null) {
      return null;
    } else {
      return helper.get(u);
    }
  }
}


If you already have a row from a non-mapped query and you just need the mapping logic, you can define a GetEntity method on your DAO. This gives you direct access to EntityHelper.get:

  @GetEntity
  PartialView asPartialView(UdtValue value);


If the fields come from different UDTs, or you have a mix of top-level columns and UDT fields, EntityHelper.get won't work. You could still write a query provider, but you would have to manually read the columns and build the entities, so you're not really taking advantage of the mapper anymore.

Olivier Michallat


Reply all
Reply to author
Forward
0 new messages