跨微服务的事务
Transactions Across Microservices
我有两个实体:
实体A:
private Long idA;
实体 B:
private Long idB;
private Long fkA;
我的微服务只能保存、修改或删除B实体:
存储库:
@Repository
public interface BRepository extends CrudRepository<B, Long>{
}
伪造外部服务:
@FeignClient(value = "aExtClient", url = "${A_EXT_MS_URL}")
public interface AExtRemoteService {
@DeleteMapping(path = "/a/{idA}", produces = MediaType.APPLICATION_JSON_VALUE)
public Esito deleteA(@PathVariable(name = "idA") Long idA);
}
服务实现:
@Component
@Transactional(rollbackFor = BusinessServiceException.class)
public class BServiceImpl implements BService {
@Autowired
private BRepository bRepository;
@Autowired
private AExtRemoteService aExtRemoteService;
@Override
public void deleteB(B b) {
Long idA = b.getFkA();
bRepository.delete(b); //marker
if(idA != null) {
aExtRemoteService.deleteA(idA);
}
}
}
外部服务只能保存、修改或删除 A 实体:
控制器:
@RestController
@RequestMapping("/a")
@CrossOrigin(origins = "${mocks.allowedCrossOrigins:*}")
public class AServiceMockController {
@Autowired
private AMockService aMockService;
@DeleteMapping(value = {
"/{idA}" }, produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody ResponseEntity<Result> deleteA(@PathVariable(name = "idA") Long idA) {
Result result = new Result();
aMockService.deleteA(idA);
result.setResult(true);
result.setCod(String.valueOf(HttpStatus.OK.value()));
result.setMess(HttpStatus.OK.name());
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
服务:
@Service
public class AMockService {
private final ARepository aRepository;
@Autowired
public AMockService(ARepository aRepository) {
this.aRepository = aRepository;
}
public void deleteA(Long idA) {
A byId = aRepository.findById(idA).orElseThrow(NoSuchElementException);
aRepository.delete(byId);
}
}
存储库:
@Repository
public interface ARepository extends CrudRepository<A, Long>{
}
调用 deleteB 时,外部服务返回了这条消息:
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-02292: integrity constraint violated (X.YY) - child record found
我该如何解决?
似乎没有考虑 bRepository.delete(b);
P.S。我可以访问外部服务,所以如果需要我可以修改它。
错误信息表明删除还没有完成。我要尝试的第一件事是在删除后手动 bRepository.flush()。
您可以在此处阅读有关冲洗的信息:https://www.baeldung.com/spring-data-jpa-save-saveandflush
您不能在微服务之间进行这样的交易 - 一个是因为您正在使用对服务 A 的远程 http 调用,并且它本质上无法参与您在服务 B 中启动的交易。您正在使用的交易是resource-local transaction. Second you are sharing a database in both the services and the remote http service is trying to delete a record which has a foreign key relation as a parent. Since you are using Spring transaction manager you can use a TransactionEventListener ( see here 到示例用法)以在 B 中的事务成功完成后调用删除 A。还有其他模式可以解决跨微服务的事务,例如
Saga
我有两个实体:
实体A:
private Long idA;
实体 B:
private Long idB;
private Long fkA;
我的微服务只能保存、修改或删除B实体:
存储库:
@Repository
public interface BRepository extends CrudRepository<B, Long>{
}
伪造外部服务:
@FeignClient(value = "aExtClient", url = "${A_EXT_MS_URL}")
public interface AExtRemoteService {
@DeleteMapping(path = "/a/{idA}", produces = MediaType.APPLICATION_JSON_VALUE)
public Esito deleteA(@PathVariable(name = "idA") Long idA);
}
服务实现:
@Component
@Transactional(rollbackFor = BusinessServiceException.class)
public class BServiceImpl implements BService {
@Autowired
private BRepository bRepository;
@Autowired
private AExtRemoteService aExtRemoteService;
@Override
public void deleteB(B b) {
Long idA = b.getFkA();
bRepository.delete(b); //marker
if(idA != null) {
aExtRemoteService.deleteA(idA);
}
}
}
外部服务只能保存、修改或删除 A 实体:
控制器:
@RestController
@RequestMapping("/a")
@CrossOrigin(origins = "${mocks.allowedCrossOrigins:*}")
public class AServiceMockController {
@Autowired
private AMockService aMockService;
@DeleteMapping(value = {
"/{idA}" }, produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody ResponseEntity<Result> deleteA(@PathVariable(name = "idA") Long idA) {
Result result = new Result();
aMockService.deleteA(idA);
result.setResult(true);
result.setCod(String.valueOf(HttpStatus.OK.value()));
result.setMess(HttpStatus.OK.name());
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
服务:
@Service
public class AMockService {
private final ARepository aRepository;
@Autowired
public AMockService(ARepository aRepository) {
this.aRepository = aRepository;
}
public void deleteA(Long idA) {
A byId = aRepository.findById(idA).orElseThrow(NoSuchElementException);
aRepository.delete(byId);
}
}
存储库:
@Repository
public interface ARepository extends CrudRepository<A, Long>{
}
调用 deleteB 时,外部服务返回了这条消息:
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-02292: integrity constraint violated (X.YY) - child record found
我该如何解决? 似乎没有考虑 bRepository.delete(b);
P.S。我可以访问外部服务,所以如果需要我可以修改它。
错误信息表明删除还没有完成。我要尝试的第一件事是在删除后手动 bRepository.flush()。
您可以在此处阅读有关冲洗的信息:https://www.baeldung.com/spring-data-jpa-save-saveandflush
您不能在微服务之间进行这样的交易 - 一个是因为您正在使用对服务 A 的远程 http 调用,并且它本质上无法参与您在服务 B 中启动的交易。您正在使用的交易是resource-local transaction. Second you are sharing a database in both the services and the remote http service is trying to delete a record which has a foreign key relation as a parent. Since you are using Spring transaction manager you can use a TransactionEventListener ( see here 到示例用法)以在 B 中的事务成功完成后调用删除 A。还有其他模式可以解决跨微服务的事务,例如 Saga