我尝试使用CrudRepository的findById方法从数据库中通过id获取对象。主实体的id是一个复合键,由包含所有属性的@Embeadable类使用。
以下是我当前实现的详细信息:
下面是主实体类(InitialMoneyCount):
@Entity
@Getter
@Setter
@EqualsAndHashCode
public class InitialMoneyCount {
@EmbeddedId
@NonNull
private InitialMoneyCountId initialMoneyCountId;
@NonNull
private BigDecimal amount;
@NonNull
private BigDecimal amountSystemCurrency;
@NonNull
private String currency;
// Default constructor
public InitialMoneyCount() {
}
// Constructor with all properties
public InitialMoneyCount(InitialMoneyCountId initialMoneyCountId, BigDecimal amount,
BigDecimal amountSystemCurrency, String currency) {
this.initialMoneyCountId = initialMoneyCountId;
this.amount = amount;
this.amountSystemCurrency = amountSystemCurrency;
this.currency = currency;
}
// Builder
public static InitialMoneyCountBuilder builder() {
return new InitialMoneyCountBuilder();
}
public static class InitialMoneyCountBuilder {
private InitialMoneyCountId initialMoneyCountId;
private BigDecimal amount;
private BigDecimal amountSystemCurrency;
private String currency;
public InitialMoneyCountBuilder initialMoneyCountId(InitialMoneyCountId initialMoneyCountId) {
this.initialMoneyCountId = initialMoneyCountId;
return this;
}
public InitialMoneyCountBuilder amount(BigDecimal amount) {
this.amount = amount;
return this;
}
public InitialMoneyCountBuilder amountSystemCurrency(BigDecimal amountSystemCurrency) {
this.amountSystemCurrency = amountSystemCurrency;
return this;
}
public InitialMoneyCountBuilder currency(String currency) {
this.currency = currency;
return this;
}
public InitialMoneyCount build() {
return new InitialMoneyCount(initialMoneyCountId, amount, amountSystemCurrency, currency);
}
}
}
下面是主键@Embeddable类:
@EqualsAndHashCode
@Embeddable
@Getter
public class InitialMoneyCountId implements Serializable {
@NonNull
private UUID posTerminalId;
@NonNull
private UUID cashierId;
@NonNull
private UUID cashDrawerId;
@NonNull
private LocalDate bookingDate;
@NonNull
private Integer bookingPeriod;
@NonNull
private UUID paymentMethodId;
// Default constructor
public InitialMoneyCountId() {
}
// Constructor with all properties
public InitialMoneyCountId(UUID posTerminalId, UUID cashierId, UUID cashDrawerId,
LocalDate bookingDate, Integer bookingPeriod, UUID paymentMethodId) {
this.posTerminalId = posTerminalId;
this.cashierId = cashierId;
this.cashDrawerId = cashDrawerId;
this.bookingDate = bookingDate;
this.bookingPeriod = bookingPeriod;
this.paymentMethodId = paymentMethodId;
}
// Builder
public static InitialMoneyCountIdBuilder builder() {
return new InitialMoneyCountIdBuilder();
}
public static class InitialMoneyCountIdBuilder {
private UUID posTerminalId;
private UUID cashierId;
private UUID cashDrawerId;
private LocalDate bookingDate;
private Integer bookingPeriod;
private UUID paymentMethodId;
public InitialMoneyCountIdBuilder posTerminalId(UUID posTerminalId) {
this.posTerminalId = posTerminalId;
return this;
}
public InitialMoneyCountIdBuilder cashierId(UUID cashierId) {
this.cashierId = cashierId;
return this;
}
public InitialMoneyCountIdBuilder cashDrawerId(UUID cashDrawerId) {
this.cashDrawerId = cashDrawerId;
return this;
}
public InitialMoneyCountIdBuilder bookingDate(LocalDate bookingDate) {
this.bookingDate = bookingDate;
return this;
}
public InitialMoneyCountIdBuilder bookingPeriod(Integer bookingPeriod) {
this.bookingPeriod = bookingPeriod;
return this;
}
public InitialMoneyCountIdBuilder paymentMethodId(UUID paymentMethodId) {
this.paymentMethodId = paymentMethodId;
return this;
}
public InitialMoneyCountId build() {
return new InitialMoneyCountId(posTerminalId, cashierId, cashDrawerId,
bookingDate, bookingPeriod, paymentMethodId);
}
}
}
下面是JpaRepository接口定义:
@Repository
public interface InitialMoneyCountRepository extends JpaRepository<InitialMoneyCount, InitialMoneyCountId> {}
下面是对findById(InitialMoneyCountId id)的测试:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, properties = "spring.h2.console.enabled=true")
public class InitialMoneyCountRepositoryTest {
@Autowired
SampleDataGenerator sampleDataGenerator;
@Autowired
InitialMoneyCountRepository initialMoneyCountRepository;
@AfterEach
public void resetDatabase_AfterEach() {
initialMoneyCountRepository.deleteAll();
}
@After
public void resetDatabase_After() {
initialMoneyCountRepository.deleteAll();
}
@Before
public void resetDatabase_Before() {
initialMoneyCountRepository.deleteAll();
}
@Test
public void findById_sucess() {
InitialMoneyCountId initialMoneyCountId = InitialMoneyCountId.builder()
.posTerminalId(UUID.randomUUID())
.cashDrawerId(UUID.randomUUID())
.cashierId(UUID.randomUUID())
.bookingDate(LocalDate.now())
.bookingPeriod(5)
.paymentMethodId(UUID.randomUUID())
.build();
InitialMoneyCount initialMoneyCount = InitialMoneyCount.builder()
.initialMoneyCountId(initialMoneyCountId)
.amount(BigDecimal.valueOf(555.55))
.amountSystemCurrency(BigDecimal.valueOf(55.555))
.currency(Currency.EUR.value())
.build();
InitialMoneyCount savedInitialMoneyCount = initialMoneyCountRepository.save(initialMoneyCount);
Optional<InitialMoneyCount> imc = initialMoneyCountRepository.findById(initialMoneyCountId);
Assertions.assertTrue(imc.isPresent());
}
}
但是我无法通过ID找到对象。这是我得到的例外:
expected: <true> but was: <false>
Comparison Failure:
Expected :true
Actual :false
<Click to see difference>
org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; statement executed: delete from initial_money_count where booking_date=? and booking_period=? and cash_drawer_id=? and cashier_id=? and payment_method_id=? and pos_terminal_id=?; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; statement executed: delete from initial_money_count where booking_date=? and booking_period=? and cash_drawer_id=? and cashier_id=? and payment_method_id=? and pos_terminal_id=?
你知道我错过了什么吗?
我认为CrudRepository的findById方法适用于@Embeadable复合Id,对吗?
1条答案
按热度按时间7eumitmz1#
虽然我无法用spring-boot:3/jakarta.perstistence(,hibernate:6)重现这个问题
立即切换到spring-boot:2/javax.persistence hit!
(快速)解决方案:
...(跟踪)sql和参数似乎是正确的,所以问题一定是在事务传播/刷新的某个地方。
原因:(隐藏得很好)在发布说明/spring-boot 2和3之间的版本差异/ hibernate 5 vs 6... spring(- Boot )-test(!)...