Почему SimpleJdbcCall igronre @Transactional аннотация - PullRequest
0 голосов
/ 26 сентября 2019

Я хочу сделать некоторые связанные с БД действия в сервисном методе.Изначально это выглядит так:

@Override
@Transactional
public void addDirectory(Directory directory) {
    //some cheks here
    directoryRepo.save(directory);
    rsdhUtilsService.createPhysTable(directory);
}

Метод Firs directoryRepo.save(directory); - это просто простое действие сохранения JPA, второе rsdhUtilsService.createPhysTable(directory); - это JDBCTemplate вызов хранимой процедуры из своей собственной службы.Проблема заключается в том, что если в действии JPA или SimpleJdbcCall возникает какое-либо исключение, транзакция будет откатываться, и ничто, связанное с JPA, не будет сохранено, но если в действии JPA произойдет исключение only , результатом SimpleJdbcCall не будетзатронут откат транзакции.Чтобы проиллюстрировать это поведение, я удалил действие JAP, пометил @Transactional как (readOnly = true) и переместил всю JDBCTemplate связанную логику из другого сервиса в текущий.

@Service
public class DirectoriesServiceImpl implements DirectoriesService {

    private final DirectoryRepo directoryRepo;

    private final MapSQLParamUtils sqlParamUtils;

    private final JdbcTemplate jdbcTemplate;

    @Autowired
    public DirectoriesServiceImpl(DirectoryRepo directoryRepo, MapSQLParamUtils sqlParamUtils, JdbcTemplate jdbcTemplate) {
        this.directoryRepo = directoryRepo;
        this.sqlParamUtils = sqlParamUtils;
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    @Transactional(readOnly = true)
    public void addDirectory(Directory directory) {
        directoryRepo.save(directory);

        new SimpleJdbcCall(jdbcTemplate).withSchemaName("RSDH_DICT").withCatalogName("UTL_DICT")
                .withFunctionName("create_dict")
                .executeFunction(String.class, sqlParamUtils.getMapSqlParamForCreatePhysTable(directory));
    }

}

В результате @Transactional аннотацияигнорируется, и я вижу, что новые записи сохраняются в БД.У меня есть только один источник данных, настроенный через application.properties, и вот как JDBCTemlate настроен

@Component
class MapSQLParamUtils {

    private final DataSource dataSource;

    @Autowired
    MapSQLParamUtils(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource);
    }

}

Итак, мои вопросы: почему @Transactional игнорируется SimpleJdbcCall и как настроить JPA и JDBCTemlate для использования одного и того же менеджера транзакций.

ОБНОВЛЕНИЕ : так я использую этот сервис в контроллере

@RestController
@RequestMapping(value = "/api/v1/directories")
public class DirectoriesRESTControllerV1 {

    private final DirectoriesService directoriesService;

    @Autowired
    public DirectoriesRESTControllerV1(DirectoriesService directoriesService) {
        this.directoriesService = directoriesService;
    }

    @PostMapping
    @PreAuthorize("hasPermission('DIRECTORIES_USER', 'W')")
    public ResponseEntity createDirectory(@NotNull @RequestBody DirectoryRequestDTO createDirectoryRequestDTO) {
        Directory directoryFromRequest = ServiceUtils.convertDtoToEntity(createDirectoryRequestDTO);
        directoriesService.addDirectory(directoryFromRequest);
        return ResponseEntity.noContent().build();
    }

}

1 Ответ

0 голосов
/ 26 сентября 2019

Пожалуйста, попробуйте это:

@Transactional(rollbackFor = Exception.class)
public void addDirectory(Directory directory){

@ Транзакционный откат транзакций только для непроверенных исключений.Для проверенных исключений и их подклассов он фиксирует данные.Поэтому, хотя здесь возникает исключение, поскольку это проверенное исключение, Spring игнорирует его и фиксирует данные в базе данных.

Так что, если вы генерируете исключение или его подкласс, всегда используйте вышеприведенное с @Транзакционная аннотация, указывающая Spring откатывать транзакции в случае возникновения проверенного исключения.

Это очень просто, просто используйте следующее с @Transactional:

@Transactional(rollbackFor = Exception.class)
...