SPRING REST controller - return image AND json values - spring

I have built a rest web service, using SPRING and Hibernate.
I have 2 entities : Image and user, linked with a oneToOne annotation.
When I try to return the user details AND the image corresponding to this user, I get this error :
"org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation"
When I do it separately It works fine, but when I do it in one route, I get this error.
Here is my controller :
#CrossOrigin(
origins = "*",
methods = {RequestMethod.POST, RequestMethod.GET, RequestMethod.OPTIONS, RequestMethod.DELETE},
allowedHeaders = "*")
#RestController
#RequestMapping(path = "/user")
public class UserController {
#Autowired
UserRepository userRepository;
#Autowired
ImageRepository imageRepsository;
doesn't work--> #RequestMapping(value="/{userId}/getUserAndImage",method=RequestMethod.GET,produces = MediaType.IMAGE_JPEG_VALUE )
public Optional<User> getUserAndImage(#PathVariable Long userId) {
return userRepository.findById(userId);
}
works fine--> #RequestMapping(value="/{userId}/image", method=RequestMethod.GET, produces = MediaType.IMAGE_PNG_VALUE)
public byte[] getUserImage(#PathVariable Long userId) {
byte[] image = (imageRepsository.findImageWithUserId(userId)).getImage();
return image;
}
Here are entities :
User entity :
#Entity
#Table(name="users")
public class User {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#NotNull
#Size(max=100)
#Column
private String nom;
#NotNull
#Size(max=250)
#Column
private String prenom;
#OneToOne(fetch=FetchType.EAGER,
cascade = CascadeType.PERSIST)
private Image image;
//getters and setters
}
Image entity :
#Entity
#Table(name="images")
public class Image {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="image")
#Lob
private byte[] image;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="user_id")
private User user;
//getters and setters
}

in the annotation, produce set as MediaType.IMAGE_JPEG_VALUE, then your code return response as User object. As result it throw that exception because spring expect your code to return JPEG type file only.
What can i suggest here,use produces = MediaType.APPLICATION_JSON_VALUE, and convert your image from byte[] to base64 string then return response as json object

Related

how to properly design a controller and a jsp page for an entity that has three keys, two external and one internal?

