2
votes

How do I extend CassandraRepository in spring-data-cassandra?
I've tried so many different combinations to get this to work, eventually I ended up with below.

User.java

@Table(name = User.TABLE)
public class User {
  private static final String TABLE = "user";

  @PartitionKey
  private UUID id;

  @Column
  private String name;

  // Getter setter methods
  ...
}

CassandraRepositoryCustom.java

@NoRepositoryBean
public interface CassandraRepositoryCustom<T> extends CassandraRepository<T> {
  public T save(T t, Options options);
}

CassandraRepositoryImpl.java

public class CassandraRepositoryImpl<T> 
 extends SimpleCassandraRepository<T, MapId> 
 implements CassandraRepositoryCustom<T> {

  public CassandraRepositoryImpl(CassandraEntityInformation<T, MapId> metadata, 
                                 CassandraOperations operations) {
    super(metadata, operations);
  }

  @Override
  public T save(T t, Options options) {
    ...
  }
}

UserRepositoryCustom.java

public interface UserRepositoryCustom extends CassandraRepositoryCustom<User> {
  User findById(UUID id);
}

UserRepositoryImpl.java

public class UserRepositoryImpl 
 extends CassandraRepositoryImpl<User> 
 implements CassandraRepositoryCustom<User> {

    public UserRepositoryImpl(CassandraEntityInformation<T, MapId> metadata, 
                              CassandraOperations operations) {
      super(metadata, operations);
  }

}

UserDao.java

@Component
public class UserDao {

  @Autowired
  private UserRepositoryCustom repo;

  public save(User user, Options options) {
    repo.save(user, options);
  }

  public getUser(UUID id) {
    repo.findById(id);
  }

}

CassandraConfig.java

@Configuration
@EnableCassandraRepositories(repositoryBaseClass = CassandraRepositoryImpl.class)
public class CassandraConfig {
  ...
}

Now all I'm met with is the following exception

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepositoryCustom': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property save found for type User!

1

1 Answers

2
votes

You can customize Spring Data repositories in two ways:

  1. Adding one or more custom methods to all repositories
  2. Adding one or more custom methods to single repositories

From the code above I'm deducing that you're trying to add methods that should be available for multiple repositories. The reference documentation contains a guide how to customize single repositories.

Adding one or more custom methods to all repositories

Adding a method to all repositories requires you to follow four steps. This approach is handy if you want to provide a generic method that can be accessed from multiple repositories, such as your example with save(T, Options).

  1. Adding a base class that extends SimpleCassandraRepository containing the methods you're planning to expose.

    public class MyCustomizedCassandraRepository<T>
                                extends SimpleCassandraRepository<T, MapId>
                                implements MyCassandraRepository<T> {
    
        public MyCustomizedCassandraRepository(CassandraEntityInformation<T, MapId> metadata,
                                                        CassandraOperations operations) {
            super(metadata, operations);
        }
    
        public T save(T entity, WriteOptions options) {
    
            System.out.println(String.format("Called MyCustomizedCassandraRepository.save(%s, %s)", 
                                                 entity, options));
            return entity;
        }
    }
    
  2. Configure the base class in @EnableCassandraRepositories

    @SpringBootApplication
    @EnableCassandraRepositories(repositoryBaseClass = MyCustomizedCassandraRepository.class)
    public class DemoApplication {
        // ...
    }
    
  3. Now create an interface that declares the method you're exposing. Doing so allows you rather extend from the base interface instead of declaring the method on each repository but that's totally up to you.

    @NoRepositoryBean
    public interface MyCassandraRepository<T> extends CassandraRepository<T> {
    
        /**
         * Customized save method.
         */
         T save(T entity, WriteOptions options);
    }
    
  4. Declare your repository interfaces by extending your base interface

    interface UserRepository extends MyCassandraRepository<User> {
    }
    

There are no strict rules regarding naming. The important bits are setting repositoryBaseClass in @EnableCassandraRepositories and method name/parameter type convergence between the base method and the repository interface method.

I created a runnable example that customizes the repository base class. Find the link at the end of this post.

Remarks

The code from above neither compiles (because of public UserRepositoryImpl(CassandraEntityInformation<T, MapId> metadata) nor it leads to PropertyReferenceException.

See also: