java.lang.StackOverflowError: null for the jpa saveAll - java

I am getting these error while integrating the spring-boot with JPA repository
here is the code
#CrossOrigin(origins = "http://localhost:4200")
#RestController
#RequestMapping("/api")
public class EmpController {
#Autowired
private CrudRepo crud;
#Autowired
private AddrCrudRepo addr;
#Autowired
private EntityManager entity;
//#Autowired
//private ModelMapper mapper;
private static int count = 0;
#Bean
public ModelMapper model() {
return new ModelMapper();
}
//#Autowired
// public EmpController(ModelMapper mapper) {
// this.mapper = mapper;
// }
#RequestMapping(path = "/post-addr", method = RequestMethod.POST)
public List<AddressModel> postAddr(#Valid #RequestBody List<AddressRequest> addr1){
// crud.findById(id)
//AddressModel list = new AddressModel();
EmployeeModel emp = new EmployeeModel();
System.out.println("CALLING THE MAPPER "+addr1);
List<AddressModel> addr_list = ObjectMapperUtils.mapAll(addr1, AddressModel.class);
System.out.println("CALLED THE MAPPER "+addr_list);
addr_list.forEach((a) -> {
crud.findById(a.getEmpId()).ifPresent((b) -> {
System.out.println(this.count++);
a.setEmp_id(b);
b.getAddress().add(a);
});
});
// AddressModel addr_list = model().map(addr1, AddressModel.class);
//
// crud.findById(addr1.getEmp_id()).ifPresent((b) -> {
// addr_list.setEmp_id(b);
//
// });`enter code here`
System.out.println(addr_list.size());
List<AddressModel> addr3 = addr.saveAll(addr_list);
System.out.println(addr3);
return addr_list;
}
getting an error in the postAddr method as when it returns the List<AddressModel> and here is the AddressModel
#Entity
#Table(name="Emp_Address")
public class AddressModel implements Serializable{
#Column(name="address_id")
#Id
private Integer address_id;
#Column(name="city")
private String city;
#Column(name="states")
private String states;
#Transient
private Integer empId;
#ManyToOne
#JoinColumn(name="emp_id")
private EmployeeModel emp_id;
public AddressModel() {
}
//getter and setter
and EmployeeModel
#Entity
#Table(name="Employee")
public class EmployeeModel implements Serializable{
#Column(name="Emp_id")
#Id
private Integer emp_id;
#Column(name="Emp_Name")
private String emp_name;
#OneToMany(mappedBy="emp_id")
private Collection<AddressModel> address = new ArrayList<>();
public EmployeeModel() {
}
//getter and setters
so while saveAll is done properly but when the postAddr method returns the List it throws the StackOverflow

This StackOverflow error is coming because generated toString methods of both classes are circularly dependent on each other.
EmployeeModel tries to print AddressModel but again AddressModel tries to print EmployeeModel and hence the error.
Try removing AddressModel from toString method of EmployeeModel class or reverse, remove EmployeeModel from toString method of AddressModel class.

Related

Cloudinary image upload not persisting image and returning null value in spring boot app

I'm working on a spring boot ecommerce app that requires cloudinary to persist image and get using the url.
However, all effort to get this done has been proved abortive. The code is not throwing any error but its not persisting in the cloudinary page and the database. And the response is null.
This is a response for example. Meanwhile i expect a link in the form of String
{
"productName": "Track suit",
"price": 300,
"productDescription": "XXL",
"productImage1": "",
"productImage2": "",
"productImage3": ""
}
This is my code
ENTITY
#Entity
public class Product {
#Id
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
private String id;
#Column
private String productName;
#Column
private double price;
#Column
private String productDescription;
#Column(nullable = false)
private String productImage1;
#Column(nullable = true)
private String productImage2;
#Column(nullable = true)
private String productImage3;
private LocalDateTime createdDate;
private LocalDateTime updatedDate;
#ManyToOne
#JoinColumn(name = "admin_id")
private Admin admin;
#ManyToOne
#JoinColumn(name = "category_id")
private Category category;
#ManyToOne
#JoinColumn(name = "users_entity_id")
private UsersEntity usersEntity;
}
REQUEST DTO
#Data
public class UploadProductRequestDto {
private String productName;
private double price;
private String productDescription;
private MultipartFile productImage1;
private MultipartFile productImage2;
private MultipartFile productImage3;
}
RESPONSE DTO
#Data
public class UploadProductResponseDto {
private String productName;
private double price;
private String productDescription;
private String productImage1;
private String productImage2;
private String productImage3;
}
REPOSITORY
public interface ProductRepository extends JpaRepository<Product,String> {
Optional<Product> findByProductName(String productName);
}
SERVICE
public interface ProductService {
UploadProductResponseDto uploadProducts(UploadProductRequestDto uploadProductRequestDto, String categoryName) throws AuthorizationException, GeneralServiceException, ImageUploadException;
}
SERVICEIMPL
#Slf4j
#Service
public class ProductServiceImpl implements ProductService {
#Autowired
CloudStorageService cloudStorageService;
#Autowired
AdminRepository adminRepository;
#Autowired
CategoryRepository categoryRepository;
#Autowired
PasswordEncoder passwordEncoder;
#Autowired
ModelMapper modelMapper;
#Autowired
UserPrincipalService userPrincipalService;
#Autowired
UserRepository userRepository;
#Autowired
ProductRepository productRepository;
#Override
public UploadProductResponseDto uploadProducts(UploadProductRequestDto uploadProductRequestDto, StringcategoryName) throws AuthorizationException, GeneralServiceException, ImageUploadException {
Optional<Category> checkCategory = categoryRepository.findByCategoryName(categoryName);
if (checkCategory.isEmpty()){
throw new AuthorizationException(CATEGORY_NOT_RECOGNIZED);
}
Product product = new Product();
product=mapAdminRequestDtoToProduct(uploadProductRequestDto,product);
productRepository.save(product);
UploadProductResponseDto adminUploadProductResponseDto = packageAdminProductUploadResponseDTO(product);
return adminUploadProductResponseDto;
}
private UploadProductResponseDto packageAdminProductUploadResponseDTO(Product product){
UploadProductResponseDto uploadProductResponseDto=new UploadProductResponseDto();
modelMapper.map(product,uploadProductResponseDto);
return uploadProductResponseDto;
}
private Product mapAdminRequestDtoToProduct(UploadProductRequestDto uploadProductRequestDto,Product product) throws ImageUploadException {
modelMapper.map(uploadProductRequestDto,product);
product=uploadProductImagesToCloudinaryAndSaveUrl(uploadProductRequestDto,product);
product.setId("Product "+ IdGenerator.generateId());
return product;
}
private Product uploadProductImagesToCloudinaryAndSaveUrl(UploadProductRequestDto uploadProductRequestDto,Product product) throws ImageUploadException {
product.setProductImage1(imageUrlFromCloudinary(uploadProductRequestDto.getProductImage1()));
product.setProductImage2(imageUrlFromCloudinary(uploadProductRequestDto.getProductImage2()));
product.setProductImage3(imageUrlFromCloudinary(uploadProductRequestDto.getProductImage3()));
return product;
}
private String imageUrlFromCloudinary(MultipartFile image) throws ImageUploadException {
String imageUrl="";
if(image!=null && !image.isEmpty()){
Map<Object,Object> params=new HashMap<>();
params.put("public_id","E&L/"+extractFileName(image.getName()));
params.put("overwrite",true);
try{
Map<?,?> uploadResult = cloudStorageService.uploadImage(image,params);
imageUrl= String.valueOf(uploadResult.get("url"));
}catch (IOException e){
e.printStackTrace();
throw new ImageUploadException("Error uploading images,vehicle upload failed");
}
}
return imageUrl;
}
private String extractFileName(String fileName){
return fileName.split("\\.")[0];
}
}
Controller
#Slf4j
#RestController
#RequestMapping(ApiRoutes.ENMASSE)
public class ProductController {
#Autowired
ProductService productService;
#PostMapping("/upload-product/categoryName")
public ResponseEntity<?> UploadProduct(#ModelAttribute UploadProductRequestDto UploadProductRequestDto,#RequestParam String categoryName){
try{
return new ResponseEntity<>
(productService.uploadProducts(UploadProductRequestDto,categoryName), HttpStatus.OK);
}catch (Exception exception){
return new ResponseEntity<>(exception.getMessage(),HttpStatus.BAD_REQUEST);
}
}
}
CLOUD
cloudConfig
#Component
#Data
public class CloudinaryConfig {
#Value("${CLOUD_NAME}")
private String cloudName;
#Value("${API_KEY}")
private String apikey;
#Value("${API_SECRET}")
private String secretKey;
}
CloudConfiguration
#Component
public class CloudinaryConfiguration {
#Autowired
CloudinaryConfig cloudinaryConfig;
#Bean
public Cloudinary getCloudinaryConfig(){
return new Cloudinary(ObjectUtils.asMap("cloud_name",cloudinaryConfig.getCloudName(),
"api_key",cloudinaryConfig.getApikey(),"api_secret",cloudinaryConfig.getSecretKey()));
}
}
CloudinaryStorageServiceImpl
#Service
public class CloudinaryStorageServiceImpl implements CloudStorageService{
#Autowired
Cloudinary cloudinary;
#Override
public Map<?, ?> uploadImage(File file, Map<?, ?> imageProperties) throws IOException {
return cloudinary.uploader().upload(file,imageProperties);
}
#Override
public Map<?, ?> uploadImage(MultipartFile multipartFile, Map<?, ?> imageProperties) throws IOException {
return cloudinary.uploader().upload(multipartFile.getBytes(),imageProperties);
}
}
CloudStorageService
public interface CloudStorageService {
Map<?,?> uploadImage(File file, Map<?,?> imageProperties) throws IOException;
Map<?,?> uploadImage(MultipartFile multipartFile, Map<?, ?> imageProperties) throws IOException;
}
You didn't include the implementation of cloudStorageService.uploadImage(?,?) in the code you pasted here.
Cloudinary's Java implementation requires you pass in the multipart file in bytes. I do not know if you have that since your upload method isn't here.
Maybe refer to the simple implementation here
PS: You can clone the repo to see the implementation of the upload method in the CloudinaryServiceImpl.
It happens that there is nothing wrong with my code. The issue is that it has to be coupled with the frontend so the image tag can render it. Thank you.

repoistory.save() getting invoked with invalid entry when unit testing

I'm using java validation API to validate fields in my Note class:
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "note")
public class Note {
#Id
#Column(name = "id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "date", columnDefinition = "DATE")
private LocalDate date;
#NotBlank(message = "Enter a topic")
#Column(name = "topic")
private String topic;
#NotBlank(message = "Content can't be empty")
#Column(name = "content")
private String content;
#Column(name = "type")
private NoteType noteType;
#NotNull
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name = "user_id")
#JsonIgnore
private User user;
}
NoteService:
#Service
#AllArgsConstructor
public class NoteService {
#Autowired
private NoteRepository noteRepository;
#Autowired
private UserRepository userRepository;
public void addNote(#Valid Note note) {
note.setUser(getLoggedInUser());
if (validateNote(note)) {
noteRepository.save(note);
}
}
public List<Note> getNotes() {
return getLoggedInUser().getNotes();
}
public Note editNote(Note newNote, Long id) {
noteRepository.editNoteById(newNote, id);
return newNote;
}
public List<Note> getNotesByTopic(String topic) {
List<Note> notes = noteRepository.getNotesByTopicAndUser(topic, getLoggedInUser());
return notes;
}
public boolean validateNote(Note note) {
return validateNoteType(note.getNoteType())
&& note.getDate() != null;
}
public boolean validateNoteType(NoteType type) {
return type.equals(NoteType.NOTE)
|| type.equals(NoteType.SKILL);
}
public User getLoggedInUser() {
return userRepository.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName());
}
}
Test:
#ExtendWith(MockitoExtension.class)
#ExtendWith(SpringExtension.class)
class NoteServiceTest {
#Mock
private NoteRepository noteRepositoryMock;
#Mock
private UserRepository userRepositoryMock;
#Mock
SecurityContext mockSecurityContext;
#Mock
Authentication authentication;
private NoteService noteService;
#BeforeEach
void setUp() {
noteService = new NoteService(noteRepositoryMock, userRepositoryMock);
Mockito.when(mockSecurityContext.getAuthentication()).thenReturn(authentication);
SecurityContextHolder.setContext(mockSecurityContext);
}
#Test
void shouldAddNote() {
LocalDate date = LocalDate.now();
Note note = new Note(0L, date, "test", "", NoteType.NOTE, null);
noteService.addNote(note);
Mockito.verify(noteRepositoryMock).save(note);
}
}
The field user in the Note class is annotated with #NotNull and I'm passing a null user to this note but the note is still getting saved. Same thing when I pass an empty string. Any idea why that is happening? I'm new to unit testing
I'm new to unit testing - your perfectly valid question has nothing to do with unit testing.
#NotNull does nothing on it own. Its actually a contract stating the following:
A data member (or anything else annotated with #NotNull like local variables, and parameters) can't be should not be null.
For example, instead of this:
/**
* #param obj should not be null
*/
public void MyShinyMethod(Object obj)
{
// Some code goes here.
}
You can write this:
public void MyShinyMethod(#NotNull Object obj)
{
// Some code goes here.
}
P.S.
It is usually appropriate to use some kind of annotation processor at compile time, or something that processes it at runtime. But I don't really know much about annotation processing. But I am sure Google knows :-)
You need to activate validation on you service class with the #Validated annotation so the validation of parameters kicks in.
#Service
#AllArgsConstructor
#Validated
public class NoteService {
...
See Spring #Validated in service layer and Spring Boot: How to test a service in JUnit with #Validated annotation? for more details.
If for some reason you need to manually perform the validation you can always do something like this:
#Component
public class MyValidationImpl {
private final LocalValidatorFactoryBean validator;
public MyValidationImpl (LocalValidatorFactoryBean validator) {
this.validator = validator;
}
public void validate(Object o) {
Set<ConstraintViolation<Object>> set = validator.validate(o);
if (!set.isEmpty()) {
throw new IllegalArgumentException(
set.stream().map(x -> String.join(" ", x.getPropertyPath().toString(), x.getMessage())).collect(
Collectors.joining("\n\t")));
}
}
}
So your noteRepository is Mocked, so you it's not actually calling save on your repository.
Mockito.verify(noteRepositoryMock).save(note);
All you are verifying here is that a call to save is made, not that it was successful.

Post Method using DTO

I want to use DTO to communicate with the Angular, but actually it doesn't work. I want to create POST request to add data from my application to the database using Dto model.
You can see my errors on the picture:
My class Customer:
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "name")
private String name;
#OneToMany
private List<Ticket> ticket;
...
Class CustomerDto:
public class CustomerDto {
private String name;
private List<TicketDto> ticket;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<TicketDto> getTicket() {
return ticket;
}
public void setTicket(List<TicketDto> ticket) {
this.ticket = ticket;
}
}
Class CustomerController:
#Autowired
CustomerService customerService;
#PostMapping(value = "/customers/create")
public Customer postCustomer(#RequestBody CustomerDto customerDto, List<TicketDto> ticketDtos) {
//ArrayList<TicketDto> tickets = new ArrayList<>();
ticketDtos.add(customerDto.getName());
ticketDtos.add(customerDto.getTicket());
Customer _customer = customerService.save(new Customer(customerDto.getName(), ticketDtos ));
return _customer;
}
CustomerService:
public interface CustomerService {
void save(CustomerDto customerDto, List<TicketDto> ticketDtos);
}
CustomerServiceImpl:
#Service
public class CustomerServiceImpl implements CustomerService {
#Autowired
CustomerRepository repository;
#Override
public void save(CustomerDto customerDto, List<TicketDto> ticketDtos) {
Customer customer = new Customer();
customer.setName(customerDto.getName());
customer.setTicket(customerDto.getTicket());
List<Ticket> tickets = new ArrayList<>();
for (TicketDto ticketDto : ticketDtos) {
Ticket ticket = new Ticket();
ticket.setDestinationCity(ticketDto.getDepartureCity());
ticket.setDestinationCity(ticketDto.getDestinationCity());
tickets.add(ticket);
}
}
Since you CustomerServiceImpl is taking CustomerDto and list of TicketDtos, you need to change your method call on controller as below:
Class CustomerController:
#Autowired
CustomerService customerService;
#PostMapping(value = "/customers/create")
public Customer postCustomer(#RequestBody CustomerDto customerDto) {
Customer _customer = customerService.save(customerDto));
return _customer;
}
And update CustomerServiceImpl as:
#Service
public class CustomerServiceImpl implements CustomerService {
#Autowired
CustomerRepository repository;
// change save to return saved customer
#Override
public Customer save(CustomerDto customerDto) {
Customer customer = new Customer();
customer.setName(customerDto.getName());
// customer.setTicket(customerDto.getTicket()); // remove this
List<Ticket> tickets = new ArrayList<>();
for (TicketDto ticketDto : customerDto.getTicketDtos) {
Ticket ticket = new Ticket();
ticket.setDestinationCity(ticketDto.getDepartureCity());
ticket.setDestinationCity(ticketDto.getDestinationCity());
tickets.add(ticket);
}
customer.setTickets(tickets); // add this to set tickets on customer
return repository.save(customer);
}
Obviously, you need to change your interface as well:
public interface CustomerService {
Customer save(CustomerDto customerDto);
}
For entity-DTO conversion, we need to use ModelMapper or mapstruct library.
With the help of these libraries, we can easily convert from Dto to entity and entity to dto object. After adding any of the dependency, We are able to use it.
How can we use, Let see...
Define modelMapper bean in spring configuration.
#Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
Suppose we need to convert List to List obj then we can perform simply like that :
List<TicketDto> ticketDtos = .... //Suppose It is holding some data
List<Ticket> tickets = ticketDtos.stream()
.map(tkt-> mappper.map(tkt, ticket.class))
.collect(Collectors.toList());
It is very simple to use like mappper.map(targetClass, DestinationClass.class)
I used Java8 code here but you can use anyone. I hope It would be very helpful to you.

Spring Async breaks JPA - detached entity passed to persist

Using H2 and JPA my REST app worked well before Ansyc, but after implementation breaks the JPA persistence model.
Here is the case:
My repository has a method JpaRepository.save() but when called from a separate thread, it throws InvalidDataAccessApiUsageException error.
My Controller calls the Service which calls the Repository to insert a new object, and I get the following error:
InvalidDataAccessApiUsageException: detached entity passed to persist: TransactionalEntity; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: TransactionalEntity]
CONTROLLER:
#Autowired
#Qualifier("depositIntentService")
private TransactionIntentService depositIntentService;
#PostMapping("/services/transactions/deposit")
public CompletableFuture<ResponseEntity<TransactionIntent>> deposit(#Valid #RequestBody TransactionClientRequest request) {
CompletableFuture<TransactionIntent> depositIntentFuture =
transactionIntentFactory.createDepositIntent(
request.entity.id,
Money.of(CurrencyUnit.of(request.money.currency), request.money.amount));
return depositIntentFuture.thenApply(intent -> {
TransactionIntent publishedIntent = depositIntentService.attemptPublish(intent); //<-- causes error
ResponseEntity.ok(publishedIntent)
});
}
SERVICE:
#Component
#Repository
public abstract class TransactionIntentServiceImpl implements TransactionIntentService{
#Autowired
private TransactionIntentRepository transactionIntentRepo;
#Transactional
public TransactionIntent attemptPublish(TransactionIntent intent){
transactionIntentRepo.save(intent); //<-- Throws error: ...detached entity passed to persist
}
}
REPOSITORY
#Repository
public interface TransactionIntentRepository extends JpaRepository<TransactionIntent, Long>{
}
Any ideas how to support JPA persistance in an Async environment?
Thanks!
Update1
FACTORY
#Component
public class TransactionIntentFactory {
#Autowired
private UserService userService;
#Async("asyncExecutor")
public CompletableFuture<TransactionIntent> createDepositIntent(long beneficiaryId, Money money) {
CompletableFuture<User> bank = userService.findByUsername("bankItself#bank.com");
CompletableFuture<User> user = userService.find(beneficiaryId);
CompletableFuture<Void> allUserFutures = CompletableFuture.allOf(bank, user);
return allUserFutures.thenApply(it -> {
User userSource = bank.join();
User userBeneficiary = user.join();
TransactionIntent intent = new TransactionIntentBuilder()
.status(new TransactionIntentStatus(TRANSFER_STATUS.CREATED, "Deposit"))
.beneficiary(userBeneficiary)
.source(userSource)
.amount(money)
.build();
return intent;
});
}
}
ENTITY
#Entity
public class TransactionIntent {
#Id
#GeneratedValue
public long id;
public final Money amount;
public final Date createdAt;
#OneToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
public final TransactionIntentStatus status;
#OneToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
public final TransactionalEntity beneficiary; //to
#OneToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
public final TransactionalEntity source; //from
TransactionIntent(){
this.amount= null;
this.createdAt = null;
this.status = null;
this.beneficiary = null;
this.source = null;
}
public TransactionIntent(TransactionIntentBuilder builder) {
this.amount = builder.amount;
this.createdAt = new Date();
this.status = builder.status;
this.beneficiary = builder.beneficiary;
this.source = builder.source;
}
}

Hibernate #Formula returns an old value in PUT response

My Spring boot app has 2 Entities - Document and Card. Card has column dtFrom. Clients have to work with column daysOnDtConfirm (Document.dtConfirm - dtFrom). Annotation #Formula for GET requests works great, but in PUT response returns an old value of daysOnDtConfirm. How return a new value?
#Entity
#Table(name="document")
public class Document extends BaseEntity{
private String name;
#Column(name = "dt_confirm")
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
#JsonFormat(shape = JsonFormat.Shape.STRING)
private LocalDateTime dtConfirm ;
#Column(name = "contragent_name")
private String contragentName;
....
//CARD
#OneToMany(mappedBy="document" , fetch = FetchType.EAGER)
private List<Card> cards = new ArrayList<Card>();
public List<Card> getCards() {
if (this.cards == null) {
this.cards = new ArrayList<Card>();
}
return this.cards;
}
public void setCard(Card card) {
getCards().add(card);
card.setDocument(this);
}
public int getNrOfCards() {
return getCards().size();
}
....
}
And
#Entity
#Table(name="card")
public class Card extends BaseEntity {
#ManyToOne
#JsonIgnore
#JoinColumn(name = "document_id")
private Document document;
private String name;
private double quantity;
#Column(name = "dt_from")
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
#JsonIgnore
private LocalDate dtFrom ;
#Formula("(select IFNULL(DATEDIFF(Document.dt_confirm , dt_from), 0) from
Document where Document.id = document_id )")
private int daysOnDtConfirm;
...
public void setDtFrom(LocalDate dtFrom) {
this.dtFrom = dtFrom;
}
public void setDtFrom(int daysOnDtConfirm) {
if (this.document.getDtConfirm() != null){
LocalDate dateTo = this.document.getDtConfirm().toLocalDate();
this.dtFrom = dateTo.minusDays(daysOnDtConfirm);
}
}
...
}
Service :
#Service
public class DocumentServiceImpl implements DocumentService {
#Autowired
DocumentRepository documentRepository;
#Autowired
CardRepository cardRepository;
...
#Override
#Transactional
public void changeCard(Document document, Card card) {
//IF ID is NULL then isNew==true!!!!
if (card.isNew()){
card.setDocument(document);
card.setDtFrom(card.getDaysOnDtConfirm());
document.setCard(card);
cardRepository.saveAndFlush(card);
}
else{
Card cardEdit = cardRepository.findOne(card.getId());
if (cardEdit != null) {
cardEdit.setDocument(document);
cardEdit.setName(card.getName());
cardEdit.setUnit(card.getUnit());
cardEdit.setQuantity(card.getQuantity());
//cardEdit.setDtFrom(card.getDtFrom());
cardEdit.setDtFrom(card.getDaysOnDtConfirm());
cardEdit.setDescription(card.getDescription());
cardRepository.saveAndFlush(cardEdit);
}
}
#Override
#Transactional
public Document changeDocumentAndCards(Document document) {
Document documentEdit = changeDocument(document);
List<Card> cards = document.getCards();
//check if the same rows in DB and Client, DELETE difference
deleteCardsFromDocument(document);
//if not empty received from client rows then change
if (!cards.isEmpty()) {
for (Card card : cards) {
changeCard(documentEdit, card);
}
}
return documentEdit;
}
...
}
RestController:
#RestController
#RequestMapping("/api/docs")
public class DocController {
#Autowired
DocumentService documentService;
#RequestMapping(value = "",
method = RequestMethod.GET,
produces = {"application/json", "application/xml"})
#ResponseStatus(HttpStatus.OK)
public #ResponseBody
List<Document> getAllDocument(HttpServletRequest request, HttpServletResponse response) {
List<Document> list = new ArrayList<>();
Iterable<Document> documents = this.documentService.getDocumentAll();
documents.forEach(list::add);
return list;
}
....
#RequestMapping(value = "/{id}",
method = RequestMethod.PUT,
consumes = {"application/json", "application/xml"},
produces = {"application/json", "application/xml"})
#ResponseStatus(HttpStatus.OK)
public Document updateDocument(//#ApiParam(value = "The ID of the existing Document resource.", required = true)
#PathVariable("id") Long id,
#RequestBody Document document,
HttpServletRequest request, HttpServletResponse response) {
Document documentEdit = documentService.changeDocumentAndCards(document);
return documentEdit;
}
...
}
The issue seems to come from the changeDocument(Document document) method. The return value of saveAndFlush() call should be assigned back to documentEdit
UPDATE
The issue is that hibernate will not re-calculate the #Formula field after it is updated. It just fetches it from cache.
The only way I managed to get this working on my machine was to refresh the card entity after updating it. For that to work I needed to add an entity manager in the service class.
In your DocumentServiceImpl (actually could be any service class) class add the following:
public class DocumentServiceImpl implements DocumentService {
//...
#PersistenceContext
private EntityManager em;
#Transactional
public void refreshEntity(Object entity) {
em.refresh(entity);
}
Then, you should call this refreshEntity() method after an update, so that hibernate doesn't fetch it from cache.
This way it worked for me. Hope it helps you.

Categories