doanduyhai / Achilles

An advanced Java Object Mapper/Query DSL generator for Cassandra
http://achilles.archinnov.info
Apache License 2.0
241 stars 92 forks source link

UDT validation fails if the order of fields in the UDT class is different than what was used during table creation #346

Closed wittyameta closed 5 years ago

wittyameta commented 5 years ago
@Data
@UDT(name = "test")
public class Test {
  private String name;
  private UUID id;
}

@Data
@Table
public class TestTable {
  @Column
  @Frozen
  private Test test;
  @PartitionKey
  private String id;
}

CQL query: CREATE TYPE test (id uuid,name text);

This gives exception: Caused by: info.archinnov.achilles.exception.AchillesBeanMappingException: Data type 'frozen<ks.test>' for column 'test' of entity 'class TestTable' does not match type in live schema 'ks.test'

This works fine if I re-order the fields in the UDT class.

@Data
@UDT(name = "test")
public class Test {
  private UUID id;
  private String name;
}
doanduyhai commented 5 years ago

What version of Achilles are you using ?

wittyameta commented 5 years ago

5.3.1

doanduyhai commented 5 years ago

Just check the source code and the error message comes from here: https://github.com/doanduyhai/Achilles/blob/master/achilles-core/src/main/java/info/archinnov/achilles/internals/schema/SchemaValidator.java#L118-L122

            final DataType runtimeType = columnMeta.getType();
            final DataType staticType = x.buildType(Optional.empty());
            validateBeanMappingTrue(runtimeType.equals(staticType),
                    "Data type '%s' for column '%s' of entity '%s' does not match type in live schema '%s'",
                    staticType, cqlColumn, entityClass, runtimeType);

Now, since we're talking about UDT, DataType == UserType at runtime

When looking at the way the Java driver core 3.5.0 implements their UserType.equals() method:

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof UserType))
            return false;

        UserType other = (UserType) o;

        // Note: we don't test byName because it's redundant with byIdx in practice,
        // but also because the map holds 'int[]' which don't have proper equal.
        return name.equals(other.name)
                && keyspace.equals(other.keyspace)
                && typeName.equals(other.typeName)
                && Arrays.equals(byIdx, other.byIdx);
    }

By looking at the definition of the attribute byIdx:

    // Note that we don't expose the order of fields, from an API perspective this is a map
    // of String->Field, but internally we care about the order because the serialization format
    // of UDT expects a particular order.
    final Field[] byIdx;

It is said clearly that UDT by design (and by specs) does care about the order of the declared field.

For me there is nothing wrong from Achilles side, it works as designed.

wittyameta commented 5 years ago

thanks! Will check with datastax.