I'm trying to make a Spring MVC application.I have 4 entities(Company,Pass_in_trip,Passenger,Trip) Pass_in_trip has 3 keys consisting of Passenger, Trip and Timestamp, I don't know how to properly issue a key and how to transfer it through the jsp page to the controller, and how to issue the controller itself, can anyone tell me?and also an interesting question is how to make a request to the database to search for a record using three keys.
Thanks
here's what I was able to write at the moment, see if there are any errors somewhere
#Entity
#Table(name="company")
public class Company implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name="id_comp")
private int id_comp;
#Column(name="name")
private String name;
//Getters and Setters
#Entity
#Table (name="pass_in_trip")
public class Pass_in_trip implements Serializable {
#EmbeddedId
private KeysPass_in_trip key=new KeysPass_in_trip();
#Column(name="place")
private String place;
//Getters and Setters
#Embeddable
public class KeysPass_in_trip implements Serializable{
#NotNull
#JoinColumn(name="date")
private Timestamp date=new Timestamp(System.currentTimeMillis());
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id_psg")
private Passenger id_psg=new Passenger();
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "trip_no" )
private Trip trip_no=new Trip();
//Getters and Setters
//#Override hashCode and equals
#Entity
#Table(name="passenger")
public class Passenger implements Serializable {
#Column(name="name")
private String name;
#NotNull
#Id
#Column(name="id_psg")
#GeneratedValue(strategy = IDENTITY)
private int id_psg;
//Getters and Setters
#Entity
#Table(name="trip")
public class Trip implements Serializable {
#NotNull
#Id
#Column(name="trip_no")
private int trip_no;
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name = "id_comp")
private Company comp=new Company();
#Column(name="plane")
private String plane;
#Column(name="town_from")
private String town_from;
#Column(name="town_to")
private String town_to;
#Column(name="time_out")
private Timestamp time_out;
#Column(name="time_in")
private Timestamp time_in;
//Getters and Setters
Conroller
#Controller
#RequestMapping("/pass_in_trip/")
public class Aero_Controller_Pass_in_trip {
#Autowired
private Aero_DAO service;
public void setService(Aero_DAO service) {
this.service = service;
}
#RequestMapping(method=RequestMethod.GET)
public String list(Model uiModel) {
List <Pass_in_trip> pass_in_trip=service.findallPass_in_trip();
uiModel.addAttribute("pass_in_trip",pass_in_trip);
return "/pass_in_trip/list";
}
#PreAuthorize("hasRole('ROLE_Admin')")
#RequestMapping(value="delete/{id}",method=RequestMethod.GET)
public String delete(#PathVariable("id")int id, Model uiModel) {
if(service.findByIdPass_in_Trip(id)!=null)
service.delete_Pass_in_trip(id);
return "redirect:/pass_in_trip/";
}
#PreAuthorize("hasRole('ROLE_Admin')")
#RequestMapping(value="update/{id}",method=RequestMethod.GET)
public String updateform(#PathVariable("id")int id, Model uiModel) {
System.out.println("upform");
uiModel.addAttribute("pass_in_trip",service.findByIdPass_in_Trip(id));
System.out.println("upform2");
return "/pass_in_trip/edit";
}
#RequestMapping(value="update/0",method=RequestMethod.GET)
public String newform(Model uiModel) {
System.out.println("Привет!");
return "/pass_in_trip/edit";
}
#PreAuthorize("hasRole('ROLE_Admin')")
#RequestMapping(value="update/{id}",method = RequestMethod.POST)
public String update(Pass_in_trip pass_in_trip,BindingResult bindingResult,Model uiModel,HttpServletRequest httprervletrequest , RedirectAttributes redirectatributes) {
if (bindingResult.hasErrors()) {
uiModel.addAttribute("pass_in_trip", pass_in_trip);
return "pass_in_trip/update";}
service.save(pass_in_trip);
return "redirect:/pass_in_trip/";
}
}
List.jsp
interested in this part:
<s:authorize access="hasRole('ROLE_Admin')">
<td> To change </td>
<td> Delete </td>
</s:authorize>

Why does not delete data in rest api

I am working on rest api. I got error while delete data by id. All code is complete but don't know why postman fire error. I can map two table with unidirectional mapping using hibernate.
Here down is error in postman:
"message": "Required request body is missing: public org.springframework.http.ResponseEntity<org.springframework.http.HttpStatus> com.rest.RestApiPojo.Controller.PojoController.deleteAddressPerson(com.rest.RestApiPojo.Entity.Person,java.lang.Integer)"
Here down is my code:
Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer person_id;
private String name;
#JsonManagedReference
#OneToOne(cascade = CascadeType.ALL, mappedBy = "person")
private Address address;
// getter setter
}
#Table(name = "address_master")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer address_id;
private String city;
private String country;
#JsonBackReference
#OneToOne(cascade=CascadeType.ALL, targetEntity = Person.class)
#JoinColumn(name = "person_id")
private Person person;
// getter setter
}
SeviceImpl
#Override
public void deleteAddressPerson(Integer personId) {
personRepo.deleteById(personId);
}
Controller
#RequestMapping(value = "/dltpersonaddress/{personId}", method = RequestMethod.DELETE)
public ResponseEntity<HttpStatus> deleteAddressPerson(#RequestBody Person person, #PathVariable Integer personId)
{
pojoService.deleteAddressPerson(personId);
return new ResponseEntity<>(HttpStatus.OK);
}
You have an unused #RequestBody Person person parameter in your controller method.
#RequestMapping(value = "/dltpersonaddress/{personId}", method = RequestMethod.DELETE)
public ResponseEntity<HttpStatus> deleteAddressPerson(#RequestBody Person person, #PathVariable Integer personId)
{
pojoService.deleteAddressPerson(personId);
return new ResponseEntity<>(HttpStatus.OK);
}
The error message explains that this param is obligatory, and requests without it wont be processed.
Remove the param to solve the issue.

Infinite recursion with #JsonManagedReference and #JsonBackReference

I have an entity class that is self referencing itself. For example, a document can be linked to a parent document.
#Entity
#Table(name = "documents")
public class DocumentEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#JsonIgnore
#JsonManagedReference
#ManyToOne(fetch = FetchType.LAZY)
private DocumentEntity parentDocument;
#JsonBackReference
#OneToMany(mappedBy = "parentDocument", fetch = FetchType.LAZY)
private Set<DocumentEntity> documents;
#Column(nullable = false, unique = true)
private String documentId;
#Column(nullable = false)
private String fileName;
}
In my entry point / controller layer :
#GetMapping(
path = "/{fileId}",
produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE }
)
public DocumentResponse getParentDocument(#PathVariable("fileId") String fileId) {
modelMapper = createModelMapper();
DocumentDto documentDto = documentService.getParentDocument(fileId);
DocumentResponse documentResponse = modelMapper.map(documentDto, DocumentResponse.class);
documentResponse.getDocuments().forEach(document -> System.out.println(document.getDocumentId()));
return documentResponse;
}
In my Service layer :
#Override
public DocumentDto getParentDocument(String documentId) {
DocumentDto documentDtoResponse = new DocumentDto();
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
DocumentEntity storedDocumentEntity =
documentRepository.findByDocumentIdAndParentDocumentNull(documentId);
if(storedDocumentEntity.getDocumentId().isEmpty() || storedDocumentEntity.getDocumentId().isBlank()) {
throw new AppFileNotFoundException("Oops file not found");
}
documentDtoResponse = modelMapper.map(storedDocumentEntity, DocumentDto.class);
return documentDtoResponse;
}
In the repository:
Now I'm making a sql request in a repository interface that extends JpaRepository.
The application allow to have a parent document with child documents and child documents cannot have child documents.
#Repository
public interface DocumentRepository extends JpaRepository<DocumentEntity, Long> {
DocumentEntity findByDocumentIdAndParentDocumentNull(String documentId);
}
I also tried to implement the method using JPQL :
#Query("SELECT d FROM DocumentEntity d WHERE d.documentId = :documentId AND d.parentDocument IS NULL")
DocumentEntity findByDocumentIdAndParentDocumentNull(String documentId);
This query allow to get parent documents and child documents.
My code implementation separates response and database by using a DTO layer.
Issue:
My issue is that I obtain an infinite recursion. I think i'm using #JsonManagedReference and #JsonBackReference correctly. Even adding the same annotations to DTO pojo do not solve issue. If i add those annotation to response POJO, then I do not obtain child documents.
Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException
Inially I have a DTO class that also self refers to itself.
public class DocumentDto implements Serializable {
private String filePath;
private String mimeType;
private String documentType;
private DocumentDto parentDocument;
Set<DocumentDto> documents;
}
I created a second class without properties that are causing problems;
public class DocumentChildDto implements Serializable {
private String filePath;
private String mimeType;
private String documentType;
}
In the DocumentDto I simply replaced the DocumentDto with DocumentChildDto.
public class DocumentDto implements Serializable {
private String filePath;
private String mimeType;
private String documentType;
private DocumentChildDto parentDocument;
Set<DocumentChildDto> documents;
}
It's more a hack than a technical solution but it works fine. Here childDocumentDto object won't load the parentDocument.

