Search code examples
cassandraspring-dataspring-data-cassandra

How to provide CassandraEntityInformation<T, ID> for a SimpleReactiveCassandraRepository?


I'm doing some research bout spring-data-cassandra-reactive and it looks like I can't figure out or find the right documentation about this.

According to the documentation you can create an interface which is annotated with @Repository and then use annotations to create custom queries that will retrieve the data you want. The code would look something like this:


@Repository
public interface UserRepository
        extends ReactiveCassandraRepository<UserEntity, UUID>
{

    @Query("SELECT u FROM UserEntity u WHERE username = :user ALLOW FILTERING")
    Mono<UserEntity> findUserEntityByUsername(@Param("user") String user);

    /**
     * This is just to illustrate/simulate some custom/advanced logic that cannot be
     * done via @Query()
     */
    default Mono<UserEntity> saveWithBase64EncodedPassword(UserEntity entity)
    {
        String encodedPassword = Base64.getEncoder().encodeToString(entity.getPassword().getBytes());
        entity.updatePassword(encodedPassword);
        return this.save(entity);
    }

}


@Table(UserEntity.TABLE_NAME)
public class UserEntity
{

    public final static String TABLE_NAME = "users";

    @PrimaryKeyColumn(name = "uuid", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
    @CassandraType(type = CassandraType.Name.UUID)
    @Column
    private UUID id;

    @Column
    @CassandraType(type = CassandraType.Name.VARCHAR)
    private String username;

    @Column
    @CassandraType(type = CassandraType.Name.VARCHAR)
    private String password;

    public UserEntity()
    {
        this.id = UUID.randomUUID();
    }

    public UserEntity(String username, String password)
    {
        this.id = UUID.randomUUID();
        this.username = username;
        this.password = password;
    }

    public UUID getId()
    {
        return id;
    }

    public void setId(UUID id)
    {
        this.id = id;
    }

    public String getUsername()
    {
        return username;
    }

    public void setUsername(String username)
    {
        this.username = username;
    }

    public String getPassword()
    {
        return password;
    }

    public void setPassword(String password)
    {
        this.password = password;
    }

    public void updatePassword(String newPassword)
    {
        this.password = newPassword;
    }

}

Dependencies:

plugins {
    id("org.springframework.boot") version "2.6.6"
    id("io.spring.dependency-management") version "1.0.11.RELEASE"
}

dependencies {

    // Embedded Cassandra Server - used for testing.
    implementation("com.github.nosan:embedded-cassandra-spring-boot-starter:4.1.0")

    // Spring Data Cassandra Dependencies
    annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    implementation("org.springframework.boot:spring-boot-starter-data-cassandra-reactive")

    testImplementation("org.springframework.boot:spring-boot-starter-test") {
        exclude("org.junit.vintage:junit-vintage-engine")
        exclude("com.vaadin.external.google:android-json")
    }
    testImplementation("io.projectreactor:reactor-test")
    // /Spring Data Cassandra Dependencies
}

This, so far, works fine. However, I'm trying to figure out how to switch to implementing the interface in order to be able to use @Autowire (i.e. in this example code to autowire
org.springframework.security.crypto.password.PasswordEncoder)

Surfing through the code in the spring-data-cassandra jar I noticed there's a class named org.springframework.data.cassandra.repository.support.SimpleReactiveCassandraRepository which you can extend from. It already has most of the common things you would need implemented for you which is why it looks like the perfect candidate to extend from.

And here comes the problem - it requires CassandraEntityInformation<T, ID> metadata. I cannot seem to find where this is taken from or how it's supposed to be auto-wired and I feel like I might be missing something or maybe a dependency.

Any ideas?


Solution

  • SimpleReactiveCassandraRepository is a great class as it gives you access to ReactiveCassandraOperations and as such to CqlSession. It is a great way to have fine grained operations. (LWT, Bacthes)

    You are correct the different classes would be autowired. Here is a sample code:

    @Repository
    public class OwnerReactiveCassandraRepository extends SimpleReactiveCassandraRepository<OwnerEntitySpring, UUID> {
    
    protected final CqlSession cqlSession;
    protected final ReactiveCassandraOperations reactiveCassandraTemplate;
    
    @SuppressWarnings("unchecked")
    public OwnerReactiveCassandraRepository(CqlSession cqlSession, ReactiveCassandraOperations ops) {
        super(new MappingCassandraEntityInformation<OwnerEntitySpring, UUID>(
                (CassandraPersistentEntity<OwnerEntitySpring>) ops.getConverter().getMappingContext()
                .getRequiredPersistentEntity(OwnerEntitySpring.class), ops.getConverter()), ops);
        this.cqlSession = cqlSession;
        this.reactiveCassandraTemplate = ops;
    }
    
    public Flux<OwnerEntitySpring> searchByOwnerName(String ownerLastName) {
        return reactiveCassandraTemplate.getReactiveCqlOperations()
                                        .query(SimpleStatement
                                            .builder("SELECT * FROM " + TABLE_NAME + " WHERE " + COLUMN_LASTNAME  + "=?")
                                            .addPositionalValues(ownerLastName)
                                            .build(), (row, rownum) -> new OwnerEntitySpring(row));
    }
    

    TLDR; I implemented the Spring PetClinic with Spring data Reactive and you can have access to the full code here It is a step by step workshop with the code. You may want to look specially at this folder

    In the project you will find reactive with the drivers only, reactive with CassandraRepositories and reactive with SimpleCassandraRepositories. (Check the TEST folder)