1

@Service
public abstract class BaseService<R extends IChanakyaR2dbcRepository<E, String>, E, D> {

    @Autowired
    protected R repository;

    public abstract D convertToDTO(E entity);

    public abstract E convertToEntity(D DTO);

    public Flux<D> findAll() {
        return repository.findAll().map(this::convertToDTO);
    }

    public Mono<D> findById(String id) {
        return repository.findById(id).map(this::convertToDTO);
    }

    public Mono<D> create(D DTO) {
        return repository.insert(convertToEntity(DTO)).map(this::convertToDTO);
    }

    public Mono<D> update(D DTO) {
        return repository.save(convertToEntity(DTO)).map(this::convertToDTO);
    }

    public Mono<Void> delete(D DTO) {
        return repository.delete(convertToEntity(DTO));
    }
}

I have the above class as an abstract class from which all my base services class will inherit.


@Service
public class ClusterService extends BaseService<IChanakyaR2dbcRepository<ClusterEntity, String>, ClusterEntity, ClusterDTO> {

    @Autowired
    ModelMapper modelMapper;

    @Override
    public ClusterDTO convertToDTO(ClusterEntity entity) {
        return modelMapper.map(entity, ClusterDTO.class);
    }

    @Override
    public ClusterEntity convertToEntity(ClusterDTO DTO) {
        return modelMapper.map(DTO, ClusterEntity.class);
    }

}

This is my service class.


@NoRepositoryBean
public interface IChanakyaR2dbcRepository<T, ID> extends ReactiveCrudRepository<T, ID> {

    /**
     * Inserts a given entity. Use the returned instance for further operations as the save operation might have changed the
     * entity instance completely.
     *
     * @param entity must not be {@literal null}.
     * @return {@link Mono} emitting the inserted entity.
     * @throws IllegalArgumentException in case the given {@literal entity} is {@literal null}.
     */
    <S extends T> Mono<S> insert(S entity);

}

This is my repository class.


@Slf4j
@Transactional(readOnly = true)
public class ChanakyaR2dbcRepository<T, ID> extends SimpleR2dbcRepository<T, ID> {

    private final RelationalEntityInformation<T, ID> entity;
    private final DatabaseClient databaseClient;
    private final R2dbcConverter converter;
    private final ReactiveDataAccessStrategy accessStrategy;

    public ChanakyaR2dbcRepository(RelationalEntityInformation entity, DatabaseClient databaseClient, R2dbcConverter converter, ReactiveDataAccessStrategy accessStrategy) {
        super(entity, databaseClient, converter, accessStrategy);
        this.entity = entity;
        this.databaseClient = databaseClient;
        this.converter = converter;
        this.accessStrategy = accessStrategy;
    }

    @Transactional
    public <S extends T> Mono<S> insert(S objectToSave) {

        Assert.notNull(objectToSave, "Object to insert must not be null!");

        try {
            return this.databaseClient.insert()
                    .into(this.entity.getJavaType())
                    .table(this.entity.getTableName()).using(objectToSave)
                    .map(this.converter.populateIdIfNecessary(objectToSave))
                    .first()
                    .defaultIfEmpty(objectToSave);
        } catch(DataIntegrityViolationException diex) {
            String errMsg = String.format("Entity {} already exists in database", this.entity);
            log.error(errMsg, diex);
            return Mono.error(new IllegalStateException(errMsg));
        }
    }

}

This is my repository implementation.

When I run the application it, I get the following error.

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'clusterService': Unsatisfied dependency expressed through field 'repository'; nested exception is

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.appdynamics.sum.chanakya.repository.base.IChanakyaR2dbcRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

The above autowire doesn't work, but when I autowire IChanakyaR2dbcRepository<ClusterEntity, String> in another componenet directly ( not via a generic type ). It works fine.

What is the correct way to do such a thing? I want to have BaseService implementation.

I know if you create an interface that extends IChanakyaR2dbcRepository<ClusterEntity, String> and pass that class as the generic type to BaseService then it will work fine. But that would mean creating such empty classes for every entity I have which is not an ideal solution.

art
  • 1,222
  • 9
  • 19
Deepak Kar
  • 83
  • 2
  • 7

2 Answers2

2

you should try this :

@EnableR2dbcRepositories(basePackages = {"your.repository.package"}, 
basePackageClasses = {}) 

the problem lies in the fact springboot does not know where to find this custom type r2dbc repository

Mayur Satav
  • 985
  • 2
  • 12
  • 32
  • you do not need to declare an implementation to interface itself, unless you absolutely want to customize something beyond crud – user2980868 Jul 08 '20 at 14:05
0

It's not quite sure what you are trying to achieve. If you want your IChanakyaR2dbcRepository to be a "baserepository" (hence @NoRepositoryBean) and have "another" @Repository extend it and autowire it in your "Base Service"

@Repository
MyIChanakyaR2dbcRepository ....

@Autowire
MyIChanakyaR2dbcRepository rep;

or autowire an @Repository in your ClusterService or your "BaseService" should implement any of your Repositories (depends on what you are trying to achieve)

but just telling from the exception you need to provide an @Repository in your "BaseService" as in

@Repository
MyIChanakyaR2dbcRepository extends "YourBasyRepository"<"YourEntityType", "YourIdType">{.....}

@Service
public abstract class BaseService<R extends IChanakyaR2dbcRepository<E, String>, E, D> {

    @Autowired
    protected MyIChanakyaR2dbcRepository repository;

you can't autowire @NoRepositoryBeans

Bender
  • 72
  • 6