我正在关注此map a many-to-many association with extra columns tutorial,但并不太成功。
所以我有以下实体...
@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生成的表是这样的:
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))
这是堆栈跟踪。.
javax.persistence.PersistenceException: org.hibernate.PropertyAccessException:无法设置字段值1 反映价值:[类 com.sample.shared.entity.PeopleStatsId.peopleId]的设置 com.sample.shared.entity.PeopleStatsId.peopleId位于 org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:149) 在 org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:157) 在 org.hibernate.internal.ExceptionConverterImpl.convert 等等... 63更多
我遇到了这个similar issue,找到了解决方法,但是没有用。尝试了第一种解决方案后,即为new PeopleStatsId
创建一个@EmbeddedId
对象,它引发了同样的错误。
任何人都可以引导我前进吗?谢谢。
更新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:分离的实体 传递给坚持:com.sample.Stats在 org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:124) 在 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);
}
答案 0 :(得分:0)
因此,在@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
中检索它的原因。
我还更新了poc in github。
希望这对接下来的人有帮助。