![](/img/trans.png)
[英]Spring Data JPA - Hibernate - OneToMany, ManytoOne bidirectional
[英]Spring Data JPA - Multi Bidirectional @ManyToOne Propagation Data
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString
public class Lawyer extends ID{
@EqualsAndHashCode.Exclude
@ToString.Exclude
@JsonIgnore
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "lawyer")
private Set<Appointment> appointments = new HashSet<>();
public void addAppointment(Client client, LocalDateTime data) {
Appointment app = new Appointment (client,this,data);
this.consultas.add(app);
app.getClient().getAppointments().add(app);
}
}
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString
public class Appointment extends ID{
@EqualsAndHashCode.Exclude
@ToString.Exclude
@ManyToOne
private Client client;
@EqualsAndHashCode.Exclude
@ToString.Exclude
@ManyToOne
private Lawyer lawyer;
}
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString
public class Client extends ID{
@EqualsAndHashCode.Exclude
@ToString.Exclude
@JsonIgnore
@OneToMany
private Set<Appointment> appointments = new HashSet<>();
}
@MappedSuperclass
@Getter
@Setter
@NoArgsConstructor
public class ID{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
BootStrap類
@Component
public class Bootstrap implements ApplicationListener<ContextRefreshedEvent> {
private LaywerRepoI LaywerService;
public Bootstrap(LaywerRepoI LaywerService) {
this.LaywerService = LaywerService;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
Client c1 = new Client("Lukz", LocalDate.of(1971, 11, 26));
Client c2 = new Client("Adrian", LocalDate.of(1956, 01, 28));
Client c3 = new Client("Danny", LocalDate.of(1936, 1, 11));
Laywer l1 = new Laywer("Morgan", LocalDate.of(1941, 1, 1));
Laywer l2 = new Laywer("Ana", LocalDate.of(1931, 10, 1));
l1.addAppointment(c1,LocalDateTime.of(2018, 11, 22,18, 25));
l1.addAppointment(c1,LocalDateTime.of(2018, 11, 22, 10, 15));
LawyerService.save(l1);
LawyerService.save(l2);
}
}
當我在班級律師上進行新的約會時,我試圖將數據從律師傳播到客戶,但是我只能將其傳達給約會。 從約會到客戶端,我無法傳播它。...我收到此錯誤:
原因:java.lang.IllegalStateException:org.hibernate.TransientPropertyValueException:對象引用了一個未保存的瞬態實例-在刷新之前保存該瞬態實例
如何從約會傳播到客戶? 我已經閱讀了一些有關此類案例的文章,但我仍然不理解它們。
保存Lowyer
,因此需要層疊Lowyer->約會和約會->客戶關系。
因此,您還必須級聯關系“ 約會->客戶” 。
@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString
public class Appointment extends ID{
@EqualsAndHashCode.Exclude
@ToString.Exclude
@ManyToOne(cascade = CascadeType.ALL)
private Client client;
@EqualsAndHashCode.Exclude
@ToString.Exclude
@ManyToOne
private Lawyer lawyer;
}
spring-data-jpa是JPA之上的一層。 每個實體都有其自己的存儲庫,您必須處理該存儲庫。
@Entity
public class Lawyer {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "client", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Appointment> appointments;
@Entity
public class Client {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "lawyer", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Appointment> appointments;
@Entity
public class Appointment {
@EmbeddedId
private AppointmentId id = new AppointmentId();
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("lawyerId")
private Lawyer lawyer;
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("clientId")
private Client client;
public Appointment() {}
public Appointment(Lawyer lawyer, Client client) {
this.lawyer = lawyer;
this.client = client;
}
@SuppressWarnings("serial")
@Embeddable
public class AppointmentId implements Serializable {
private Long lawyerId;
private Long clientId;
並使用它,如上所示:
@Transactional
private void update() {
System.out.println("Step 1");
Client client1 = new Client();
Lawyer lawyer1 = new Lawyer();
Appointment apt1 = new Appointment(lawyer1, client1);
clientRepo.save(client1);
lawyerRepo.save(lawyer1);
appointmentRepo.save(apt1);
System.out.println("Step 2");
Client client2 = new Client();
Lawyer lawyer2 = new Lawyer();
Appointment apt2 = new Appointment(lawyer2, client2);
lawyerRepo.save(lawyer2);
clientRepo.save(client2);
appointmentRepo.save(apt2);
System.out.println("Step 3");
client2 = clientRepo.getOneWithLawyers(2L);
client2.getAppointments().add(new Appointment(lawyer1, client2));
clientRepo.save(client2);
System.out.println("Step 4 -- better");
Appointment apt3 = new Appointment(lawyer2, client1);
appointmentRepo.save(apt3);
}
注意,我沒有顯式設置AppointmentId
id。 這些由持久層(在這種情況下為休眠)處理。
還請注意,由於設置了CascadeType.ALL
,您可以使用其自己的存儲庫來顯式更新Appointment
條目,也可以通過將其添加到列表中並將其從列表中刪除來進行更新,如圖所示。 將CascadeType.ALL
用於spring-data-jpa的問題在於,即使您預取了聯接表實體,spring-data-jpa仍將再次執行此操作。 嘗試通過CascadeType.ALL
為新實體更新關系是有問題的。
如果沒有CascadeType
則lawyers
或Clients
列表(應為Sets)都不是關系的所有者,因此,在持久性方面添加到他們將不會完成任何事情,並且僅用於查詢結果。
在閱讀Appointment
關系時,由於沒有FetchType.EAGER
需要專門獲取它們。 FetchType.EAGER
的問題是開銷,如果您不希望加入連接,並且同時將其放在“ Client
和“ Lawyer
那么您將創建一個遞歸提取,以獲取所有Clients
和lawyers
的任何查詢。
@Query("select c from Client c left outer join fetch c.lawyers ls left outer join fetch ls.lawyer where t.id = :id")
Client getOneWithLawyers(@Param("id") Long id);
最后,請始終檢查日志。 創建關聯需要spring-data-jpa(我認為是JPA)讀取現有表以查看該關系是新關系還是更新關系。 無論您自己創建和保存Appointment
還是預取列表,都會發生這種情況。 JPA有單獨的合並,我認為您可以更有效地使用它。
create table appointment (client_id bigint not null, lawyer_id bigint not null, primary key (client_id, lawyer_id))
create table client (id bigint generated by default as identity, primary key (id))
alter table appointment add constraint FK3gbqcfd3mnwwcit63lybpqcf8 foreign key (client_id) references client
create table lawyer (id bigint generated by default as identity, primary key (id))
alter table appointment add constraint FKc8o8ake38y74iqk2jqpc2sfid foreign key (lawyer_id) references lawyer
insert into client (id) values (null)
insert into lawyer (id) values (null)
select appointmen0_.client_id as client_i1_0_0_, appointmen0_.lawyer_id as lawyer_i2_0_0_ from appointment appointmen0_ where appointmen0_.client_id=? and appointmen0_.lawyer_id=?
insert into appointment (client_id, lawyer_id) values (?, ?)
insert into lawyer (id) values (null)
insert into client (id) values (null)
select appointmen0_.client_id as client_i1_0_0_, appointmen0_.lawyer_id as lawyer_i2_0_0_ from appointment appointmen0_ where appointmen0_.client_id=? and appointmen0_.lawyer_id=?
insert into appointment (client_id, lawyer_id) values (?, ?)
select client0_.id as id1_1_0_, appointmen1_.client_id as client_i1_0_1_, appointmen1_.lawyer_id as lawyer_i2_0_1_, lawyer2_.id as id1_2_2_, appointmen1_.lawyer_id as lawyer_i2_0_0__, appointmen1_.client_id as client_i1_0_0__ from client client0_ left outer join appointment appointmen1_ on client0_.id=appointmen1_.lawyer_id left outer join lawyer lawyer2_ on appointmen1_.lawyer_id=lawyer2_.id where client0_.id=?
select client0_.id as id1_1_1_, appointmen1_.lawyer_id as lawyer_i2_0_3_, appointmen1_.client_id as client_i1_0_3_, appointmen1_.client_id as client_i1_0_0_, appointmen1_.lawyer_id as lawyer_i2_0_0_ from client client0_ left outer join appointment appointmen1_ on client0_.id=appointmen1_.lawyer_id where client0_.id=?
select appointmen0_.client_id as client_i1_0_0_, appointmen0_.lawyer_id as lawyer_i2_0_0_ from appointment appointmen0_ where appointmen0_.client_id=? and appointmen0_.lawyer_id=?
insert into appointment (client_id, lawyer_id) values (?, ?)
select appointmen0_.client_id as client_i1_0_0_, appointmen0_.lawyer_id as lawyer_i2_0_0_ from appointment appointmen0_ where appointmen0_.client_id=? and appointmen0_.lawyer_id=?
insert into appointment (client_id, lawyer_id) values (?, ?)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.