簡體   English   中英

使用@Embeddable保存實體時的PropertyAccessException

[英]PropertyAccessException when saving entity with @Embeddable

我正在跟蹤這張地圖,其中包含多列關聯以及額外的列教程,但並不太成功。

所以我有以下實體...

@Data
@Entity
@Table(name = "PEOPLE")
public class People implements Serializable {

    @Id
    @SequenceGenerator(name = "people", sequenceName = "people_id_seq", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "people")
    private long peopleId;

    private String peopleName;

    @ToString.Exclude
    @EqualsAndHashCode.Exclude
    @OneToMany(
            mappedBy = "people", 
            cascade = CascadeType.ALL, 
            orphanRemoval = true
    )
    private List<PeopleStats> peopleStats;

    public void addStats(Stats stats) {

        if (this.peopleStats == null) {
            this.peopleStats = new ArrayList<>();
        }

        PoepleStats pStats = PoepleStats.builder().people(this).stats(stats).build();

        this.peopleStats.add(pStats);
    }
}

@Data
@Entity
@Table(name = "STATS")
public class Stats implements Serializable {

    @Id
    @SequenceGenerator(name = "stats", sequenceName = "stats_id_seq", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "stats")
    private long statsId;

    private String statsName;
    private String statsDescription;

}

@Data
@Entity
@Table(name = "PEOPLE_STATS")
public class PeopleStats implements Serializable {

    @EmbeddedId
    private PeopleStatsId peopleStatsId;

    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("peopleId")
    private People people;

    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("statsId")
    private Stats stats;

    private long value;

}

@Data
@Embeddable
@EqualsAndHashCode
public class PeopleStatsId implements Serializable {

    // Putting @Column(name = "people_id") or not doesn't seem to have any effect
    private long peopleId;
    // Same goes for this
    private long statsId;

}

然后進行以下單元測試。

@RunWith(SpringRunner.class)
@DataJpaTest
public class PeopleRepositoryTest {

    @Autowired
    private TestEntityManager entityManager;


    @Test
    public void testSavePeople() {

        // People object created
        people.addStats(Stats.builder().statsId(new Long(1)).statsName("a").statsDescription("b").build());

        this.entityManager.persistAndFlush(people);
    }
}

休眠生成的表是這樣的:

Hibernate: create table people_stats (value bigint not null, people_people_id bigint not null, stats_stats_id bigint not null, primary key (people_people_id, stats_stats_id))

這是stacktrace。

javax.persistence.PersistenceException:org.hibernate.PropertyAccessException:無法通過反射設置字段值1的值:[class com.sample.shared.entity.PeopleStatsId.peopleId] com.sample.shared.entity.PeopleStatsId.peopleId的設置者org.hibernate.internal.ExceptionConverterImpl.convert(Exception。更多

我遇到了類似的問題 ,但沒有解決方案。 在嘗試第一個解決方案后,即為@EmbeddedId創建一個new PeopleStatsId對象,它引發了相同的錯誤。

有人可以引導我前進嗎? 謝謝。

更新1:我已經在github上上傳了POC。

更新2

public void addStats(Stats stats) {

        if (this.peopleStats == null) {
            this.peopleStats = new ArrayList<>();
        }

        PeopleStats pStats = PeopleStats.builder().peopleStatsId(new PeopleStatsId()).people(this).stats(stats).build();

        this.peopleStats.add(pStats);
    }

現在,它拋出分離的實體錯誤。

引起原因:org.hibernate.PersistentObjectException:傳遞給持久對象的分離實體:org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:124)上的com.sample.Stats org.hibernate.internal.SessionImpl.firePersist( SessionImpl.java:807)...還有68

更新3

我已經將CascadeType.ALL更改為MERGE ,看來可以解決問題,但是我不確定為什么。 我什至刪除了更新2中有關.peopleStatsId(new PeopleStatsId()) ,它也可以正常工作。 現在我更加困惑。

