2015-09-28 21 views
7

Tôi có một vấn đề kỳ lạ nơi hibernate không tạo ra các loại thực thể dự kiến ​​trong một relataionship nhiều. Chúng tôi có các đối tượng sau với hệ thống phân cấp lớp (giản thể):Hibernate tạo subtype thực thể sai trong mối quan hệ

@Entity 
@Table(name = "A") 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING, length = 1) 
public abstract class A { 

    @Id 
    ... 
    public Long getId() { ... } 
    ... 
} 

@Entity 
@DiscriminatorValue("1") 
public class A1 extends A { 
    ... 
} 

@Entity 
@DiscriminatorValue("2") 
public class A2 extends A { 
    ... 
} 


@Entity 
@Table(name = "B") 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING, length = 1) 
public abstract class B<AClass extends A> { 

    protected AClass a; 

    @Id 
    ... 
    public Long getId() { ... } 
    ... 

    public abstract AClass getA(); 
    public void setA(AClass a) { ... } 
} 

@Entity 
@DiscriminatorValue("1") 
public class B1 extends B<A1> { 
    ... 

    @Override 
    @ManyToOne(fetch = EAGER) 
    @JoinColumn(name = "A_ID") 
    public A1 getA() { ... } 
} 

@Entity 
@DiscriminatorValue("2") 
public class B2 extends B<A2> { 
    ... 

    @Override 
    @ManyToOne(fetch = EAGER) 
    @JoinColumn(name = "A_ID") 
    public A2 getA() { ... } 
} 

Trong persistence.xml cả các đối tượng được khai báo theo thứ tự

A2 
A1 
B2 
B1 

Bây giờ tôi có thể tạo thể hiện của A1 và B1 trong DB:

A1 a1 = new A1(); 
entityManager.persist(a1); 
B1 b1 = new B1(); 
b1.setA(a1); 
entityManager.persist(b1); 

Tôi có thể xem các trường hợp được lưu vào DB chính xác mỗi ID có 1, DISCRIMINATOR cũng là 1, A_ID trong B cũng là 1.

Khi tôi bây giờ cố gắng để có được B (trong một phiên hibernate):

B b = entityManager.find(B.class, 1L); 

tôi nhận được ngoại lệ:

org.hibernate.PropertyAccessException: Exception occurred inside getter of B 
Caused by: java.lang.ClassCastException: A2 cannot be cast to A1 
at B1.getA(B1.java:61) 
... 108 more 

Với gỡ lỗi tôi phát hiện ra rằng chế độ ngủ đông đang tạo ra các thực thể chính xác loại B1 và ​​tạo ra một thực thể không chính xác của loại A2 cho mối quan hệ với A. Loại đúng A1 được tạo ra nếu thứ tự trong persistence.xml được thay đổi. Có vẻ như hibernate không lấy cột DISCRIMINATOR của bảng A trong trường hợp này nhưng luôn luôn tạo kiểu con đầu tiên được khai báo trong cấu hình. Làm thế nào để sửa cái này? Có điều gì sai với chú thích không?

(Tôi cũng đã thực hiện cụ thể của phương pháp getA() với các chú thích của nó trong siêu kiểu B lúc đầu, nhưng điều này dẫn đến vấn đề tương tự.)

Trả lời

3

Với Hibernate 5.0.2.Thật tôi đã có thể tạo ví dụ của bạn bằng cách sử dụng @ManyToOne(..., targetEntity = A.class). Tôi cũng thay thế public abstract AClass getA(); bằng một bộ thu thông thường.

@Entity 
@Table(name = "B") 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING, length = 1) 
public abstract class B<AClass extends A> { 
    private Long id; 
    private AClass a; 

    @Id 
    @GeneratedValue 
    public Long getId() { 
     return id; 
    } 

    public void setId(Long id) { 
     this.id = id; 
    } 

    @ManyToOne(fetch = FetchType.EAGER, targetEntity = A.class) 
    @JoinColumn(name = "A_ID") 
    public AClass getA() { 
     return a; 
    } 

    public void setA(AClass a) { 
     this.a = a; 
    } 
} 
@Entity 
@DiscriminatorValue("1") 
public class B1 extends B<A1> { 
    // no need to override getA() 
} 
@Entity 
@DiscriminatorValue("2") 
public class B2 extends B<A2> { 
    // no need to override getA() 
} 

tôi không tìm thấy bất cứ điều gì về hành vi này trong tài liệu.Vì vậy, tôi chỉ có quan sát của tôi:

  • Without targetEntity = A.class Hibernate thậm chí không truy vấn DISCRIMINATOR cột của bảng A khi hăm hở lấy hàng từ A cùng với B, như nó đã thực hiện một quyết định về loại thực tế của A.
  • Khi tôi thêm targetEntity = A.class, A.DISCRIMINATOR xuất hiện trong truy vấn và đối tượng được tạo với các lớp con bên phải của lớp A.
+0

Cảm ơn câu trả lời của bạn. Hôm qua tôi đã có cơ hội để kiểm tra điều này và nó hoạt động trong kịch bản của câu hỏi :) – Gandalf

3

Bạn đang sử dụng tham gia cùng một cột (A_ID) trong cả hai B1B2 lớp con.

Sử dụng một khác nhau trong mỗi lớp con:

@Entity 
@DiscriminatorValue("1") 
public class B1 extends B<A1> { 
    @Override 
    @ManyToOne(fetch = EAGER) 
    @JoinColumn(name = "A1_ID") 
    public A1 getA() { ... } 
} 

@Entity 
@DiscriminatorValue("2") 
public class B2 extends B<A2> { 
    @Override 
    @ManyToOne(fetch = EAGER) 
    @JoinColumn(name = "A2_ID") 
    public A2 getA() { ... } 
} 

Mặc dù nó có thể làm cho tinh thần để tái sử dụng các cột (với các cột khác nhau ai sẽ nào được null cho mỗi bản ghi phụ thuộc vào phân lớp), có vẻ như sử dụng Hibernate tên cột trong nội bộ để xác định duy nhất một số phần tử ánh xạ trong cùng một bảng. Đó là lý do tại sao nó có thể bỏ qua định nghĩa của ánh xạ nhiều người trong B1 và sử dụng một từ B2 cho nó là tốt (vì B2 được xác định trước B1 trong persistence.xml).

+0

Cảm ơn Dragan vì câu trả lời của bạn! :) Tôi đã không có thời gian để kiểm tra nếu nó hoạt động vì câu trả lời khác hoạt động và có một số lợi thế: 1. Ít mã. 2. Các cột DB ít hơn 3. Chúng ta có thể giữ các ràng buộc khóa không rỗng và ngoại trong cột DB. – Gandalf

Các vấn đề liên quan