提问人:Nhân Nguyễn 提问时间:11/7/2023 更新时间:11/7/2023 访问量:47
Spring Boot @Transactional在处理异常时不会回滚数据库插入
Spring Boot @Transactional does not roll back database inserts when I handle exceptions
问:
我在这里需要一些帮助,我不明白为什么我的事务在发生异常时没有被回滚。我在 Oracle Database 3.0.11 中使用 Spring Boot 3.17.15 和 JOOQ 12c。
JooqConfiguration
@Configuration
@EnableTransactionManagement
public class JooqConfiguration {
@Primary
@Bean
@ConfigurationProperties(prefix = "spring.datasource.thutienchamno")
public DataSource dataSourceThuTienChamNo() {
return DataSourceBuilder.create().build();
}
@Bean
public DSLContext dslThuTienChamNo(@Qualifier("dataSourceThuTienChamNo") DataSource dataSource) {
return DSL.using(dataSource, SQLDialect.ORACLE);
}
}
GlobalExceptionHandlerController
@RestControllerAdvice
public class GlobalExceptionHandlerController {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandlerController.class);
@Bean
public ErrorAttributes errorAttributes() {
// Hide exception field in the return object
return new DefaultErrorAttributes() {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
return super.getErrorAttributes(webRequest, ErrorAttributeOptions.defaults().excluding(ErrorAttributeOptions.Include.EXCEPTION));
}
};
}
@ExceptionHandler(CustomException.class)
public ResponseEntity<ResponseDTO> handleCustomException(CustomException ex) {
ResponseDTO responseDTO = new ResponseDTO(false, ex.getMessage(), null);
logger.error("Có lỗi xả ra với service Nợ khó đòi: ", ex);
return ResponseEntity.status(ex.getHttpStatus()).body(responseDTO);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ResponseDTO> handleException(Exception ex) {
ResponseDTO responseDTO = new ResponseDTO();
// Lấy chi tiết lỗi ra
StackTraceElement[] stackTraceElement = ex.getStackTrace();
if (stackTraceElement != null && stackTraceElement.length > 0) {
OutputError outputError = new OutputError(stackTraceElement[0].getFileName(), stackTraceElement[0].getClassName(), stackTraceElement[0].getMethodName(), stackTraceElement[0].getLineNumber());
responseDTO = new ResponseDTO(false, "Có lỗi xả ra với service Nợ khó đòi: " + ex.getMessage(), outputError);
} else {
responseDTO = new ResponseDTO(false, "Có lỗi xả ra với service Nợ khó đòi: " + ex.getMessage(), null);
}
// logger.error("Có lỗi xả ra với service Nợ khó đòi: ", ex);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(responseDTO);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ResponseDTO> handleInvalidArgument(MethodArgumentNotValidException ex, HttpServletResponse res) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String message = error.getDefaultMessage();
errors.put(fieldName, message);
});
logger.error("Có lỗi xả ra với service Nợ khó đòi: ", ex);
ObjectMapper mapper = new ObjectMapper();
String jsonResult;
try {
jsonResult = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(errors);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseDTO(false, HttpStatus.BAD_REQUEST.name(), mapper.readValue(jsonResult, Object.class)));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleBindException(BindException e) {
// Trả về message của lỗi đầu tiên
if (e.getBindingResult().hasErrors()) {
e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
}
return "Request không hợp lệ";
}
}
CustomException
public class CustomException extends RuntimeException {
private static final long serialVersionUID = 1L;
private final String message;
private final HttpStatus httpStatus;
public CustomException(String message, HttpStatus httpStatus) {
super(message);
this.message = message;
this.httpStatus = httpStatus;
}
@Override
public String getMessage() {
return message;
}
public HttpStatus getHttpStatus() {
return httpStatus;
}
}
CNHSoUploadNKDRepositoryImpl
@Repository
public class CNHSoUploadNKDRepositoryImpl implements CNHSoUploadNKDRepository {
private final DSLContext dslContext;
CnHsoUpload cnHsoUpload = CnHsoUpload.CN_HSO_UPLOAD.as("cnHsoUpload");
public CNHSoUploadNKDRepositoryImpl(DSLContext dslContext) {
this.dslContext = dslContext;
}
@Override
public Boolean save(ThongTinHoSoNKD hoSo) {
dslContext.insertInto(cnHsoUpload)
.set(cnHsoUpload.ID_TTHAI_NO, hoSo.ID_TTHAI_NO)
.set(cnHsoUpload.MA_LOAI_HSO, hoSo.MA_LOAI_HSO)
.set(cnHsoUpload.MA_DVIQLY, hoSo.MA_DVIQLY)
.set(cnHsoUpload.ID_HSO, dslContext.nextval(Sequences.SEQ_CN_HSO_UPLOAD))
.execute();
return true;
}
@Override
public Boolean update(ThongTinHoSoNKD hoSo) {
// I intentionally handled it this way to trigger an exception to enable transaction rollback.
String aa = hoSo.MA_DVIQLY.concat("");
dslContext.update(cnHsoUpload)
.set(cnHsoUpload.ID_TTHAI_NO, hoSo.ID_TTHAI_NO)
.set(cnHsoUpload.MA_LOAI_HSO, hoSo.MA_LOAI_HSO)
.set(cnHsoUpload.MA_DVIQLY, hoSo.MA_DVIQLY)
.where(cnHsoUpload.ID_HSO.eq(hoSo.ID_HSO))
.execute();
return true;
}
}
NoKhoDoiService
@Service
public class NoKhoDoiService {
private final CNHSoUploadNKDRepository cnhSoUploadNKDRepository;
public NoKhoDoiService(CNHSoUploadNKDRepository cnhSoUploadNKDRepository) {
super();
this.cnhSoUploadNKDRepository = cnhSoUploadNKDRepository;
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = {CustomException.class, Exception.class, RuntimeException.class})
public void testRollBackData() throws CustomException {
ThongTinHoSoNKD thongTinHoSoNKD_1 = new ThongTinHoSoNKD("PD0601", -1l, -1l, "TEST_1");
thongTinHoSoNKD_1 = cnhSoUploadNKDRepository.saveAndReturn(thongTinHoSoNKD_1);
//The code I intentionally triggered an exception, but the data inserted above is still not rolled back.
cnhSoUploadNKDRepository.update(new ThongTinHoSoNKD());
}
}
application.yml
server:
port: 8804
spring:
profiles:
active: debug
application:
name: service-nokhodoi
main:
allow-bean-definition-overriding: true
datasource:
type: com.zaxxer.hikari.HikariDataSource
thutienchamno:
jdbcUrl: jdbc:oracle:thin:@10.0.40.87:1522:CMIS3
username: TTIENCNO
password: TTIENCNODB27
hikari:
auto-commit: false
这是我的代码。我记录了该过程,当我测试它时,日志输出如下。
22:36:09.660 [http-nio-8804-exec-6] DEBUG o.s.j.support.JdbcTransactionManager - Creating new transaction with name [evn.evnict.nokhodoi.services.NoKhoDoiService.testGetData]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-evn.evnict.nokhodoi.exceptions.CustomException,-java.lang.Exception,-java.lang.RuntimeException
22:36:09.663 [http-nio-8804-exec-6] DEBUG o.s.j.support.JdbcTransactionManager - Acquired Connection [HikariProxyConnection@2110726266 wrapping oracle.jdbc.driver.T4CConnection@d7732f5] for JDBC transaction
22:36:09.663 [http-nio-8804-exec-6] DEBUG o.s.j.support.JdbcTransactionManager - Switching JDBC Connection [HikariProxyConnection@2110726266 wrapping oracle.jdbc.driver.T4CConnection@d7732f5] to manual commit
22:36:09.664 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - Executing query : select "TTIENCNO"."SEQ_CN_HSO_UPLOAD".nextval from DUAL
22:36:09.670 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - Fetched result : +-------+
22:36:09.670 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - : |nextval|
22:36:09.670 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - : +-------+
22:36:09.670 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - : | 77|
22:36:09.670 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - : +-------+
22:36:09.670 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - Fetched row(s) : 1
22:36:09.671 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - Executing query : insert into "TTIENCNO"."CN_HSO_UPLOAD" "cnHsoUpload" ("ID_TTHAI_NO", "MA_LOAI_HSO", "MA_DVIQLY", "ID_HSO") values (?, ?, ?, ?)
22:36:09.671 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - -> with bind values : insert into "TTIENCNO"."CN_HSO_UPLOAD" "cnHsoUpload" ("ID_TTHAI_NO", "MA_LOAI_HSO", "MA_DVIQLY", "ID_HSO") values (-1, 'TEST_1', 'PD0601', 77)
22:36:09.680 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - Affected row(s) : 1
22:36:09.681 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - Fetched result : +---------+------+-----------+-----------+
22:36:09.681 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - : |MA_DVIQLY|ID_HSO|ID_TTHAI_NO|MA_LOAI_HSO|
22:36:09.681 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - : +---------+------+-----------+-----------+
22:36:09.681 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - : |PD0601 | 77| -1|TEST_1 |
22:36:09.681 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - : +---------+------+-----------+-----------+
22:36:09.681 [http-nio-8804-exec-6] DEBUG org.jooq.tools.LoggerListener - Fetched row(s) : 1
22:36:09.681 [http-nio-8804-exec-6] DEBUG o.s.j.support.JdbcTransactionManager - Initiating transaction rollback
22:36:09.681 [http-nio-8804-exec-6] DEBUG o.s.j.support.JdbcTransactionManager - Rolling back JDBC transaction on Connection [HikariProxyConnection@2110726266 wrapping oracle.jdbc.driver.T4CConnection@d7732f5]
22:36:09.682 [http-nio-8804-exec-6] DEBUG o.s.j.support.JdbcTransactionManager - Releasing JDBC Connection [HikariProxyConnection@2110726266 wrapping oracle.jdbc.driver.T4CConnection@d7732f5] after transaction
22:36:09.683 [http-nio-8804-exec-6] DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Using @ExceptionHandler evn.evnict.nokhodoi.exceptions.GlobalExceptionHandlerController#handleException(Exception)
22:36:09.683 [http-nio-8804-exec-6] DEBUG o.s.w.s.m.m.a.HttpEntityMethodProcessor - Using 'application/json', given [application/json] and supported [application/json, application/*+json]
22:36:09.683 [http-nio-8804-exec-6] DEBUG o.s.w.s.m.m.a.HttpEntityMethodProcessor - Writing [ResponseDTO(success=false, message=Có lỗi xả ra với service Nợ khó đòi: Cannot invoke "String.concat (truncated)...]
22:36:09.684 [http-nio-8804-exec-6] WARN o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolved [java.lang.NullPointerException: Cannot invoke "String.concat(String)" because "hoSo.MA_DVIQLY" is null]
22:36:09.684 [http-nio-8804-exec-6] DEBUG o.s.web.servlet.DispatcherServlet - Completed 500 INTERNAL_SERVER_ERROR
22:36:09.685 [http-nio-8804-exec-6] DEBUG o.a.coyote.http11.Http11Processor - Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@1ab663d1:org.apache.tomcat.util.net.NioChannel@459072d5:java.nio.channels.SocketChannel[connected local=/127.0.0.1:8804 remote=/127.0.0.1:52455]], Status in: [OPEN_READ], State out: [CLOSED]
22:36:09.685 [http-nio-8804-exec-6] DEBUG o.a.coyote.http11.Http11NioProtocol - Pushed Processor [org.apache.coyote.http11.Http11Processor@11b95d27]
22:36:09.685 [http-nio-8804-exec-6] DEBUG o.a.tomcat.util.threads.LimitLatch - Counting down[http-nio-8804-exec-6] latch=1
22:36:09.685 [http-nio-8804-exec-6] DEBUG o.apache.tomcat.util.net.NioEndpoint - Calling [org.apache.tomcat.util.net.NioEndpoint@3d68f3a5].closeSocket([org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@1ab663d1:org.apache.tomcat.util.net.NioChannel@459072d5:java.nio.channels.SocketChannel[connected local=/127.0.0.1:8804 remote=/127.0.0.1:52455]])
我已经研究并尝试了各种方法,但是当发生异常时,我仍然无法回滚数据。你能看看我的代码并帮助我确定问题可能出在哪里吗?
答: 暂无答案
评论
NoKhoDoiService
CNHSoUploadNKDRepository
DataSource
HikariProxyConnection