@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:对象引用了未保存的临时实例-在刷新之前保存该临时实例
我如何从约会传播到客户? 我已经阅读了一些有关此类案例的文章,但我仍然不理解它们。
答案 0 :(得分:0)
您保存了Lowyer
,因此需要将 Lowyer->约会和 Appointment->客户关系进行级联。
因此,您还必须级联关系约会->客户。
@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;
}
答案 1 :(得分:0)
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。这些由持久层(在这种情况下为休眠)处理。
还请注意,由于已设置Appointment
,因此可以使用其自己的存储库显式更新CascadeType.ALL
条目,也可以通过从列表中添加和删除它们来进行更新,如图所示。对spring-data-jpa使用CascadeType.ALL
的问题在于,即使您预取了连接表实体,spring-data-jpa仍将再次执行此操作。尝试通过CascadeType.ALL
更新新实体的关系是有问题的。
没有CascadeType
或lawyers
列表(应为Set)都不是该关系的所有者,因此,在持久性和仅用于查询结果。
在阅读Clients
关系时,由于没有Appointment
,因此需要专门获取它们。 FetchType.EAGER
的问题是开销,如果您不希望联接,并且同时将其放在FetchType.EAGER
和Client
上,那么您将创建一个递归获取来获取所有{{ 1}}和Lawyer
进行任何查询。
Clients
最后,始终检查日志。创建关联需要spring-data-jpa(我认为是JPA)读取现有表以查看该关系是新关系还是更新关系。无论您自己创建和保存lawyers
还是预取列表,都会发生这种情况。 JPA有单独的合并,我认为您可以更有效地使用它。
@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);