在人類中:

    @OneToMany(
            mappedBy = "people", 
            cascade = CascadeType.MERGE, 
            orphanRemoval = true
    )
    private List<PeopleStats> peopleStats;

    public void addStats(Stats stats) {

        if (this.peopleStats == null) {
            this.peopleStats = new ArrayList<>();
        }

        PeopleStats pStats = PeopleStats.builder().people(this).stats(stats).build();

        this.peopleStats.add(pStats);
    }

您需要在PeopleStats類中實例化peopleStatsId。 所以換行:

@EmbeddedId
private PeopleStatsId peopleStatsId;

對此:

@EmbeddedId
private PeopleStatsId peopleStatsId = new PeopleStatsId();

Hibernate嘗試設置PeopleStatsId的字段,但該實例等於null,因此拋出NullPointerException。

因此,在@K.Nicholas和研究的幫助下,我認為我已經設法解決了問題並從中學到了新東西。

@K.Nicholas關於設置“人員”和“統計”的字段是正確的,但最初對我來說還不太清楚。 無論如何,后來我想到了。

基本上,除“ People課程外,所有其他課程幾乎保持相同。

@Data
@Entity
@Builder
@Table(name = "PEOPLE")
public class People implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @SequenceGenerator(name = "people", sequenceName = "people_id_seq", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "people")
    private long peopleId;

    private String peopleName;

    @ToString.Exclude
    @EqualsAndHashCode.Exclude
    @OneToMany(
            mappedBy = "people", 
            cascade = CascadeType.ALL, 
            orphanRemoval = true
    )
    private List<PeopleStats> peopleStats;

    // Maintain the state of objects association
    public void addStats(Stats stats) {

        if (this.peopleStats == null) {
            this.peopleStats = new ArrayList<>();
        }

        // Important to ensure that a new instance of PeopleStatsId is being passed into the field
        // otherwise, PropertyAccessException will be encountered
        PeopleStats pStats = PeopleStats.builder()
                                .people(this)
                                .stats(stats)
                                .peopleStatsId(new PeopleStatsId(this.getPeopleId(), stats.getStatsId()))
                                .build();

        this.peopleStats.add(pStats);
    }

}

請注意addStats方法中的注釋,在該注釋中,我需要傳遞一個新的PeopleStatsId對象實例以初始化PeopleStatsId對象,該對象首先應該已經完成​​,但實際上並非必須如此。 學習到教訓了。

我還提到我以前曾遇到過獨立實體的問題。 這是因為我在不需要時會嘗試在“統計信息ID”字段中進行設置。

根據《 休眠指南》

超脫

實體具有關聯的標識符,但不再與持久性上下文關聯(通常是因為持久性上下文已關閉或實例已從上下文中退出)

在我的帖子中,我試圖將“統計數據”設置為“人”,然后堅持下去。

@Test
    public void testSavePeople() {

        // People object created
        people.addStats(Stats.builder().statsId(new Long(1)).statsName("a").statsDescription("b").build());

        this.entityManager.persistAndFlush(people);
    }

.statsId(new Long(1))是問題所在,因為由於存在ID,它被視為獨立實體。 CascadeType.MERGE在這種情況下可以工作是因為我認為由於saveOrUpdate功能? 無論如何,如果不設置statsId, CascadeType.ALL就可以正常工作。

單元測試的樣本(工作中):

@Test
    public void testSavePeopleWithOneStats() {

        // Creates People entity
        People people = this.generatePeopleWithoutId();
        // Retrieve existing stats from StatsRepository
        Stats stats = this.statsRepository.findById(new Long(1)).get();
        // Add Stats to People
        people.addStats(stats);
        // Persist and retrieve
        People p = this.entityManager.persistFlushFind(people);

        assertThat(p.getPeopleStats().size()).isEqualTo(1);
    }

我有一個data-h2.sql腳本,該腳本在單元測試開始時就加載了Stats數據,這就是為什么我可以從statsRepository檢索它的statsRepository

我還在github中更新了我的poc

希望這對接下來的人有所幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM