View Single Record in Spring Boot and Mongo - mongodb

I'm trying to develop a simple crud application using Spring and Mongodb.
When I'm trying to develop view single data function, I get no error.
But it return value as null when I try in Postman.
Could you please help me to find what is the wrong with my code?
Controller
#GetMapping("/patient/{id}")
public Optional<Patients> findTicketById(#PathVariable("id") #NotNull String id){
System.out.println(id);
return patientRepository.findById(id);
}
Repository
#Repository
public interface PatientRepository extends MongoRepository<Patients, Long> {
Optional<Patients> findById(String id);
}

You can use ifPresentOrElse , check for the usages :
Functional style of Java 8's Optional.ifPresent and if-not-Present?

Related

Is there any way to force spring not to use/create '_class' field in the mapping?

The thing is on production servers we got mapping for Elasticsearch with dynamic set to strict. Currently, we use a rest level client to communicate with Elastisearch, however, we would like to migrate to spring-data-elasticsearch.
Unfortunately, it seems spring data force to use either _class or #TypeAlias which also interfere with the mapping itself. Is any way to use spring-data without _class or #TypeAlias?
Ok I have found a workaround for it.
Be aware of using it when your elasticsearch model uses inheritance.
To solve this problem create class like this:
public class CustomMappingEsConverter extends MappingElasticsearchConverter {
public CustomMappingEsConverter(MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext, GenericConversionService conversionService) {
super(mappingContext, conversionService);
}
#Override
public Document mapObject(#Nullable Object source) {
Document target = Document.create();
if (source != null) {
this.write(source, target);
}
target.remove("_class"); // << workaround to remove those _class field in elasticsearch
return target;
}
}
And register the bean:
#Configuration
public class MappingEsConfiguration {
#Bean
#Primary
public CustomMappingEsConverter CustomMappingElasticsearchConverter(MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext,
GenericConversionService genericConversionService) {
return new CustomMappingEsConverter(mappingContext, genericConversionService);
}
}
After this changes I was able to use spring data without additional field _class.
Currently this is not possible. There is an open issue for that.
Edit 25.04.2021:
this feature will be available from the next version (4.3) on.

Call not propagating to the service method from Spring Reactive Controller

I am a beginner to the spring webflux. We are currently migrating our application to Spring Webflux. No I have stuck with a problem. The following is my scenario.
The main service class is calling the following service classes for data
StudentService - return Mono<Student>
StaffService - return Mono<Staff>
Here I have a wrapper class StudentWithMentor to store the result from these service classes.
public class StudentWithMentor {
private Student student;
private Staff mentor;
}
Now in controller I am calling the above 2 services and map it into 'StudentWithMentor' in the following way
Mono<StudentWithMentor> studentWithMentorMono = Mono.just(new StudentWithMentor());
return studentWithMentorMono.map(s->{
studentService.getStudentById(id)
.doOnSuccess(s::setStudent)
.doOnSuccess(st->staffService.getStaffByGrade(st.getGrade()));
return s;
});
But when I call this endpoint I am getting the following result in postman
{
"student": null,
"mentor": null
}
Note: I am getting result from the underlying services when I debugg. But the call is returning before it process.
How can I achieve this in a complete non-blocking way.
Appreciates any help.
The easiest way will be to to use a zipWith operator to merge the results into StudentWithMentor object.
See the code below:
Mono<StudentWithMentor> studentWithMentorMono = studentService.getStudentById(id)
.zipWhen(student -> staffService.getStaffByGrade(student.getGrade()), StudentWithMentor::new);

How to inject spring aop advice for MongoDb call?

I am new to Spring Aop, but I have case to implement AOP advice for a mongo db call(monog db update). I am trying in different way but getting 'Point cut not well formed' error or 'warning no match for this type name: arg string [Xlint:invalidAbsoluteTypeName]'(even if I give absolute name of the argument). Anyone can help on this as how to inject advice for mongo db update call?
#Aspect
#Component
public class DBStatsLoggerAspect {
private static final Logger log = LoggerFactory
.getLogger(DBStatsLoggerAspect.class);
private static final Document reqStatsCmdBson = new Document(
"getLastRequestStatistics", 1);
private DbCallback<Document> requestStatsDbCallback = new DbCallback<Document>() {
#Override
public Document doInDB(MongoDatabase db) throws MongoException,
DataAccessException {
return db.runCommand(reqStatsCmdBson);
}
};
#After("execution( public * com.mongodb.client.MongoCollection.*(..)) && args(org.bson.conversions.Bson.filter,..)")
public void requestStatsLoggerAdvice(JoinPoint joinPoint) {
MongoTemplate mongoTemplate = (MongoTemplate) joinPoint.getTarget();
log.info(mongoTemplate.execute(requestStatsDbCallback).toJson());
}
}
Actual db call method where I need to inject advice:(filter, updatePart all are org.bson.conversions.Bson data type) and here 'collection' is com.mongodb.client.MongoCollection.collection
Document result = collection.findOneAndUpdate(filter, updatePart, new FindOneAndUpdateOptions().upsert(false));
I am not a Spring or MongoDB user, just an AOP expert. But from what I see I am wondering:
You are intercepting execution(public * com.mongodb.client.MongoCollection.*(..)), so joinPoint.getTarget() is a MongoCollection type. Why do you think you can cast it to MongoTemplate? That would only work if your MongoCollection happened to be a MongoTemplate subclass. To me this looks like a bug.
Class MongoCollection is not a Spring component but a third-party class. Spring AOP can only intercept Spring component calls by means of creating dynamic proxies for those components and adding aspect interceptors to said proxies. so no matter how correct or incorrect your pointcut, it should never trigger.
What you can do instead is switch from Spring AOP to full-blown AspectJ. The standard way to do this is to activate AspectJ load-time weaving (LTW).

