我有两个实体如下,主要问题是当我想更新AccountRequestStatus
实体。我将AccountRequestStatusEnum
的整数枚举代码保存在数据库中,以便在整个应用程序中持久化AcountRequest状态。
AccountRequestStatusEnum
public enum AccountRequestStatusEnum {
INITIAL(0),
SUCCESS(1);
private final Integer type;
AccountRequestStatusEnum(Integer type) {
this.type = type;
}
public Integer getType() {
return type;
}
public static AccountRequestStatusEnum of(Integer type) {
for (AccountRequestStatusEnum accountRequestStatusEnum : AccountRequestStatusEnum.values()) {
if (type.equals(accountRequestStatusEnum.getType()))
return accountRequestStatusEnum;
}
return null;
}
}
AccountRequest
@Entity
@Table(name = "T_ACCOUNT_REQUEST", uniqueConstraints = {@UniqueConstraint(columnNames = {"ACCOUNT_NO", "MESSAGE_ID"})})
@SequenceGenerator(
name = "SEQ_T_ACCOUNT_REQUEST",
sequenceName = "SEQ_T_ACCOUNT_REQUEST",
allocationSize = 1)
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@ToString
public class AccountRequest extends AbstractAuditingEntity {
private Long id;
private String messageId;
private String issuer;
private EventType type;
private EventName name;
private String accountNo;
private LocalDateTime dateTime;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_T_ACCOUNT_REQUEST")
@Column(name = "ID", nullable = true, insertable = true, updatable = true, precision = 0)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column(name = "MESSAGE_ID")
public String getMessageId() {
return messageId;
}
public void setMessageId(String messageId) {
this.messageId = messageId;
}
@Column(name = "ISSUER")
public String getIssuer() {
return issuer;
}
public void setIssuer(String issuer) {
this.issuer = issuer;
}
@Transient
public EventType getType() {
return type;
}
public void setType(EventType type) {
this.type = type;
}
@Column(name = "TYPE")
public Integer getEventTypeCode() {
if (Objects.nonNull(type)) {
return type.getType();
} else return null;
}
public void setEventTypeCode(Integer typeCode) {
type = EventType.of(typeCode);
}
@Transient
public EventName getName() {
return name;
}
public void setName(EventName name) {
this.name = name;
}
@Column(name = "NAME")
public Integer getEventNameCode() {
if (Objects.nonNull(name)) {
return name.getType();
} else return null;
}
public void setEventNameCode(Integer type) {
name = EventName.of(type);
}
@Column(name = "ACCOUNT_NO")
public String getAccountNo() {
return accountNo;
}
public void setAccountNo(String accountNo) {
this.accountNo = accountNo;
}
@Column(name = "DATE_TIME")
public LocalDateTime getDateTime() {
return dateTime;
}
public void setDateTime(LocalDateTime dateTime) {
this.dateTime = dateTime;
}
}
AccountRequestStatus
@Entity
@Table(name = "T_ACCOUNT_REQUEST_STATUS")
@SequenceGenerator(
name = "SEQ_T_ACCOUNT_REQUEST_STATUS",
sequenceName = "SEQ_T_ACCOUNT_REQUEST_STATUS",
allocationSize = 1
)
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class AccountRequestStatus extends AbstractAuditingEntity {
private Long id;
private AccountRequestStatusEnum accountRequestStatusEnum;
private AccountRequest accountRequest;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_T_ACCOUNT_REQUEST_STATUS")
@Column(name = "ID", nullable = true, insertable = true, updatable = true, precision = 0)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Transient
public AccountRequestStatusEnum getAccountRequestStatusEnum() {
return accountRequestStatusEnum;
}
public void setAccountRequestStatusEnum(AccountRequestStatusEnum accountRequestStatusEnum) {
this.accountRequestStatusEnum = accountRequestStatusEnum;
}
@Column(name = "ACCOUNT_REQUEST_STATUS")
public Integer getAccountRequestStatusCode() {
if (Objects.nonNull(accountRequestStatusEnum)) {
return accountRequestStatusEnum.getType();
} else return null;
}
public void setAccountRequestStatusCode(Integer type) {
accountRequestStatusEnum = AccountRequestStatusEnum.of(type);
}
@ManyToOne(targetEntity = AccountRequest.class)
@JoinColumn(name = "ACCOUNT_REQUEST", referencedColumnName = "ID")
public AccountRequest getAccountRequest() {
return accountRequest;
}
public void setAccountRequest(AccountRequest accountRequest) {
this.accountRequest = accountRequest;
}
}
第一次从MQ向应用程序发出帐户请求时,我将AccountRequestStatusEnum
的initial
代码保存在服务中,如下所示。这种状态保持正常,没有问题,但是当我想更新AccountRequestStatus
并添加新的success
代码AccountRequestStatusEnum
(在另一个服务中)时它不会保存在DB中。
这是收到帐户请求并保存initial
代码后调用的第一个服务。
@Service
@Transactional(readOnly = true)
public class AccountRequestServiceImpl implements IAccountRequestService {
@Value("${mq.event_argument_key}")
private String eventArgumentKey;
private final AccountRequestRepository accountRequestRepository;
private final AccountRequestStatusServiceImpl mqRequestStatusService;
private final EventToAccountRequestEntityMapper eventMapper;
private final AccountRequestMapper accountRequestMapper;
@Autowired
public AccountRequestServiceImpl(AccountRequestRepository accountRequestRepository,
AccountRequestStatusServiceImpl mqRequestStatusService,
EventToAccountRequestEntityMapper eventMapper,
AccountRequestMapper accountRequestMapper) {
this.accountRequestRepository = accountRequestRepository;
this.mqRequestStatusService = mqRequestStatusService;
this.eventMapper = eventMapper;
this.accountRequestMapper = accountRequestMapper;
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)// to prevent rollback for whole receive method in mq service
public void saveAccountRequest(Event event) {
AccountRequest accountRequest = eventMapper.eventToAccountRequest(event, eventArgumentKey);
accountRequestRepository.save(accountRequest);
AccountRequestDto accountRequestDto = accountRequestMapper.toDto(accountRequest);
saveAccountRequestStatus(accountRequestDto, AccountRequestStatusEnum.INITIAL);
}
private void saveAccountRequestStatus(AccountRequestDto accountRequestDto, AccountRequestStatusEnum status) {
AccountRequestStatusDto accountRequestStatusDto = new AccountRequestStatusDto();
accountRequestStatusDto.setAccountRequestStatusEnum(status);
accountRequestStatusDto.setAccountRequestDto(accountRequestDto);
mqRequestStatusService.saveAccountRequestStatus(accountRequestStatusDto);
}
}
这是第二个应该保存AccountRequestStatus
的success
代码的服务。
@Service
@Transactional(readOnly = true)
public class SyncLegacyAccountServiceImpl implements ISyncLegacyAccountService {
@Value("${mq.event_argument_key}")
private String eventArgumentKey;
@Value("${range.account_title_code}")
private String accountTitleCode;
private static final Logger log = LoggerFactory.getLogger(SyncLegacyAccountServiceImpl.class);
private final AccountMapRepository accountMapRepository;
private final AccountRequestRepository accountRequestRepository;
private final CustomerRepository customerRepository;
private final CustomerPersonRepository customerPersonRepository;
private final CustomerCompanyRepository customerCompanyRepository;
private final IMQService iMQService;
private final AccountRequestStatusServiceImpl accountRequestStatusServiceImpl;
private final GalaxyApi galaxyApi;
private final RangeApi rangeApi;
private final CustomerMapper customerMapper;
private final InquiryMapper inquiryMapper;
private final AccountRequestMapper accountRequestMapper;
private final EventToAccountRequestEntityMapper eventToAccountRequestMapper;
@Override
public void handleSyncRequest(Event event) {
saveSuccessfulAccountStatus(event); // ****** This is the main issue******
try {
CustomerAccountResponseDto galaxyData = getGalaxyData(event);
Optional<AccountMapEntity> optAccountMapEntity = accountMapRepository.findByNewAccountNo(event.getArgument().get(eventArgumentKey).toString());
if (!optAccountMapEntity.isPresent()) {
//openAccount(event);
} else {
AccountMapEntity accountMapEntity = optAccountMapEntity.get();
CustomerAccountResponseDto customerData = getCustomerData(accountMapEntity);
// save in legacy
}
} catch (Exception exception) {
handleEventRequestException(exception, event);
}
}
private void handleEventRequestException(Exception exception, Event event) {
if (exception instanceof RangeServiceException) {
log.error("Something went wrong with the Range service!");
throw new RangeServiceException();
} else if (exception instanceof GalaxySystemException) {
log.error("Something went wrong with the Galaxy service!");
NotifyAccountChangeResponse notifyAccountChangeResponse = MQUtil.buildAccountChangeResponse(new GalaxySystemException(), null, event.getMessageId());
iMQService.send(notifyAccountChangeResponse);
throw new GalaxySystemException();
}
}
public void saveSuccessfulAccountStatus(Event event) {
AccountRequest accountRequest = eventToAccountRequestMapper.eventToAccountRequest(event, eventArgumentKey);
AccountRequestDto accountRequestDto = accountRequestMapper.toDto(accountRequest);
saveAccountRequestStatus(accountRequestDto, AccountRequestStatusEnum.SUCCESS);
}
public void saveAccountRequestStatus(AccountRequestDto accountRequestDto, AccountRequestStatusEnum status) {
AccountRequestStatusDto accountRequestStatusDto = new AccountRequestStatusDto();
accountRequestStatusDto.setAccountRequestStatusEnum(status);
accountRequestStatusDto.setAccountRequestDto(accountRequestDto);
accountRequestStatusServiceImpl.saveAccountRequestStatus(accountRequestStatusDto);
}
}
AccountRequestStatusServiceImpl
@Service
@Transactional(readOnly = true)
public class AccountRequestStatusServiceImpl implements IAccountRequestStatusService {
private final AccountRequestStatusRepository accountRequestStatusRepository;
private final AccountRequestStatusMapper accountRequestStatusMapper;
@Autowired
public AccountRequestStatusServiceImpl(AccountRequestStatusRepository accountRequestStatusRepository,
AccountRequestStatusMapper accountRequestStatusMapper) {
this.accountRequestStatusRepository = accountRequestStatusRepository;
this.accountRequestStatusMapper = accountRequestStatusMapper;
}
@Override
@Transactional
public void saveAccountRequestStatus(AccountRequestStatusDto accountRequestStatusDto) {
AccountRequestStatus accountRequestStatus = accountRequestStatusMapper.toAccountRequestStatus(accountRequestStatusDto);
accountRequestStatusRepository.save(accountRequestStatus);
}
}
AccountRequestDto
@Data
public class AccountRequestDto {
private Long id;
private String messageId;
private String issuer;
private EventType type;
private EventName name;
private String accountNo;
private LocalDateTime dateTime;
}
AccountRequestStatusDto
@Data
public class AccountRequestStatusDto {
private Long id;
private AccountRequestStatusEnum accountRequestStatusEnum;
private AccountRequestDto accountRequestDto;
}
AccountRequestStatusMapper
@Mapper(componentModel = "spring")
public interface AccountRequestStatusMapper extends EntityToDtoMapper<AccountRequestStatusDto, AccountRequestStatus>, DtoToEntityMapper<AccountRequestStatusDto, AccountRequestStatus> {
@Mapping(target = "accountRequest.id", source = "accountRequestDto.id")
@Mapping(target = "accountRequest.messageId", source = "accountRequestDto.messageId")
@Mapping(target = "accountRequest.issuer", source = "accountRequestDto.issuer")
@Mapping(target = "accountRequest.type", source = "accountRequestDto.type")
@Mapping(target = "accountRequest.name", source = "accountRequestDto.name")
@Mapping(target = "accountRequest.accountNo", source = "accountRequestDto.accountNo")
@Mapping(target = "accountRequest.dateTime", source = "accountRequestDto.dateTime")
@Named(value = "toAccountRequestStatus")
AccountRequestStatus toAccountRequestStatus(AccountRequestStatusDto accountRequestStatusDto);
@Mapping(target = "accountRequestDto.id", source = "accountRequest.id")
@Mapping(target = "accountRequestDto.messageId", source = "accountRequest.messageId")
@Mapping(target = "accountRequestDto.issuer", source = "accountRequest.issuer")
@Mapping(target = "accountRequestDto.type", source = "accountRequest.type")
@Mapping(target = "accountRequestDto.name", source = "accountRequest.name")
@Mapping(target = "accountRequestDto.accountNo", source = "accountRequest.accountNo")
@Mapping(target = "accountRequestDto.dateTime", source = "accountRequest.dateTime")
@Named(value = "toAccountRequestStatusDto")
AccountRequestStatusDto toAccountRequestStatusDto(AccountRequestStatus accountRequestStatus);
}
您在保存之前没有设置accountRequestStatus的id字段。Hibernate应该如何知道要更新哪个实体?我猜它只是生成一个新的id,因为你没有提供一个。您需要以某种方式获得已经创建的accountRequestStatus的id,并在更新之前设置它。或者获取持久化的accountRequestStatus实体并修改它。
话虽如此,我认为你可能把事情复杂化了。为什么AccountRequestStatus需要是一个实体?它本身似乎没有标识,它更像是AccountRequest的一个属性。你不能只是在AccountRequest中添加一个AccountRequestStatusEnum字段,毕竟是一对一的关系?如果你在类中包含状态是因为你预见到它将来可能会有更多的字段,考虑将其设置为@Embeddable而不是@Entity。 要知道一个类是否必须是@Entity,一般规则是它必须有一个标识。请求的状态看起来不像是一个好的候选者,因为它的标识可能只是请求的标识。将状态设置为@Entity意味着您现在拥有了一个人工id,令人惊讶的是,这正是当前问题的根本原因。如果你想更好地理解这个主题,我建议你阅读@Entity和@Embeddable之间的区别,如果你觉得理论,读一点关于DDD(领域驱动设计)。