Spring Framework Responses from POST

What is the standard object design for accepting a POST request from a client, saving the record to the database, and then returning a response back to the client? I'm working with the Spring framework.
Should I be sending back the entity and hiding properties that aren't necessary for the response?
#RestController
public class SomeController {
private final SomeService service;
#PostMapping(value = "/post/new", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SomeEntity> post(#RequestBody final SomeEntity someEntity) {
SomeEntity savedEntity = service.save(someEntity);
return ResponseEntity.ok(savedEntity);
}
}
#Entity
#Table(name = "posts")
public class SomeEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "title")
private String title;
#Column(name = "body")
#JsonIgnore
private String body;
#JsonIgnore
#Column(name = "deleted_ind")
private boolean deleted;
#JsonIgnore
#Column(name = "author")
private String author;
#Column(name = "created_at")
private LocalDateTime createdAt;
}
or would I accept some sort of POST request object that I convert to an entity, then re-assemble the entity into a response?
#JsonIgnoreProperties(ignoreUnknown = true)
public class SomePostRequestResource {
private String title;
private String body;
private String createdAt;
}
#RequiredArgsConstructor
#RestController
public class SomeController {
private final SomeService service;
private final SomeResourceAssembler resourceAssembler;
#PostMapping(value = "/post/new", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SomePostRequestResource> post(
#RequestBody final SomePostRequestResource someResource
) {
SomeEntity savedEntity = service.convertToEntityAndSave(someResource);
SomePostRequestResource response = resourceAssembler.toResource(savedEntity);
return ResponseEntity.ok(response);
}
}
But then maybe I only want to send back the createdAt, would I hide the other properties in the SomePostRequestResource, or do I need another object to represent the response, which only has the property I want to send back?
I would also appreciate any book or article suggestions related to desigining objects for use with a RESTful API. I have seen articles concerning how to design and name the endpoints, but not so many concerning how to design the objects on the backend.
I would recommend you create a DTO class for the incoming/outgoing data containing the filed that are set/viewable by the client like:
public class SomeEntityIncomingDto {
private String title;
....
}
public class SomeEntityOutgoingDto {
private Long id;
private String title;
....
}
On the other hand, You won't need to map your persistence entities to DTOs and vice versa manually, you can use a library like ModelMapper or MapStruct that handles the conversion automatically.

