HttpMessageNotWritableException in SpringBootTest - spring

I have created a controller which actually ends in an error in my testcase. There are few other controllers which I have made the same way and there the tests work. Currently I am looking for a solution but I am stuck since hours.
The following testcase fails because it results in an http error 500 instead of 200
#ActiveProfiles("Test")
#AutoConfigureMockMvc
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#Transactional
#SpringBootTest
. . .
#Test
public void whenCreateCustomer_ThenReturnIt() throws Exception {
String customerName = "foobar2";
MvcResult result = mvc.perform(post(REST_CUSTOMERS)
.header(HEADER_AUTH_KEY, authTokenAdminUser.getToken())
.contentType(MediaType.APPLICATION_JSON)
.content("{\n" +
" \"name\": \""+ customerName + "\"" +
"}")
)
.andExpect(status().isOk())
.andReturn();
String responseString = result.getResponse().getContentAsString();
CustomerEntity customer = objectMapper.readValue(responseString, CustomerEntity.class);
assertThat(customer).isNotNull();
assertThat(customer.getName()).isEqualTo(customerName);
assertThat(customer.getCreated()).isNotNull();
}
Here is the method under test. I have debugged it, and it looks good. The entity has been created and also it comes to the point where the ResponseEntity should return ok with the entity in its body. Also I have evaluated this return ResponseEntity.ok().body(createdCustomer.get()); in the debugger and it worked.
#Override
#PostMapping
public ResponseEntity<CustomerDTO> create(#RequestBody CustomerDTO dto) {
dto.setId(uidService.getNextUidScServer());
if (dto.getCreated() == null){
dto.setCreated(LocalDateTime.now());
}
Optional<CustomerDTO> createdCustomer = customerService.create(dto);
if (createdCustomer.isPresent()){
return ResponseEntity.ok().body(createdCustomer.get());
}
else{
return ResponseEntity.badRequest().build();
}
}
In the stacktrace I found this, which I think is my problem. But I have actually no idea how to solve it.
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotWritableException
Here is the entity
#NoArgsConstructor
#AllArgsConstructor
#Data
#Entity
#Builder
#Table(name = "Customer")
public class CustomerEntity {
#Id
#Column(name = "uid")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "created")
private LocalDateTime created;
#OneToMany(
mappedBy = "customer",
cascade = CascadeType.ALL,
orphanRemoval = true)
List<PenEntity> pen;
Here the dto
#Data
#AllArgsConstructor
#NoArgsConstructor
public class CustomerDTO {
private Long id;
private String name;
#JsonDeserialize(using = LocalDateDeserializer.class)
#JsonSerialize(using = LocalDateSerializer.class)
private LocalDateTime created;
#JsonIgnore
private List<PenEntity> pen;
}
From the error message it looks like there is a problem with the json mapping. Any idea?

For the message converter, and you are not using the correct Serializer. You must use the LocalDateTimeSerializer and not the LocalDateSerializer.
#Data
#AllArgsConstructor
#NoArgsConstructor
public class CustomerDTO {
private Long id;
private String name;
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
#JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime created;
#JsonIgnore
private List<PenEntity> pen;
}
You said you used breakpoints. For the future, if you have an exception you don"t understand, use a breakpoint in the constructors of this exception. Then, use the stack trace to identify which class throws the exception, and how you can fix it.
In my debugger, I saw this:
"Could not write JSON: class java.time.LocalDateTime cannot be cast to class java.time.LocalDate (java.time.LocalDateTime and java.time.LocalDate are in module java.base of loader 'bootstrap')"
You are also suppose to have getters, but as I understood, #Data from Lombok generates them. So you should be good.

Related

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 data mongo No property b found on entity class when retrieving entity by Id

