I am trying to audit any time changes are made to a table called Book.
So, I have created an advice which will be called when ever I update a row in Book table and saves the audit details in Book_Audit table. Every thing seems to work fine but whenever there is a failure to save in Book_Audit table, it is not been captured by catch block.
This is happening only when the point-cut is involved in a transaction.
@Aspect
@Component
@Slf4j
public class BookAuditAspect {
private transient BookAuditRepository BookAuditRepository;
BookAuditAspect(final BookAuditRepository BookAuditRepository) {
this.BookAuditRepository = BookAuditRepository;
}
@After("execution(* com.test.inventory.app.repository.BookRepository.save(..))")
public void afterSave(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
Book book = (Book) args[0];
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String username = request.getHeader("username");
BookAudit audit =
BookAudit.builder()
.id(book.getId())
.title(book.getTitle())
.author(book.getAuthor())
.pages(book.getPages())
.lastModifiedBy(username)
.lastModified(LocalDateTime.now())
.build();
try {
BookAuditRepository.save(audit);
} catch (Exception e) {
log.error("failed to save book due to error: {}", e.getMessage());
}
}
}
But, when I change the code to use another service class to handle Book Audit and If the bookAuditService.saveBookAudit() fails it's exception is being caught by the catch block. Aspect with Audit service class:
@Aspect
@Component
@Slf4j
public class BookAuditAspect {
private transient BookAuditService bookAuditService;
BookAuditAspect(final BookAuditService bookAuditService) {
this.bookAuditService = bookAuditService;
}
@After("execution(* com.test.inventory.app.repository.BookRepository.save(..))")
public void afterSave(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
Book book = (Book) args[0];
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String username = request.getHeader("username");
BookAudit audit =
BookAudit.builder()
.id(book.getId())
.title(book.getTitle())
.author(book.getAuthor())
.pages(book.getPages())
.lastModifiedBy(username)
.lastModified(LocalDateTime.now())
.build();
try {
bookAuditService.saveBookAudit(audit);
} catch (Exception e) {
log.error("failed to save book due to error: {}", e.getMessage());
}
}
}
Here are the other Parts of the API:
Controller
@RestController
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) {
this.bookService = bookService;
}
@PostMapping("/save-book")
public void saveBook(@RequestBody Book book) {
bookService.saveBook(book);
}
}
Book Service Interface
public interface BookService {
void saveBook(Book book);
}
Book Service Implementation
@Service
public class BookServiceImpl implements BookService {
private final BookRepository bookRepository;
public BookServiceImpl(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
@Transactional
public void saveBook(Book book) {
bookRepository.save(book);
}
}
Book Audit Service Interface
public class BookAuditService {
public void saveBookAudit(BookAudit bookAudit) {}
}
Book Audit Service Implementation
@Service
public class BookAuditServiceImpl extends BookAuditService {
private BookAuditRepository bookAuditRepository;
public BookAuditServiceImpl(BookAuditRepository bookAuditRepository) {
this.bookAuditRepository = bookAuditRepository;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveBookAudit(BookAudit bookAudit) {
bookAuditRepository.save(bookAudit);
}
}
Book Entity
@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "Book", schema = "Library")
public class Book {
@Id
private Long id;
private String title;
private String author;
private Integer pages;
}
Book Audit Entity
@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "Book_Audit", schema = "Library")
public class BookAudit {
@Id private Long id;
private String title;
private String author;
private Integer pages;
private LocalDateTime lastModified;
private String lastModifiedBy;
}
I tried to understand why this is the case, But I was not able to get any satisfactory answer. Please anyone help me what is happening in this scenario.
Thanks in advance.