Why the record is posted twice in the database?

Can you tell me, why the record is posted twice in the database. I think. this happens because I use save() method. But shouldn't I save the master-entity and dependent-entity separately?
Controller method:
#RequestMapping(value = "/addComment/{topicId}", method = RequestMethod.POST)
public String saveComment(#PathVariable int topicId, #ModelAttribute("newComment")Comment comment, BindingResult result, Model model){
Topic commentedTopic = topicService.findTopicByID(topicId);
commentedTopic.addComment(comment);
// TODO: Add a validator here
if (!comment.isValid() ){
return "//";
}
// Go to the "Show topic" page
commentService.saveComment(comment);
return "redirect:../details/" + topicService.saveTopic(commentedTopic);
}
Services:
#Service
#Transactional
public class CommentService {
#Autowired
private CommentRepository commentRepository;
public int saveComment(Comment comment){
return commentRepository.save(comment).getId();
}
}
#Service
#Transactional
public class TopicService {
#Autowired
private TopicRepository topicRepository;
public int saveTopic(Topic topic){
return topicRepository.save(topic).getId();
}
}
Model:
#Entity
#Table(name = "T_TOPIC")
public class Topic {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#ManyToOne
#JoinColumn(name="USER_ID")
private User author;
#Enumerated(EnumType.STRING)
private Tag topicTag;
private String name;
private String text;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)
private Collection<Comment> comments = new LinkedHashSet<Comment>();
}
#Entity
#Table(name = "T_COMMENT")
public class Comment
{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#ManyToOne
#JoinColumn(name="TOPIC_ID")
private Topic topic;
#ManyToOne
#JoinColumn(name="USER_ID")
private User author;
private String text;
private Date creationDate;
}
In this concrete case, you do not need to save the master and the client.
Saving the master or the client would be enough (with this concrete mapping)
But I think the main problem is that you do not have a good equals method in your Comment so your ORM Provider think that there are two different comments, and therefore store them twice.

Resources