Spring custom repository for MingoDB - spring

Tell me how to properly implement your repository for mongodb?
At the moment, with this configuration, for some reason I get an error .... ((
#Configuration
#EnableMongoRepositories(basePackages = "by.repository.mongo", repositoryBaseClass = BaseMongoRepository.class)
public class MongoConfig {}
#NoRepositoryBean
public interface BaseMongoRepository<T extends BaseDocument> extends MongoRepository<T, String> {
void saveCascade(T document);
}
public interface LimitRepository extends BaseMongoRepository<LimitDocument>, QuerydslPredicateExecutor<LimitDocument> {}
public class LimitRepositoryImpl extends BaseMongoRepositoryImpl<LimitDocument> implements BaseMongoRepository<LimitDocument> {
public LimitRepositoryImpl(MongoEntityInformation<LimitDocument, String> metadata, MongoOperations mongoOperations) {
super(metadata, mongoOperations);
}
#Override
public void saveCascade(LimitDocument document) {
save(document);
}
Description:
Parameter 0 of constructor in by.repository.mongo.LimitRepositoryImpl required a bean of type 'org.springframework.data.mongodb.repository.query.MongoEntityInformation' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.data.mongodb.repository.query.MongoEntityInformation' in your configuration.
UPDATE 1
I have found simple solution...
But I would like to know if there are pitfalls in such a decision
public class LimitRepositoryImpl extends BaseMongoRepositoryImpl<LimitDocument> implements BaseMongoRepository<LimitDocument> {
...
public LimitRepositoryImpl(MongoOperations mongoOperations) {
super(getEntityInformationFor(LimitDocument.class, String.class), mongoOperations);
}
...
public static MongoEntityInformation getEntityInformationFor(Class clazz, Class idClazz) {
TypeInformation typeInformation = ClassTypeInformation.from(clazz);
MongoPersistentEntity mongoPersistentEntity = new BasicMongoPersistentEntity(typeInformation);
return new MappingMongoEntityInformation(mongoPersistentEntity, idClazz);
}
}

You can use this method
Maven dependency :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>2.5.6</version>
</dependency>
Repository:
#Repository
public interface PersonRepository extends MongoRepository<PersonEntity, Integer>{}
And in your main class:
#SpringBootApplication(scanBasePackages = {"com.example.data"})
#EnableMongoRepositories(basePackages = {"com.example.data"})
public class PersonApplication {
public static void main(String[] args) {
SpringApplication.run(PersonApplication.class, args);
}
}

Related

How to register Converter in Spring Data Rest application

I have Spring converter which uses Spring Data REST's component called EnumTranslator
#Component
public class TranslationStringToSpecificationStatusEnumConverter implements Converter<String, Specification.Status> {
private final EnumTranslator enumTranslator;
#Autowired
public TranslationStringToSpecificationStatusEnumConverter(EnumTranslator enumTranslator) {
this.enumTranslator = enumTranslator;
}
#Override
public Specification.Status convert(String source) {
return enumTranslator.fromText(Specification.Status.class, source);
}
}
Recommended way to register such converter is to subclass RepositoryRestConfigurerAdapter as follows:
#Configuration
public class RepositoryRestConfig extends RepositoryRestConfigurerAdapter {
private final TranslationStringToSpecificationStatusEnumConverter converter;
#Autowired
public RepositoryRestConfig(TranslationStringToSpecificationStatusEnumConverter converter) {
this.converter = converter;
}
#Override
public void configureConversionService(ConfigurableConversionService conversionService) {
conversionService.addConverter(converter);
super.configureConversionService(conversionService);
}
}
When I run the Spring Boot application, it fails on the following:
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| translationStringToSpecificationStatusEnumConverter defined in file ...
↑ ↓
| org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration (field java.util.List org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration.configurers)
↑ ↓
| repositoryRestConfig defined in file ...
└─────┘
So there is circular bean dependency.
How can I register the converter above so that I don't introduce circular bean dependency?
To make it work:
#Override
public void configureConversionService(ConfigurableConversionService conversionService) {
conversionService.addConverter(String.class, Status.class, new StringToTranslatedEnumConverter<>(Status.class));
super.configureConversionService(conversionService);
}
First I created utility class that help me work with Spring beans in unmanaged objects:
#Component
public final class SpringUtils {
#Autowired private ApplicationContext ctx;
private static SpringUtils instance;
#PostConstruct
private void registerInstance() {
instance = this;
}
public static <T> T getBean(Class<T> clazz) {
return instance.ctx.getBean(clazz);
}
}
Then I created the converter:
public class StringToTranslatedEnumConverter<T extends Enum<T> & TranslatedEnum> implements Converter<String, T> {
private final ConcurrentMapCache cache;
private EnumTranslator enumTranslator;
private Class<T> type;
public StringToTranslatedEnumConverter(Class<T> type) {
this.type = type;
cache = new ConcurrentMapCache(type.getName());
}
#Override
public T convert(String from) {
if (enumTranslator == null) {
enumTranslator = SpringUtils.getBean(EnumTranslator.class);
}
Cache.ValueWrapper wrapper = cache.get(from);
if (wrapper != null) {
//noinspection unchecked
return (T) wrapper.get();
}
T translatedEnum = enumTranslator.fromText(type, from);
cache.put(from, translatedEnum);
return translatedEnum;
}
}
UPDATED
TranslatedEnum - it's interface-marker, used to mark enums which translation is only need.
public interface TranslatedEnum {
}
public enum Status implements TranslatedEnum {
CREATED, DELETED
}
The solution to this problem is Spring Core specific. In order to break circle bean dependency cycle, we have to delay setting converter in RepositoryRestConfig. It can be achieved with setter injection:
#Component
public class RepositoryRestConfig extends RepositoryRestConfigurerAdapter {
private TranslationStringToSpecificationStatusEnumConverter converter;
#Override
public void configureConversionService(ConfigurableConversionService conversionService) {
conversionService.addConverter(converter);
super.configureConversionService(conversionService);
}
#Autowired
public void setConverter(TranslationStringToSpecificationStatusEnumConverter converter) {
this.converter = converter;
}
}
You can find how to solve it in this commit by Greg Turnquist: https://github.com/pmihalcin/custom-converter-in-spring-data-rest/commit/779a6477d76dc77515b3e923079e5a6543242da2

Spring Bean Factory Configuration passing input parameter

I'm trying to create a BeanFactory called TaskBeanFactory that I can Autowire into another prototype class that's running on a thread. I want a different instance of a bean returned by the Factory based on a taskName that i want to pass into it but when i start the application i get a null pointer exception because the taskName is null. I had a look at this article but i'm confused about how I should configure the Factory and then pass in the taskName.
The Factory:
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.stereotype.Component;
#Data
#Component
#NoArgsConstructor
public class TaskBeanFactory extends AbstractFactoryBean<GenericTask>{
private TaskNameEnum taskName;
public TaskBeanFactory(TaskNameEnum taskName) {
setSingleton(false);
}
#Override
public Class<?> getObjectType() {
return GenericTask.class;
}
#Override
protected GenericTask createInstance() throws Exception {
switch (taskName) {
case FILE_OPERATION:
return new FileTask();
case DATA_OPERATION:
return new DataTask();
default:
return new GenericTask();
}
}
}
The classes used by the Factory:
#Data
public class GenericTask {
private String idTask;
public void executeTask(Work work) {};
}
#Component
#Scope(value="prototype")
public class FileTask extends GenericTask {
#Override
public void executeTask(Work work) {
//some processing
}
}
#Component
#Scope(value="prototype")
public class DataTask extends GenericTask {
#Override
public void executeTask(Work work) {
//some processing
}
}
and the thread that's calling the Factory:
#Slf4j
#Data
#Scope("prototype")
#Component
public class WorkerThread implements Runnable {
#Autowired
private TaskBeanFactory taskBeanFactory;
#Autowired
private DataService dataService;
#Override
public void run() {
//iterate a Map of taskIds from the dataService
taskBeanFactory.setTaskName(TaskNameEnum.valueOf(taskEntry.getKey()));
GenericTask genericTask = taskBeanFactory.getObject();
//expecting genericTask to be of Type FileTask if called with one Key
//or of Type DataTask if called with another
}
}
}

Spring Boot JPA with REST Service

I know that such questions were asked (Did not find handler method ), but after trying multiple solutions I'm still stucked.
So my problem is: I can't use both REST and JPA in my project.
com.db.ruf: WRepository.class:
#NoRepositoryBean
#Component
public interface WRepository <T, ID extends Serializable>
extends JpaRepository<T, ID> {
}
com.db.ruf: WRepositoryImpl.class:
public class WRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements WRepository<T, ID> {
private EntityManager entityManager;
// There are two constructors to choose from, either can be used.
public WRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
super(domainClass, entityManager);
// This is the recommended method for accessing inherited class dependencies.
this.entityManager = entityManager;
}
}
com.db: MyRepositoryFactoryBean.class
public class MyRepositoryFactoryBean <R extends JpaRepository<T, I>, T, I extends Serializable>
extends JpaRepositoryFactoryBean<R, T, I> {
/**
* Creates a new {#link JpaRepositoryFactoryBean} for the given repository interface.
*
* #param repositoryInterface must not be {#literal null}.
*/
public MyRepositoryFactoryBean(Class<? extends R> repositoryInterface) {
super(repositoryInterface);
}
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new MyRepositoryFactory(entityManager);
}
private static class MyRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private EntityManager entityManager;
public MyRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
protected Object getTargetRepository(RepositoryMetadata metadata) {
return new WRepositoryImpl<T, I>((Class<T>) metadata.getDomainType(), entityManager);
}
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
// The RepositoryMetadata can be safely ignored, it is used by the JpaRepositoryFactory
//to check for QueryDslJpaRepository's which is out of scope.
return WRepository.class;
}
}
}
com.rest.: WebadminRESTController.class
#RestController
#Component
public class WebadminRESTController {
#Autowired
WRepository<ExternalLink, Long> wRepositoryImpl;
#RequestMapping(value = "/allExternalLinks", method = RequestMethod.GET)
public ResponseEntity<?> allExternalLinks() {
...
}
}
com:
#SpringBootApplication
#EnableAutoConfiguration
#EnableJpaRepositories(basePackages = "com.db.ruf", repositoryFactoryBeanClass = MyRepositoryFactoryBean.class)
#ComponentScan(basePackages = "com", resourcePattern = "com.*")
#EntityScan({"com.db"})
public class WebadminApplication {
public static void main(String[] args) {
SpringApplication.run(WebadminApplication.class, args);
}
}
In this case I get:
Did not find handler method for [/allExternalLinks]
If I change
#ComponentScan(basePackages = "com", resourcePattern = "com.*") to #ComponentScan(basePackages = "com") or #ComponentScan I get:
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'com.db.ruf.WRepository' available: expected at least 1 bean which qualifies
as autowire candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
I really don't know what's wrong.
Could anyone be so kind to explain it?
Thank you in advance
Use #Repository on WRepositoryImpl.java. And edit WebadminRESTController.java:
#Autowired
WRepositoryImpl<ExternalLink, Long> wRepositoryImpl;
Accidentally I found solution (don't think that it is the best one, but at least it works):
public interface ExternalLinksRepo extends CrudRepository<ExternalLink, Long> {
...
}
and:
public class WebadminRESTController {
#Autowired
ExternalLinksRepo wRepositoryImpl;
...}
Then wRepositoryImpl is automatically created as instance of WRepositoryImpl
Thanks to all, especially to Cepr0

Spring boot Autowiring service implementation in a bean fails

I'm trying to autowire a service implementation in one of my beans, but I keep getting a NoSuchBeanDefinitionException. This is my code:
Repository:
#Repository
public interface GlobalPropertiesRepository extends BaseRepository<GlobalProperties, Long>{
}
Service:
public interface GlobalPropertiesService {
GlobalProperties findOne(Long id);
}
Base Repository:
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.Repository;
import java.io.Serializable;
import java.util.List;
#NoRepositoryBean
public interface BaseRepository<T, ID extends Serializable> extends Repository<T, ID> {
void delete(T deleted);
List<T> findAll();
List<T> findAll(Iterable<ID> ids);
T findOne(ID id);
T save(T persisted);
<S extends T> S saveAndFlush(S entity);
}
Service Impl:
#Service("globalPropertiesService")
public class GlobalPropertiesServiceImpl implements GlobalPropertiesService{
#Autowired
GlobalPropertiesRepository globalPropertiesRepository;
#Override
public GlobalProperties findOne(Long id) {
return globalPropertiesRepository.findOne(id);
}
}
And then I autowire the implementation in one of my beans as below:
public class GlobalPropertiesLoader {
#Autowired
private GlobalPropertiesService globalPropertiesService;
private GlobalProperties globalProperties;
#PostConstruct
public void init(){
globalProperties = globalPropertiesService.findOne(1L);
}
public GlobalProperties getGlobalProperties(){
return globalProperties;
}
}
Finally, this is my Configuration class:
#Configuration
public class AppServiceConfig {
public AppServiceConfig() {
}
// Global properties
#Bean(name="globalPropertiesLoader")
public GlobalPropertiesLoader globalPropertiesLoader(){
return new GlobalPropertiesLoader();
}
}
This is my SpringBoot class:
#SpringBootApplication
#ComponentScan(basePackages="...")
public class TrackingService {
private static final Logger LOGGER = LoggerFactory.getLogger(TrackingService.class);
static AnnotationConfigApplicationContext context;
public static void main(String[] args) throws Exception {
SpringApplication.run(TrackingService.class, args);
ClassPathScanningCandidateComponentProvider provider =
new ClassPathScanningCandidateComponentProvider(true);
String basePackage = "...";
Set<BeanDefinition> components = provider.findCandidateComponents(basePackage);
for (BeanDefinition component : components) {
LOGGER.info("Component: "+component.getBeanClassName());
}
context = new AnnotationConfigApplicationContext();
context.refresh();
context.close();
}
}
Now, when I try to start the application, I get the following error in my GlobalPropertiesLoader bean:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [GlobalPropertiesService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}

Issues with a Spring #Configurable class not getting its dependencies autowired

I am trying to configure Load Time Weaving for my Spring Boot app to properly autowire dependencies on a #Configurable java class.
Here is my configuration/main class:
package com.bignibou;
#Configuration
#EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class, ThymeleafAutoConfiguration.class, FlywayAutoConfiguration.class })
#EnableSpringConfigured
#EnableLoadTimeWeaving(aspectjWeaving = AspectJWeaving.ENABLED)
#ComponentScan
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
Here is how I start the application (my gradle build renamed the spring-instrument jar):
java -javaagent:build/lib/springinstrument.jar -jar myapp.jar
Here is the #Configurable class that does not get its dependencies autowired:
package com.bignibou.converter;
#Configurable
public class StringToDayToTimeSlotConverter implements Converter<String, DayToTimeSlot> {
#Autowired
private DayToTimeSlotRepository dayToTimeSlotRepository;//NOT AUTOWIRED!!
#Override
public DayToTimeSlot convert(String id) {
return dayToTimeSlotRepository.findOne(Long.parseLong(id));//NPE HERE!!
}
}
Here is where the class is instantiated (with new):
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "com.bignibou.controller" }, useDefaultFilters = false, includeFilters = { #Filter(type = FilterType.ANNOTATION, value = Controller.class),
#Filter(type = FilterType.ANNOTATION, value = ControllerAdvice.class) })
#Import(ApplicationControllerAdvice.class)
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
...
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new DayToTimeSlotToStringConverter());
registry.addConverter(new StringToDayToTimeSlotConverter());//INSTANTIATED HERE!
registry.addConverter(new LanguageToStringConverter());
registry.addConverter(new StringToLanguageConverter());
registry.addConverter(new AddressToStringConverter());
registry.addConverter(new StringToAddressConverter());
super.addFormatters(registry);
}
Can anyone please help figure out why StringToDayToTimeSlotConverter's dependencies are not autowired?
Very old question with at least a suggestion for a solution that I will turn into an answer so that the question can be "closed". Turn StringToDayToTimeSlotConverter into a Bean as follows:
#Bean
public class StringToDayToTimeSlotConverter implements Converter<String, DayToTimeSlot> {
#Autowired
private DayToTimeSlotRepository dayToTimeSlotRepository;
#Override
public DayToTimeSlot convert(String id) {
return dayToTimeSlotRepository.findOne(Long.parseLong(id));
}
}
Inject all available converters in WebMvcConfiguration as follows:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "com.bignibou.controller" }, useDefaultFilters = false, includeFilters = { #Filter(type = FilterType.ANNOTATION, value = Controller.class),
#Filter(type = FilterType.ANNOTATION, value = ControllerAdvice.class) })
#Import(ApplicationControllerAdvice.class)
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
#Autowired
List<Converter> converters;
#Override
public void addFormatters(FormatterRegistry registry) {
for (Converter converter : converter) {
registry.addConverter(converter);
}
super.addFormatters(registry);
}
}

Resources