5 Mart 2020 Perşembe

JPA @ManyToOne İlişki - Unidirectional

Giriş
Açıklaması şöyle.
For this relationship type, the default data loading method is EAGER: every time you ask for A, the B will also be retrieved.
Bu ilişki EAGER. Açıklaması şöyle
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER
ManyToOne İlişki OneToMany İlişkiye Ne Zaman Terchi Edilir
Açıklaması şöyle
Suppose you have a User and a Message, where a user can have thousands of messages, it could make sense to model it only as a ManyToOne from Message to User, because you'll rarely ask for all the messages of a user anyway.
optional Alanı
Açıklaması şöyle. Bu alanı false yapabiliyorsak yapmak lazım.
When a @OneToOne or a @ManyToOne relationship is mandatory - that is, the entity must have its associated relationship - it is important to tell JPA that this relationship is not optional.
...
Adding optional = false information will allow JPA to be more efficient in creating its select queries because it will know that it necessarily has an address associated with a person. Therefore, it is good practice to always specify this attribute when defining mandatory relationships.
Join Tablosu İle
Tuhaf görünse de ManyToOne JoinTable ile de çalışabilir. Eğer veritabanında zaten böyle bir tablo varsa işler kolaylaşıyor.
Örnek
Şöyle yaparız.
@ManyToOne(fetch = FetchType.LAZY, cascade = ALL)
@JoinTable(name = "company_services")
private Service service;

Join Tablosu Olmadan
Many tarafına @ManyToOne + @JoinColumn yazılır. @JoinColumn ile one tarafındaki primary key sütunu belirtilir.
Örnek
Şöyle yaparız. Kaydederken önce Troop nesnesi kaydedilir. Bu nesnenin primary key alanı alınarak Soldier sınıfındaki foreign key alanına atanır.
@Entity
public class Troop {
  @OneToMany(mappedBy="troop")
  public Set<Soldier> getSoldiers() {
  ...
}

@Entity
public class Soldier {
  @ManyToOne
  @JoinColumn(name="troop_fk")
  public Troop getTroop() {
  ...
Örnek
Bir kedinin bir çok sahibi olabilir. Şöyle yaparız.
@Entity
public class Cat {
  @Column(name = "OWNER_ID")
  private Long ownerId;

  @ManyToOne
  @JoinColumn(name = "OWNER_ID", insertable = false, updatable = false)
  private Owner owner;
}
ManyToOne ve Child Silinmesi
Örnek - Kötü Fikir
Elimizde şöyle bir kod olsun. Bu aslında kötü bir fikir.
public class User {

  @OneToMany(fetch = FetchType.EAGER)
  protected Set<Address> userAddresses;

}

public class Address {

  @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
  protected User addressOwner;

}
Açıklaması şöyle.  Adres silinince User'da silinmeye çalışır. User başka adreslere de bağlı olduğu için muhtemelen exception alırız.
It seems in your case to be a bad idea, as removing an Address would lead to removing the related User. As a user can have multiple addresses, the other addresses would become orphans. However the inverse case (annotating the User) would make sense - if an address belongs to a single user only, it is safe to propagate the removal of all addresses belonging to a user if this user is deleted.
Örnek - Doğru Olan
Usre sınıfındaki kod şöyle olsun
@OneToMany(cascade=CascadeType.ALL, mappedBy="user")
private Collection<Post> posts = new HashSet<>();

@OneToMany(cascade=CascadeType.ALL, mappedBy="user")
private Collection<Comment> comments = new HashSet<>();
Bu durumda önce child tarafın sonra da parent tarafın silindiğini görebiliriz. Çıktı olarak şunu alırız. Aslında kod ile sql uyumlu değil ancak mantık olarak nasıl çalıştığını gösteriyor. Burada Hibernate biraz fazla sql üretiyor. Bunu kısaltmanın yolu da burada.
Hibernate: delete from offices where id=?
Hibernate: delete from offices where id=?
Hibernate: delete from banks where id=?  
ManyToOne ve Parent Silinmesi - Aradaki İlişkinin Koparılması
Elimizde şöyle bir kod olsun.
@OneToMany (mappedBy="patient", fetch = FetchType.EAGER,orphanRemoval = false)
public Set<OutboundMessage> getOutboundMessages() 
{
  return outboundMessages;
}
Many tarafını nullable yaparız.
@ManyToOne (fetch=FetchType.EAGER)
@JoinColumn(name = "id_patient", nullable = true)
public Patient getPatient() {
  return patient;
}
Parent'ı silince child'lar halen kalır. Şöyle yaparız.
public void deletePatient(int patientId) {
  Session currentSession = sessionFactory.getCurrentSession();

  // get patient with primary key
  Patient patient = currentSession.get(Patient.class, patientId);  
  Set<OutboundMessage> messages = patient.getOutboundMessages();

  //set patient id null
  for(OutboundMessage message : messages) {
    message.setPatient(null);
  }

  //delete the patient
  currentSession.remove(patient);
}

Hiç yorum yok:

Yorum Gönder