EclipseLink – 09 – Relationship – 03 – One To One
Merhaba Arkadaslar
Bu bolumde Entity Relationship konusuna devam edecegiz ve bir diger Single–Valued Relationship ozelligi gosteren OneToOne Relationship konusunu inceleyecegiz.
Bu bolumde hem Unidirectional hem de Bidirectional OneToOne Relationship icin ornek uygulama yapacagiz.
One To One Mapping
Her calisan (Employee) icin sadece bir park yeri (ParkingSpace) oldugunu dusunursek bu durumda Employee ile ParkingSpace arasinda OneToOne bir iliski/relationship olacaktir. Oncelikle Unidirectional OneToOne mapping inceleyelim , sonrasinda Bidirectional OneToOne mapping konusunu inceleyecegiz.
Unidirectional One To One Mapping
Entity’lerimiz Employee15 ve ParkingSpace ve iliski sahibi/owning side Employee15 olsun.
Bu durumda @OneToOne annotation Employee15 sinifinda ParkingSpace sinifi tipindeki instance variable/field uzerinde yer alacaktir.
Iliski Unidirectional oldugu icin ParkingSpace sinifinda Employee15 sinifi tipinde bir instance variable/field yer almayacaktir!
Employee15.java
package _15.oneToOne.uni.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToOne; @Entity public class Employee15 { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; private String surname; private int salary; @OneToOne private ParkingSpace parkingSpace; //constructors //getters and setter //toString }
ParkingSpace.java
package _15.oneToOne.uni.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class ParkingSpace { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private int lot; private String location; //constructors //getters and setter //toString }
ParkingService.java
package _15.oneToOne.uni.service; import _15.oneToOne.uni.model.ParkingSpace; public interface ParkingService { public ParkingSpace createParkingSpace(int lot, String location); }
ParkingServiceImpl.java
package _15.oneToOne.uni.service; import javax.persistence.EntityManager; import _15.oneToOne.uni.model.ParkingSpace; public class ParkingServiceImpl implements ParkingService { private EntityManager entityManager; public ParkingServiceImpl(EntityManager entityManager) { this.entityManager = entityManager; } public ParkingSpace createParkingSpace(int lot, String location) { ParkingSpace parkingSpace = new ParkingSpace(lot, location); entityManager.persist(parkingSpace); return parkingSpace; } }
EmployeeTest.java
package _15.oneToOne.uni.test; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import _15.oneToOne.uni.model.Employee15; import _15.oneToOne.uni.model.ParkingSpace; import _15.oneToOne.uni.service.EmployeeService; import _15.oneToOne.uni.service.EmployeeServiceImpl; import _15.oneToOne.uni.service.ParkingService; import _15.oneToOne.uni.service.ParkingServiceImpl; public class EmployeeTest { public static void main(String[] args) { EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("EmployeePersistenceUnit"); EntityManager entityManager = entityManagerFactory.createEntityManager(); EntityTransaction entityTransaction = entityManager.getTransaction(); EmployeeService employeeService = new EmployeeServiceImpl(entityManager); entityTransaction.begin(); Employee15 employee = employeeService.createEmployee("Levent", "Erguder", 1000); Employee15 employee2 = employeeService.createEmployee("James", "Gosling", 10000); Employee15 employee3 = employeeService.createEmployee("Joshua", "Bloch", 10000); entityTransaction.commit(); System.out.println("Persisted :" + employee); System.out.println("Persisted :" + employee2); System.out.println("Persisted :" + employee3); ParkingService parkingSpaceService = new ParkingServiceImpl(entityManager); entityTransaction.begin(); ParkingSpace parkingSpace = parkingSpaceService.createParkingSpace(10, "Flat1"); ParkingSpace parkingSpace2 = parkingSpaceService.createParkingSpace(20, "Flat2"); ParkingSpace parkingSpace3 = parkingSpaceService.createParkingSpace(30, "Flat3"); entityTransaction.commit(); System.out.println("Persisted :" + parkingSpace); System.out.println("Persisted :" + parkingSpace2); System.out.println("Persisted :" + parkingSpace3); entityTransaction.begin(); employee.setParkingSpace(parkingSpace); employee2.setParkingSpace(parkingSpace2); employee3.setParkingSpace(parkingSpace3); entityTransaction.commit(); entityManager.close(); entityManagerFactory.close(); } }
Ornegimizi calistirdigimizda Employee15 tablomuzda “parkingspace_id” foreing key kolonu olusacaktir.
Employee15 sinifimizda “parkingspace_id” field degeri(instance variable) ve ParkingSpace sinifimizda “id” field degeri(instance variable) kullanilir. Dolayisiyla “parkingspace_id” kolonu olusacaktir.
Dikkat edelim ; parkingSpace degeri ParkingSpace sinif isminden degil instance variable isminden gelmektedir. Eger Field Access yerine Property Access yaklasimi kullanilsaydi bu durumda getter metodunun ismine gore kolon/column olusacakti.
Varsayilan olarak verilen bu kolon ismini degistirmek istersek bu durumda @JoinColumn annotation kullanabiliriz.
@JoinColumn‘u @OneToOne annotation ile birlikte kullaniriz. Convention geregi logical annotation’lar , physical annotation’lardan once olmalidir. Yani @OneToOne annotation once tanimlanmalidir.
@OneToOne @JoinColumn(name="park_id") private ParkingSpace parkingSpace;
Bir diger nokta olarak JPA spect’inde OneToOne bir iliski icin unique kisitlama olmasi gerektigi belirtilmistir fakat implementation olarak baktigimizda sadece @OneToOne annotation kullanmak cozum saglamamaktadir.
Su sekilde tek bir instance’i tum Employee objeleri icin kullandigimizda, kafamizdaki OneToOne iliski problem cikartacaktir. Burada tek bir park alani tum calisanlara verilmis olacaktir.
entityTransaction.begin(); employee.setParkingSpace(parkingSpace); employee2.setParkingSpace(parkingSpace); employee3.setParkingSpace(parkingSpace); entityTransaction.commit();
Bu durumda @JoinColumn annotation ile birlikte unique parametresini kullanabiliriz.
Bu paremetre varsayilan olarak false’tur. unique=true dedigimizde ayni parkingSpace’i farkli Employee15 objeleri icin eklememize izin vermeyecektir, calisma zamaninda hata verecektir. Dolayisiyla gercek anlamda @OneToOne bir iliski saglayacaktir.
@JoinColumn(name="parking_id", unique=true)
Bidirectional One To One Mapping
Bidirectional bir OneToOne iliskide hem owning side hem de inverse side yer almaktadir. Bidirectional OneToOne bir iliski pek tercih edilen bir yaklasim degildir.
OneToOne bir iliskide owning side secimi tercihe baglidir. Unidirectional bir iliskide @OneToOne annotation hangi sinifta yer alirsa o Entity owning side olacaktir.
Bidirectional bir iliskide her iki sinifta da @OneToOne annotation yer alacaktir.
Employee16.java
package _16.oneToOne.bi.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; @Entity public class Employee16 { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; private String surname; private int salary; @OneToOne() @JoinColumn(name = "park_id") private ParkingSpace2 parkingSpace2; //constructors //getters and setters //toString }
Employee16 sinifimizda herhangi bir farklilik yer almamaktadir.
ParkingSpace2.java
package _16.oneToOne.bi.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToOne; @Entity public class ParkingSpace2 { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private int lot; private String location; @OneToOne(mappedBy = "parkingSpace2") private Employee16 employee; // Bidirectional One To One bir iliskide mappedBy inverse side'a eklenir. // Bunun anlami Employee16 Entity'si bu iliskinin sahibidir , owning side. // Foreign key bilgisi bu durumda Employee16 tablosunda yer almaktadir. // Boylelikle inverse side'ta (ParkingSpace2) foreign key bilgisi saklanmaz. //constructors //getters and setters //toString }
ParkingSpace2 sinifimizda Employee16 tipinde bir instance variable/field yer almaktadir !
Bu instance variable icin de @OneToOne annotation kullaniyoruz.
2 Entity icin de @OneToOne annotation kullandigimiz durumda bir tarafin inverse side oldugunu belirtmemiz gereklidir. Bunu yapmak icin mappedBy parametresini bir Entity icin kullanmamiz gerekecektir.
mappedBy parametresi OneToOne bidirectional bir iliskide inverse side ‘ta bulunur. Yani OneToOne bidirectional bir iliskide mappedBy parametresi hangi sinifta/taraf yer aliyorsa inverse side olacaktir.
Eger bir taraf icin mappedBy kullanilmazsa bu durumda 2 tane unidirectional OneToOne iliski olacaktir.
mappedBy , OneToOne iliskide sadece bir taraf icin kullanilabilir , 2 taraf icin de yazmak legal degildir calisma zamaninda hata verecektir.
Bidirectional OneToOne bir iliskide mappedBy inverse side’a eklenir.Bunun anlami Employee16 Entity’si bu iliskinin sahibidir , owning side. Foreign key bilgisi bu durumda Employee16 tablosunda yer almaktadir. Boylelikle inverse side’ta (ParkingSpace2) foreign key bilgisi saklanmaz.
Eger bir taraf icin mappedBy kullanilmazsa bu durumda 2 tane unidirectional OneToOne iliski olacaktir. Bu durumda her iki tabloda da karsilikli olarak foreign key bilgisi yer alacaktir , bu da istenen bir durum olmayacaktir.
Ornegimizi calistirdigimizda ;
Burada dikkat edecek olursak sonuclar bir onceki durumla ayni. Peki bu durumda ne fark var ? Eger mappedBy kullanmasaydik bu durumda 2 tane unidirectional OneToOne iliski olacakti ve ParkingSpace2 tablosunda foreign key yer alacakti bu da gereksiz yere bir kolon olmasina veri kalabaligina neden olacakti. Bunu onlemek adina mappedBy parametresi bidirectional OneToOne iliskide kullanilmaktadir.
mappedBy parametresini kaldiririp test ettigimizde ;
Bidirectional iliskinin dogru sekilde calismasi icin karsilikli olarak setter metodunu kullanmak gereklidir. Bu islem otomatik olarak yapilmaz!
Onceki ornegimizde su sekilde kullanmistik
employee.setParkingSpace(parkingSpace);
Bu ornegimizde ise su sekilde kullanamiz gereklidir. JPA otomatik olarak bir iliski olusturmayacaktir.
employee.setParkingSpace(parkingSpace); parkingSpace.setEmployee(employee);
EmployeeTest.java
package _16.oneToOne.bi.test; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import _16.oneToOne.bi.model.Employee16; import _16.oneToOne.bi.model.ParkingSpace2; import _16.oneToOne.bi.service.EmployeeService; import _16.oneToOne.bi.service.EmployeeServiceImpl; import _16.oneToOne.bi.service.ParkingService; import _16.oneToOne.bi.service.ParkingServiceImpl; public class EmployeeTest { public static void main(String[] args) { EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("EmployeePersistenceUnit"); EntityManager entityManager = entityManagerFactory.createEntityManager(); EntityTransaction entityTransaction = entityManager.getTransaction(); EmployeeService employeeService = new EmployeeServiceImpl(entityManager); entityTransaction.begin(); Employee16 employee = employeeService.createEmployee("Levent", "Erguder", 1000); Employee16 employee2 = employeeService.createEmployee("James", "Gosling", 10000); Employee16 employee3 = employeeService.createEmployee("Joshua", "Bloch", 10000); entityTransaction.commit(); System.out.println("Persisted :" + employee); System.out.println("Persisted :" + employee2); System.out.println("Persisted :" + employee3); ParkingService parkingSpaceService = new ParkingServiceImpl(entityManager); entityTransaction.begin(); ParkingSpace2 parkingSpace = parkingSpaceService.createParkingSpace(10, "Flat1"); ParkingSpace2 parkingSpace2 = parkingSpaceService.createParkingSpace(20, "Flat2"); ParkingSpace2 parkingSpace3 = parkingSpaceService.createParkingSpace(30, "Flat3"); entityTransaction.commit(); System.out.println("Persisted :" + parkingSpace); System.out.println("Persisted :" + parkingSpace2); System.out.println("Persisted :" + parkingSpace3); entityTransaction.begin(); employee.setParkingSpace(parkingSpace); parkingSpace.setEmployee(employee); employee2.setParkingSpace(parkingSpace2); parkingSpace2.setEmployee(employee2); employee3.setParkingSpace(parkingSpace3); parkingSpace3.setEmployee(employee3); // Bidirectional iliskinin dogru sekilde calismasi icin karsilikli // olarak setter metodunu kullanmak gereklidir. // Bu islem otomatik olarak yapilmaz! // Eger karsilikli olarak setter calistirilmazsa find islemi sonrasinda entityTransaction.commit(); ParkingSpace2 foundParkingSpace = parkingSpaceService.findParkingSpace(4); if (foundParkingSpace != null) { Employee16 emp = foundParkingSpace.getEmployee(); System.out.println(emp.getName() + " " + emp.getSurname() + " " + emp.getSalary()); } entityManager.close(); entityManagerFactory.close(); } }
findParkingSpace metodunu kullanarak id degeri 4 olan kaydi buduk. Daha sonrasinda bu kayit icin Employee16 objesine ulasip bilgileri aldik. Burada emp referans degiskenini yazdirmak StackOverflowError’a neden olur. toString metodu kullanilacagi icin karsilikli olarak surekli bir cagirma soz konusu olacaktir.
Employee16 –> ParkingSpace2
ParkingSpace2—> Employee16
Kaynak kodlar : Injavawetrust.jpa_v7
Yazimi burada sonlandiriyorum.
Herkese Bol Javali Gunler dilerim.
Be an oracle man , import java.*;
Levent Erguder
OCP, Java SE 6 Programmer
OCE, Java EE 6 Web Component Developer
Leave a Reply