Retrieving additional non DB information using Spring Data Rest

I am using Spring Data Rest to expose a news feed REST API. I want to add an image (location) to the entity which will be retrieved by a separate web service API call.
What is the best way to do this using Spring Data Rest or would I have to create another separate REST API call/domain object etc.?
Any sample code would be fantastic.
You should use a ResourceProcessor
The Spring Data REST exporter executes any discovered ResourceProcessor's before it creates the output representation
#Bean
public ResourceProcessor<Resource<MyEntity>> myEntityProcessor() {
return new ResourceProcessor<Resource<MyEntity>>() {
#Override
public Resource<MyEntity> process(Resource<MyEntity> resource) {
resource.add(new Link("http://localhost:8080/images/images.jpg", "image"));
return resource;
}
};
}
Another example with access to the repository and EntityLinks object that helps to build links related to the entity..
#Component
class MyEntityResourceProcessor implements ResourceProcessor<Resource<MyEntity>> {
#Autoware
private MyEntityRepo repo;
#Autoware
private EntityLinks entityLinks;
#Override
public Resource<MyEntity> process(Resource<MyEntity> resource) {
MyEntity entity = resource.getContent();
// Some entity processing...
Link link entityLinks.linkForSingleResource(entity).slash("...").withRel("...")
resource.add(link);
return resource;
}
}
More examples of using ResourceProcessor you can find in RESTBucks project

Custom error is not rendered as hal in Spring Boot 1.3 and Spring hateoas 0.19

Initially I used Spring Boot 1.2 and Spring hateoas in my project, and I need to customize error message. So I created our class instead of the native VndErrors and VndError.
I created a class extends VndErrors.VndError.
public class MyError extends VndErrors.VndError{
//add some my custom fields
}
And antoher class to wrap the MyError.
public class ErrorDetails{
int total;
#JsonProperty("_embedded")
Map<String, List<MyError>> errors;
public ErrorDetails(List<MyError> err){
this.total=err.size();
errors.put("errors", err);
}
}
All exception are hanleded in a #ContrllerAdvice class. I used a custom Jackson2ObjectMapperBuilder to configure ObjectMapper in our project.
When I used Spring 1.2, it was rendered as expected. As following.
{
"total": 1,
"_embedded":{
"errors":[
{
//feilds,
_links:{
"self":""
}
}
]
}
}
But when upgraded to Spring Boot 1.3, it does not work as excepted.
The _links rendered as links, and the content type is application/json in the debug info.
Stage 1:
I am trying to create a simple pojo with a List of Link, it does not work.
public class ErrorDetails{}//pojo includes fields
public class MyError{
//add some my custom fields
#JsonUnwrapped
ErrorDetails content;
List<link> links;
}
public class ErrorResources{
int total;
#JsonProperty("_embedded")
Map<String, List<MyError>> errors;
public ErrorResources(List<MyError> err){
this.total=err.size();
errors.put("errors", err);
}
}
I found some related issues on github of Spring Hateoas project.
https://github.com/spring-projects/spring-hateoas/issues/279
https://github.com/spring-projects/spring-hateoas/issues/324
https://github.com/spring-projects/spring-hateoas/issues/288
I tried one of the suggestions of the issues above, when added #JsonSerialze(using=Jackson2HalModule.HalLinkListSerializer) on links of MyError class.
Got message similar with can not find the correct HttpMessageConverter, the content type of result is application/ocect(binary).
I also tried set the default contentType or default viewResolver to MappingJackson2JsonView, all did not change the result.
Whend I added a custom MappingJackson2HttpMessageConverter in my config:
#Bean
#Order(1)
public MappingJackson2HttpMessageConverter jacksonMessageConverter() {
ObjectMapper halObjectMapper=ctx.getBean("_halObjectMapper", ObjectMapper.class);
MappingJackson2HttpMessageConverter jacksonMessageConverter =
new MappingJackson2HttpMessageConverter();
jacksonMessageConverter.setObjectMapper(halObjectMapper);
jacksonMessageConverter.setSupportedMediaTypes(
Arrays.asList(MediaTypes.HAL_JSON, MediaType.APPLICATION_JSON_UTF8, MediaType.ALL));
return jacksonMessageConverter;
}
The error result is rendered as expected. But I do not think it is the correct way, because I used MediaType.ALL here. And it caused another big problem.
I used TestRestTemplate to test my rest APIs. The restTemlate tried to treat the input data as XML. I saw in the exception it indicated it tried to invoke a XmlHttpMessageConverter to process the content(it is json), even I have set the accept header as application/json. Of course, before I upgraded to Spring Boot 1.3 stack, it worked.
Stage 2:
I tried to use Resources and Resource to wrap the error result.
public class ErrorDetails{}//pojo includes error description fields
public class ErrorResource extends Resource<ErrorDetails>{
}
public class ErrorResources extends Resources<ErrorResource>{
}
public class ErrorMessage {
int total;
ErrorResources errors;
}
Spring still can not render the error result as hal format, it is application/json. When I added
#JsonSerialze(using=Jackson2HalModule.HalResourcesSerializer) on ErrorResources class, it raised an exception which complained the HalResourcesSerializer does not has a default constructor.
In the #ControllerAdvice class, I have tried to set the method return type to ErrorMessage and a wrapper ResponseEntity , it does not work.
Finally, my question is how to render the response body in a #ControllerAdvice same as the one in a normal #RestController? Why it does not work in a #ControllerAdvice class?
Is there a simple workaroud for this issue?