我正在try 使用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);
        }
    }
}

以下是PRIMARY KEY@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,对吗?

推荐答案

虽然我无法用Spring-Boot重现这个问题:3/jakarta.perstistence(,hibernate:6),

立即切换到SpringBoot:2/javax.Persistence hit

(快速)解决方案:

@Test
@org.springframework.transaction.annotation.Transactional // !

...(跟踪)SQL和PARAMS似乎是正确的,因此问题一定出在事务传播/刷新的某个地方.

原因: (隐藏得很好)在发行说明/版本不同的春靴2和3/Hibernate5vs6...弹簧(-Boot)-测试(!)...

Java相关问答推荐

@ EnableRouting注释在Kotlin项目中不工作

Java Stream,需要更新列表对象列表

如何调用Firebase Realtime Database中的子图像列表到android studio中的回收器视图?

R.id.main给我一个红色错误,无法解析MainActivity.java中的符号main

为什么在maven中,getLast方法不适用于List?

如何从错误通道回复网关,使其不会挂起

在Spring终结点中,是否可以同时以大写和小写形式指定枚举常量?

无法使用Java&;TestContainers获取AWS SQS队列的属性

使用Spring和ActiveMQ的侦听器方法引发属性名称不能重复为空警告

如何从日志(log)行中删除包名称?

如何使用log4j2(Json)记录由";异常引起的所有";?

在Java 15应用程序中运行Java脚本和Python代码

Oj算法 MatrixR032从字符串、归一化和余弦相似度计算创建

Java 21中泛型的不兼容更改

spring 更新多项管理关系

Jackson YAML:支持锚点扩展/覆盖

移动二维数组的行

对于 Hangman 游戏,索引 0 超出长度 0 的范围

语句打印在错误的行(Java Token 问题)

有没有办法仅将 JComboBox 中的选定项目居中(因此保持组合框中的所有项目左对齐)