Spring JPA select one column doesn't work - mysql

I need to retrieve all the records for players column in sports table. SportsRepository method like below.
#SuppressWarnings("unused")
#Repository
public interface SportsRepository extends JpaRepository<Sport, Long> {
public static final String GET_Players="SELECT players FROM Sport";
#Query(value = GET_Players, nativeQuery = true)
public static List<String> getPlayers();
}
After that I type "gradlew clean test" and see. Then it will generate an error saying "error: missing method body, or declare abstract". Why is that? Do we need to implement the body. Because JPA query should give the output as I could understand. Please anyone knows?

In a JAVA interface, when you try to define a static method, you should give it a method body, but here I think the only thing you should do is remove static from your method:
#SuppressWarnings("unused")
#Repository
public interface SportsRepository extends JpaRepository<Sport, Long> {
public static final String GET_Players="SELECT players FROM Sport";
#Query(value = GET_Players, nativeQuery = true)
public List<String> getPlayers();
}
Also access modifier public is not necessary here as well, because java take public as default when define a method in interface.
Note: I noticed that you assigned nativeQuery with true, you should confirm the GET_Players is sql, not a jql.

Related

How can obtain a Link when in the controller the mappings of the rest endpoints are defined by a SPEL?

I am using Spring Hateoas 1.5.2 and I have a scenario where I cannot produce a Link.
#RestController
#RequestMapping("${unexisting.property.controller:/spel-defined-url}")
public class SpelDefinedController {
#AllArgsConstructor
#Data
private static class ExampleDto {
private String test;
}
#GetMapping(value = "${unexisting.property.controller.documentation:/another-spel-persons/{id}}", produces = "application/json")
public EntityModel<ExampleDto> findOne(#PathVariable Long id) {
// Creation of the self link"self" link, i.e. this method.
Link findOneLink = linkTo(methodOn(SpelDefinedController.class).findOne(id)).withSelfRel();
// We return
return EntityModel.of(new ExampleDto("test"), findOneLink);
}
}
The line of code that fails is this one
Link findOneLink = linkTo(methodOn(SpelDefinedController.class).findOne(id)).withSelfRel();
I think the issue comes with the fact that I am using SPEL inside the #Controller or the #GetMapping and these SPELs are not resolved by the linkTo.
Or I am doing something wrong. Any help to understand is appreciated.

How to access nested Json Object Value into JPA Entity Class

I have a payload like this
{
"eventId":"ep9_0579af51",
"eventTime":"5/11/2022 5:50:58 PM",
"eventType":"UpdateTransaction",
"meta":{
"userId":"vkp",
"resourceType":"Transaction/DataDocs"
}
}
I need to map this json fields into a single entity class .
#PostMapping(path = "/id", consumes = "application/json")
public ResponseEntity<ImportTrans> import(#RequestBody ImportTrans importTrans) {
return ResponseEntity.of(Optional.ofNullable(repository.save(importTrans););
}
#Table(name = "IMPORT_TRANS")
#Entity
public class ImportTrans implements Serializable {
#Id
private Long processId;// AutoGenerator
private String eventId;
private Date eventTime;
private String eventType;
private String userId; // I dont want create new class for meta . Is there any way i
//can access meta.userId in ImportTrans class.
private String resourceType;
}
How can I access data from meta from ImportTrans without creating a separate class for it?
You should modify your request body before reaching the controller.
"You must consider the application performance factors on your own
before implementation"
Option 1. Using RequestBodyAdvice.
Option 2. Using Spring HandlerInterceptor.
Option 3. Use AOP
Option 4. Using HTTP Filter.
The below solution only works if you are using a separate DTO class.
private Map<String, String> meta = new HashMap<>();
String userID = importTrans.getMeta().get("userId");
I hope the above pieces of information answered your question.

how to send delete request without knowing the id in json?

for example
if i have used findByEmail my url will look something like this
http://localhost:8080/api/carts/search/findByEmail?email=helloworld#gmail.com
how will the URL for delete request look if i have deleteByEmail in JPA repository
this is how my JPA looks like
public interface CartRepository extends JpaRepository<Cart, Integer> {
#Transactional
#RestResource(exported = true)
List<Cart> deleteByEmail(String email);
}
You can have a rest API as http://localhost:8080/api/carts/email/{emailId} with emailId in PathVariable HttpMethod as DELETE. Inside the service method, you search by emailId and then Delete it using the ID.
You need to create a controller method with the appropriate mapping. You can define any URL you want, I used /api/carts/{email}. Make sure to send a HTTP DELETE request.
#RestController
public class CartController {
private final CartRepository cartRepository;
#Autowired
public CartController (CartRepository cartRepository) {
this.cartRepository = cartRepository;
}
#DeleteMapping("/api/carts/{email}")
public void deleteCart(#PathVariable String email) {
cartRepository.deleteByEmail(email);
}
}
If you prefer to solve this with a request param, change the deleteCart method as shown below:
#DeleteMapping("/api/carts")
public void deleteCart(#RequestParam String email) {
cartRepository.deleteByEmail(email);
}

Post processing of a Json response in spring MVC

I have several controllers that return the same generic Response object with #ResponseBody annotation, like this:
#RequestMapping(value = "/status", method = RequestMethod.GET)
#Transactional(readOnly = true)
public #ResponseBody Response<StatusVM> status()
I need to perform an operation on every controller, after the Response is returned. This operation will enrich the Response object with new data.
I don't want to duplicate code, so I need a single point of intervention. I thought I could do this with Interceptors, however, according to the docs http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-handlermapping-interceptor this does not work well with #ResponseBody:
Note that the postHandle method of HandlerInterceptor is not always ideally suited for use with #ResponseBody and ResponseEntity methods. In such cases an HttpMessageConverter writes to and commits the response before postHandle is called which makes it impossible to change the response, for example to add a header. Instead an application can implement ResponseBodyAdvice and either declare it as an #ControllerAdvice bean or configure it directly on RequestMappingHandlerAdapter.
I haven't been able to find an example of this tecnique, could anybody help me?
As an alternative I could work with aspects, but then I'd need to annotate every controller, which is something I'd like to avoid.
In the end I implemented ResponseBodyAdvice like this:
#ControllerAdvice
public class StatusAdvice implements ResponseBodyAdvice<Response<?>> {
#Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
if (returnTypeIsReponseVM(returnType)&&responseConverterIsJackson2(converterType)){
return true;
}
return false;
}
....
#Override
public Response<?> beforeBodyWrite(Response<?> body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
....
return body;
}
}
So it was easier then expected.