I have a parent class that has some shared fields and a child class that extends it.
#SuperBuilder(toBuilder = true)
#Data
public abstract class MultiTenantAuthoredDocument {
#Indexed
private String tenantId;
#CreatedDate
private LocalDateTime createdDate;
#LastModifiedDate
private LocalDateTime lastModifiedDate;
}
#Document(collection = "users")
#SuperBuilder(toBuilder = true)
#Data
#EqualsAndHashCode(callSuper = true)
#ToString(callSuper = true)
public class User extends MultiTenantAuthoredDocument {
#Id
private String id;
private String firstName;
private String lastName;
#Indexed
private String password;
#Indexed(unique = true)
private String userName;
#Indexed(unique = true)
private String email;
#Indexed
private List<UserRole> roles;
#Builder.Default
private boolean enabled = false;
}
However when running my unit tests, I get an unexpected exception when I do a findById and there's a result found namely:
No property b found on entity class be.moesmedia.erp.users.domain.User to bind constructor parameter to!
As I have no clue where property b is coming from it's pretty difficult to see what I'm doing wrong.
If anyone can help me point out what I'm doing wrong.
So I've figured out what was going wrong, Lombok generated a constructor that accepted an Object with the properties for the SuperBuilder class. Once I added #NoArgsConstructorto both the child and parent class, it works like a charm.

How to show object's update history with Auditing?