Using Hibernate #SQLDelete for Soft Delete across All Entities

We have a fairly complex data model and are using Hibernate and Spring Data JPA on top of MySQL. We have a base class that all domain objects extend to minimize boiler plate code. I would like to be able to add soft delete functionality across all of our domain objects using only this class. However, #SQLDelete requires the table name in the clause:
#SQLDelete(sql="UPDATE (table_name) SET deleted = '1' WHERE id = ?")
#Where(clause="deleted <> '1'")
Does anybody know of a way to generalize the SQLDelete statement and allow the extending domain objects to populate their own table names?
If you use hibernate and #SQLDelete, there's no easy solution to your question. But you can consider another approach to soft delete with Spring Data's expression language:
#Override
#Query("select e from #{#entityName} e where e.deleteFlag=false")
public List<T> findAll();
//recycle bin
#Query("select e from #{#entityName} e where e.deleteFlag=true")
public List<T> recycleBin();
#Query("update #{#entityName} e set e.deleteFlag=true where e.id=?1")
#Modifying
public void softDelete(String id);
//#{#entityName} will be substituted by concrete entity name automatically.
Rewrite base repository like this. All sub repository interfaces will have soft delete ability.
Another approach, which could be more flexible.
On Entity level create
#MappedSuperclass
public class SoftDeletableEntity {
public static final String SOFT_DELETED_CLAUSE = "IS_DELETED IS FALSE";
#Column(name = "is_deleted")
private boolean isDeleted;
...
}
Update your Entity which should be soft deletable
#Entity
#Where(clause = SoftDeletableEntity.SOFT_DELETED_CLAUSE)
#Table(name = "table_name")
public class YourEntity extends SoftDeletableEntity {...}
Create a custom Interface Repository which extends the Spring's Repository. Add default methods for soft delete. It should be as a base repo for your Repositories. e.g.
#NoRepositoryBean
public interface YourBaseRepository<T, ID> extends JpaRepository<T, ID> {
default void softDelete(T entity) {
Assert.notNull(entity, "The entity must not be null!");
Assert.isInstanceOf(SoftDeletableEntity.class, entity, "The entity must be soft deletable!");
((SoftDeletableEntity)entity).setIsDeleted(true);
save(entity);
}
default void softDeleteById(ID id) {
Assert.notNull(id, "The given id must not be null!");
this.softDelete(findById(id).orElseThrow(() -> new EmptyResultDataAccessException(
String.format("No %s entity with id %s exists!", "", id), 1)));
}
}
NOTE: If your application doesn't have the hard delete then you could add
String HARD_DELETE_NOT_SUPPORTED = "Hard delete is not supported.";
#Override
default void deleteById(ID id) {
throw new UnsupportedOperationException(HARD_DELETE_NOT_SUPPORTED);
}
#Override
default void delete(T entity) {
throw new UnsupportedOperationException(HARD_DELETE_NOT_SUPPORTED);
}
#Override
default void deleteAll(Iterable<? extends T> entities) {
throw new UnsupportedOperationException(HARD_DELETE_NOT_SUPPORTED);
}
#Override
default void deleteAll() {
throw new UnsupportedOperationException(HARD_DELETE_NOT_SUPPORTED);
}
Hope it could be useful.