I've got a problem, I made a CRUD in springboot with MYSQL and now I want to create a method which will return update history of my object...
I have class like:
#Entity
#Table
#EntityListeners(AuditingEntityListener.class)
#JsonIgnoreProperties(value = {"createdAt", "updatedAt"}, allowGetters = true)
#Audited
public class Note implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Getter
#Setter
private Long id;
#NotBlank
#Getter
#Setter
private String title;
#Version
#Getter
#Setter
private long version;
#NotBlank
#Getter
#Setter
private String content;
#Column(nullable = false, updatable = false)
#Temporal(TemporalType.TIMESTAMP)
#CreatedDate
#Getter
#Setter
private Date createdAt;
#Column(nullable = false)
#Temporal(TemporalType.TIMESTAMP)
#LastModifiedDate
#Getter
#Setter
private Date updatedAt;
}
But I don't know how can I now create a HTTP call to show that history of updates by #Audited.
I found something like this: Find max revision of each entity less than or equal to given revision with envers
But I don't know how to implement it in my project...
#RestController
#RequestMapping("/api")
public class NoteController
{
#Autowired
NoteRevisionService noteRevisionService;
#Autowired
NoteRepository noteRepository;
// Get All Notes
#GetMapping("/notes")
public List<Note> getAllNotes() {
return noteRepository.findAll();
}
// Create a new Note
#PostMapping("/notes")
public Note createNote(#Valid #RequestBody Note note) {
return noteRepository.save(note);
}
// Get a Single Note
#GetMapping("/notes/{id}")
public Note getNoteById(#PathVariable(value = "id") Long noteId) {
return noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
}
#GetMapping("/notes/{id}/version")
public List<?> getVersions(#PathVariable(value = "id") Long noteId)
{
return noteRevisionService.getNoteUpdates(noteId);
}
// Update a Note
#PutMapping("/notes/{id}")
public Note updateNote(#PathVariable(value = "id") Long noteId,
#Valid #RequestBody Note noteDetails) {
Note note = noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
note.setTitle(noteDetails.getTitle());
note.setContent(noteDetails.getContent());
Note updatedNote = noteRepository.save(note);
return updatedNote;
}
// Delete a Note
#DeleteMapping("/notes/{id}")
public ResponseEntity<?> deleteNote(#PathVariable(value = "id") Long noteId) {
Note note = noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
noteRepository.delete(note);
return ResponseEntity.ok().build();
}
}
getVersions its the call of function which Joe Doe sent me.
There: Repository
#Repository
public interface NoteRepository extends JpaRepository<Note, Long>
{
}
You can use AuditQuery for this. The getNoteUpdates method below returns a list of mappings. Each mapping contains an object state and the time of the update that led to that state.
#Service
#Transactional
public class NoteRevisionService {
private static final Logger logger = LoggerFactory.getLogger(NoteRevisionService.class);
#PersistenceContext
private EntityManager entityManager;
#SuppressWarnings("unchecked")
public List<Map.Entry<Note, Date>> getNoteUpdates(Long noteId) {
AuditReader auditReader = AuditReaderFactory.get(entityManager);
AuditQuery query = auditReader.createQuery()
.forRevisionsOfEntity(Note.class, false, false)
.add(AuditEntity.id().eq(noteId)) // if you remove this line, you'll get an update history of all Notes
.add(AuditEntity.revisionType().eq(RevisionType.MOD)); // we're only interested in MODifications
List<Object[]> revisions = (List<Object[]>) query.getResultList();
List<Map.Entry<Note, Date>> results = new ArrayList<>();
for (Object[] result : revisions) {
Note note = (Note) result[0];
DefaultRevisionEntity revisionEntity = (DefaultRevisionEntity) result[1];
logger.info("The content of the note updated at {} was {}", revisionEntity.getRevisionDate(), note.getContent());
results.add(new SimpleEntry<>(note, revisionEntity.getRevisionDate()));
}
return results;
}
}
Note that if you can restrict the query somehow (for example by filtering on a property), you should definitely do it, because otherwise performing the query can have a negative impact on the performance of your entire application (the size of the returned list might be huge if this object was often updated).
Since the class has been annotated with the #Service annotation, you can inject/autowire NoteRevisionService like any other regular Spring bean, particularly in a controller that handles a GET request and delegates to that service.
UPDATE
I didn't know that extra steps had to be taken to serialize a list of map entries. There may be a better solution but the following approach gets the job done and you can customize the format of the output revisionDate with a simple annotation.
You need to define another class, say NoteUpdatePair, like so:
public class NoteUpdatePair {
private Note note;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private Date revisionDate; // this field is of type java.util.Date (not java.sql.Date)
NoteUpdatePair() {}
public NoteUpdatePair(Note note, Date revisionDate) {
this.note = note;
this.revisionDate = revisionDate;
}
public Note getNote() {
return note;
}
public void setNote(Note note) {
this.note = note;
}
public Date getRevisionDate() {
return revisionDate;
}
public void setRevisionDate(Date revisionDate) {
this.revisionDate = revisionDate;
}
}
and now, instead of returning a list of map entries, you'll return a list of NodeUpdatePair objects:
#Service
#Transactional
public class NoteRevisionService {
private static final Logger logger = LoggerFactory.getLogger(NoteRevisionService.class);
#PersistenceContext
private EntityManager entityManager;
#SuppressWarnings("unchecked")
public List<NoteUpdatePair> getNoteUpdates(Long noteId) {
AuditReader auditReader = AuditReaderFactory.get(entityManager);
AuditQuery query = auditReader.createQuery()
.forRevisionsOfEntity(Note.class, false, false)
.add(AuditEntity.id().eq(noteId)) // if you remove this line, you'll get an update history of all Notes
.add(AuditEntity.revisionType().eq(RevisionType.MOD)); // we're only interested in MODifications
List<Object[]> revisions = (List<Object[]>) query.getResultList();
List<NoteUpdatePair> results = new ArrayList<>();
for (Object[] result : revisions) {
Note note = (Note) result[0];
DefaultRevisionEntity revisionEntity = (DefaultRevisionEntity) result[1];
logger.info("The content was {}, updated at {}", note.getContent(), revisionEntity.getRevisionDate());
results.add(new NoteUpdatePair(note, revisionEntity.getRevisionDate()));
}
return results;
}
}
Regarding your question about the service's usage, I can see that you've already autowired it into your controller, so all you need to do is expose an appropriate method in your NoteController:
#RestController
#RequestMapping("/api")
public class NoteController {
#Autowired
private NoteRevisionService revisionService;
/*
the rest of your code...
*/
#GetMapping("/notes/{noteId}/updates")
public List<NoteUpdatePair> getNoteUpdates(#PathVariable Long noteId) {
return revisionService.getNoteUpdates(noteId);
}
}
Now when you send a GET request to ~/api/notes/1/updates (assuming nodeId is valid), the output should be properly serialized.

converting URI to entity with custom controller in spring data rest?

i have an jpa entity like this.
#Entity
#Table(name = "location")
#Data
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "LOCATION_ID", unique = true)
#NotEmpty(message = "Please Enter Location ID")
private String name;
#Column(name = "LOCATION_DESCRIPTION")
#NotEmpty(message = "Please Enter Location Description")
private String description;
#ManyToOne
#NotNull(message = "Please Choose a Building")
Building building;
#Version
Long version;
}
and the repository like this.
public interface LocationRepository extends PagingAndSortingRepository<Location, Long> {
Location findByName(#Param("name") String name);
}
i am using spring data rest i am able to create location with rest api by providing the following payload
{
"name":"adminxxxxx","description":"adminxxx" , "building": "http://localhost:8080/buildings/2"
}
now i am trying to write my custom controller which will persist the entity. this is my custom controller
#ExposesResourceFor(Location.class)
#RepositoryRestController
#BasePathAwareController
public class LocationController {
#Autowired
LocationRepository locationDao;
#Autowired
LocationResourceAssembler resourceAssembler;
#Value("${buildings.error.messages.uniqueconstraintviolation}")
String uniqueConstrainMessage;
static final String TAG = LocationController.class.getSimpleName();
#RequestMapping(value="locations",method = org.springframework.web.bind.annotation.RequestMethod.POST)
public ResponseEntity<?> save(#RequestBody #Valid Location location) {
try {
location = locationDao.save(location);
LocationResource b = resourceAssembler.toResource(location);
return ResponseEntity.ok().body(b);
} catch (DataIntegrityViolationException e) {
if (locationAlreadyExists(location.getName()))
throw new LocationAlreadyExistException(uniqueConstrainMessage, location);
else
throw new RuntimeException("Some Error Occured");
}
}
i am getting this error
exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.alamdar.model.Building: no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/buildings/2')
at [Source: java.io.PushbackInputStream#5d468b16; line: 3, column: 60] (through reference chain: com.alamdar.model.Location["building"])</div></body></html>
can anyone please help?
I am not sure why you are writing a custom controller however the issue would appear to be that you do not have a default no args constructor so Jackson cannot instantiate an instance.
This is because you are using Lombok's #Data annotation:
https://projectlombok.org/features/Data.html
You should also annotate you class with #NoArgsConstructor to have a default no-args constructor generated:
#Entity
#Table(name = "location")
#Data
#NoArgsConstructor
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "LOCATION_ID", unique = true)
#NotEmpty(message = "Please Enter Location ID")
private String name;
#Column(name = "LOCATION_DESCRIPTION")
#NotEmpty(message = "Please Enter Location Description")
private String description;
#ManyToOne
#NotNull(message = "Please Choose a Building")
Building building;
#Version
Long version;
}

How to delete entity from database

i am new on hibernate-spring tirple..
i just try to code simple register book.. i have following codes:
Student.java
#Entity(name = "STUDENTS")
#NamedQueries({
#NamedQuery(name = "getAllStudent", query = "SELECT k FROM STUDENTS k ORDER BY k.id DESC"),
#NamedQuery(name = "findByName", query = "SELECT k FROM STUDENTS k WHERE k.name LIKE :name")
})
public class Student {
#Column(name = "STUDENTNO", nullable = false)
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "NAME", nullable = false)
private String name;
#Column(name = "SURNAME")
private String surname;
#Column(name = "AGE")
private String age;
// GET ve SET metods
StduentDAO.java
#Repository
#Transactional(readOnly = true)
public class StudentDAO implements IStudentDAO {
#PersistenceContext
EntityManager em;
#Override
public void deleteStudent(Student student) {
Student temp = em.getReference(Student.class, student.getId());
em.remove(temp);
System.out.println("### getting out from studentDAO deleteStudent method ###")
StudentController.java
#Component
#Scope(value = "request")
public class StudentController {
#Autowired
IStudentDAO studentDAO;
List<Student> allStudentList = new ArrayList();
Student student = new Student();
#PostConstruct
private void loadStudents() {
allStudentList = studentDAO.allStudent();
public void deleteStudent() {
studentDAO.deleteStudent(student);
System.out.println("### getting out from StudentController deleteStudent method ### ");
}
When I run deleteStudent() codes i am getting:
"### getting out from studentDAO deleteStudent method ###"
"### getting out from StudentController deleteStudent method ### "
i see these on output but nothing is deleting from database.. i searched a bit and i found this "every entitiy manager's methods open own session." that is why it says i should write my StudentDAO's deleteStudent methof like above..
i think i am missing something about transaciton but i have not recognized yet..
what should i do about this ?
Thanks..
#Transactional annotation create a transaction on your DBMS.
If you use (readOnly = true) you prevent operation on your DB (as INSERT/UPDATE/DELETE).
Remove readOnly = true so your delete method will work.

Resources