2009-07-13 33 views
8

Tôi đang gặp sự cố khi ánh xạ thuộc tính được nhúng của một lớp. Tôi đã tạo ra một số lớp học tương tự như những gì tôi đang cố gắng làm để minh họa. Về cơ bản, tôi có một hệ thống phân cấp lớp @Embeddable sử dụng Thừa kế. Lớp cấp cao nhất "Part Number" chỉ có một thuộc tính, và các lớp mở rộng thêm không có thuộc tính cho lớp "Part Number", chúng chỉ thêm một số validation/logic.JPA/Hibernate - Nhúng một thuộc tính

Dưới đây là những gì tôi có nghĩa là:

PHẦN

@Entity 
@Table(name="PART") 
public class Part { 
    private Integer id; 
    private String name; 
    private PartNumber partNumber; 

    @Id 
    @GeneratedValue(strategy=GenerationType.SEQUENCE) 
    public Integer getId() { 
     return id; 
    } 
    public void setId(Integer id) { 
     this.id = id; 
    } 

    @Column(name="PART_NAME") 
    public String getName() { 
     return name; 
    } 
    public void setName(String name) { 
     this.name = name; 
    } 

    @Embedded 
    public PartNumber getPartNumber() { 
     return partNumber; 
    } 
    public void setPartNumber(PartNumber partNumber) { 
     this.partNumber = partNumber; 
    } 

} 

số Part

@Embeddable 
public abstract class PartNumber { 

    protected String partNumber; 
    private String generalPartNumber; 
    private String specificPartNumber; 

    private PartNumber() { 

    } 

    public PartNumber(String partNumber) { 
     this.partNumber = partNumber; 

    } 

    @Column(name = "PART_NUMBER") 
    public String getPartNumber() { 
     return partNumber; 
    } 

    public void setPartNumber(String partNumber) { 
     this.partNumber = partNumber; 
    } 

    /** 
    * @param partNumber 
    * @return 
    */ 
    public boolean validate(String partNumber) { 
     // do some validation 
     return true; 
    } 

    /** 
    * Returns the first half of the Part Number 
    * 
    * @return generalPartNumber 
    */ 
    @Transient 
    public String getGeneralPartNumber() { 
     return generalPartNumber; 

    } 

    /** 
    * Returns the last half of the Part Number 
    * which is specific to each Car Brand 
    * 
    * @return specificPartNumber 
    */ 
    @Transient 
    public String getSpecificPartNumber() { 
     return specificPartNumber; 

    } 

} 

FORD số Part

public class FordPartNumber extends PartNumber { 

    /** 
    * Ford Part Number is formatted as 1234-#1234 
    * 
    * @param partNumber 
    */ 
    public FordPartNumber(String partNumber) { 
     super(partNumber); 
     validate(partNumber); 
    } 

    /* 
    * (non-Javadoc) 
    * 
    * @see com.test.PartNumber#validate(java.lang.String) 
    */ 
    @Override 
    public boolean validate(String partNumber) { 
     // do some validation 
     return true; 
    } 

    /* 
    * (non-Javadoc) 
    * 
    * @see com.test.PartNumber#getGeneralPartNumber() 
    */ 
    @Override 
    public String getGeneralPartNumber() { 
     return partNumber; 

    } 

    /* 
    * (non-Javadoc) 
    * 
    * @see com.test.PartNumber#getSpecificPartNumber() 
    */ 
    @Override 
    public String getSpecificPartNumber() { 
     return partNumber; 

    } 

} 

CHEVY số Part

public class ChevyPartNumber extends PartNumber { 

    /** 
    * Chevy Part Number is formatted as 1234-$1234 
    * 
    * @param partNumber 
    */ 
    public ChevyPartNumber(String partNumber) { 
     super(partNumber); 
     validate(partNumber); 
    } 

    /* 
    * (non-Javadoc) 
    * 
    * @see com.test.PartNumber#validate(java.lang.String) 
    */ 
    @Override 
    public boolean validate(String partNumber) { 
     // do some validation 
     return true; 
    } 

    /* 
    * (non-Javadoc) 
    * 
    * @see com.test.PartNumber#getGeneralPartNumber() 
    */ 
    @Override 
    public String getGeneralPartNumber() { 
     return partNumber; 

    } 

    /* 
    * (non-Javadoc) 
    * 
    * @see com.test.PartNumber#getSpecificPartNumber() 
    */ 
    @Override 
    public String getSpecificPartNumber() { 
     return partNumber; 

    } 
} 

Tất nhiên điều này không làm việc, bởi vì Hibernate bỏ qua các thừa kế Hierarchy và không thích thực tế là số Part là trừu tượng. Có cách nào để thực hiện điều này bằng cách sử dụng chú thích JPA hoặc Hibernate? Tôi đã thử sử dụng chú thích @Inheritance JPA.

Tôi không thể cấu trúc lại phần "PartNumber" của phân cấp vì Nhà phát triển ban đầu muốn có thể mở rộng PartNumber bằng nhiều lớp XXXXPartNumber.

Có ai biết nếu bất cứ điều gì như thế này sẽ là một phần của JPA 2.0 hoặc phiên bản Hibernate mới không?

Trả lời

15

Component (ví dụ: @Embeddable) kế thừa không được hỗ trợ và rất có thể sẽ không bao giờ được. Có một lý do chính đáng cho việc đó - định danh thực thể đóng một vai trò quan trọng trong tất cả các chiến lược thừa kế được hỗ trợ bởi Hibernate và các thành phần không có (định danh) các định danh.

Bạn có ba lựa chọn:

A) Bản đồ số Part (và tất cả hậu duệ của nó) như những thực thể. Số Part có thể vẫn còn trừu tượng:

@Entity 
@Inheritance(strategy=InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name="part_type", discriminatorType=DiscriminatorType.STRING) 
public abstract class PartNumber { 
... 
} 

@Entity 
@DiscriminatorValue("Ford") 
public class FordPartNumber extends PartNumber { 
... 
} 

B) Dựa trên ví dụ của bạn có vẻ như tất cả hậu duệ số Part khác nhau về hành vi chỉ (họ không giới thiệu bất kỳ đặc tính mới được lưu trữ). Nếu đó thực sự là trường hợp, bạn có thể ánh xạ các thuộc tính PartNumber cộng với giá trị phân biệt đối xử của riêng bạn (để bạn biết lớp nào khởi tạo) là thuộc tính riêng @Embedded và có các trình truy cập/setPartNumber() trong phần Lớp marshall/unmarshall các lớp con thích hợp. Bạn thậm chí có thể viết loại tùy chỉnh Hibernate của riêng bạn để làm điều đó cho bạn (nó khá đơn giản).

C) Nếu phần con số DO khác nhau trong các thuộc tính phải được lưu trữ và ánh xạ chúng dưới dạng thực thể là không thể chấp nhận vì bất kỳ lý do gì, bạn có thể sử dụng marshall/unmarshall chúng thành chuỗi (dưới dạng XML hoặc bất kỳ thứ gì khác phù hợp với hóa đơn) và lưu trữ đó. Tôi đang sử dụng XStream cho mục đích chính xác này và tôi đã viết một kiểu Hibernate đơn giản để đi với nó. Ánh xạ Part của bạn sẽ trông giống như

@Type(type="xmlBean") 
public PartNumber getPartNumber() { 
    return partNumber; 
} 
public void setPartNumber(PartNumber partNumber) { 
    this.partNumber = partNumber; 
} 

và con cháu PartNumber sẽ không phải được ánh xạ chút nào. Nhược điểm, tất nhiên, là đối phó với XML trong cơ sở dữ liệu là một chút rắc rối hơn để có thể không phải là cách tiếp cận lý tưởng cho một cái gì đó bạn có khả năng sẽ cần phải báo cáo. OTOH, tôi đang sử dụng tính năng này để lưu các cài đặt plugin và nó đã lưu tôi rất nhiều sự cố với ánh xạ/bảo trì DB.

+0

Dành cho B, bạn có biết của bất kỳ hướng dẫn tốt cho marshalling/unmarshalling lớp con thích hợp bằng cách sử dụng một kiểu ngủ đông tùy chỉnh? – systemoutprintln

+0

Bạn chỉ cần triển khai giao diện UserType của Hibernate - nó khá đơn giản. Đây là một ví dụ từ tài liệu Hibernate: (https://www.hibernate.org/172.html). Trong trường hợp của bạn, bạn sẽ trả về PartNumber từ phương thức "returnClass()" và tự động khởi tạo lớp con thích hợp trong "nullSafeGet()" dựa trên giá trị bạn lưu trữ trong một cột (và lấy từ ResultSet). – ChssPly76

1

Hmm, không chắc chắn. Bạn đã cố gắng đặt @MappedSuperclasson PartNumber và @Embeddable trên các lớp con. Xem thêm - https://forum.hibernate.org/viewtopic.php?t=966129

Là nút bên - bạn đã xem xét sử dụng Trình xác thực Hibernate thay vì phương pháp xác thực đã phát triển tại nhà chưa?

+0

đẹp cuộc gọi. Tôi tò mò như thế nào phát hiện ra các lớp con, cho rằng cấu hình không đề cập đến chúng, và JVM không cho phép bạn khám phá chúng. – skaffman

+0

ý của bạn là gì với "JVM không cho phép bạn khám phá chúng"? – Hardy

+0

@skaffman - Tôi nghĩ bạn cần di chuyển thẻ @Embedded vào lớp cha và sử dụng @MappedSuperclass ... có thể? – Petriborg

1

Có vẻ như bạn đang cố gắng làm là sử dụng "Thừa kế có thể nhúng" mà tôi không tin hỗ trợ hibernate.

@Embeddable 
@Inheritance(strategy=InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name="partType", discriminatorType=DiscriminatorType.STRING) 
public PartNumber getPartNumber() 
{ 
    return partNumber; 
} 

Vì có vẻ như giá trị dicriminating giữa Ford và Chevy trông giống như một định dạng. Tôi có lẽ sẽ ẩn bản đồ và thêm một chức năng khác để trả về kiểu cụ thể mà bạn muốn.

@Embeddable 
public PartNumber getPartNumber() 
{ 
    return partNumber; 
} 

@Transient 
public SpecificPartNumber getSpecificPartNumber() 
{ 
    return PartNumberFactory.create(getPartNumber()); 
} 

Xem liên kết trong nhận xét ...

+0

Tham khảo: http://stackoverflow.com/questions/917974/hibernate-embeddable-inheritance http://www.coderanch.com/t/415365/Object-Relational-Mapping/java/Embeddable-inheritance – ccclark

2

Tôi có một vấn đề tương tự trong schema của riêng tôi vì vậy những gì tôi đã viện đến lúc này là như thế này:

lớp phụ huynh:

@Entity 
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 
@SequenceGenerator(name="SEQ", sequenceName="part_id_seq", initialValue=1, allocationSize=1) 
public abstract class BasePart { 
    @Id 
    @Column(name="part_id") 
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ") 
    protected Long partId; 

    @YourBusinessKeyAnnotation 
    @Column(name="part_number") 
    protected String partNumber 
    ... 
} 

lớp Child:

@Entity 
public class FordPart extends BasePart { 
    ... 
} 

@Entity 
public class ChevyPart extends BasePart { 
    ... 
} 

Bây giờ tôi có thể thao tác phím biz tuy nhiên tôi cần và điều này hoạt động tốt vì mỗi loại phần khác nhau đều có bảng riêng của chúng (điều này rất hữu ích cho chúng ta).

Bạn cũng có thể sử dụng @Embedded với @AttributeOverrides Tôi nghĩ chỉ định tên cột khác nhau tuy nhiên bạn cần ... Có ví dụ từ annotation docs.

@Entity 
public class Person implements Serializable { 

    // Persistent component using defaults 
    Address homeAddress; 

    @Embedded 
    @AttributeOverrides({ 
      @AttributeOverride(name="iso2", column = @Column(name="bornIso2")), 
      @AttributeOverride(name="name", column = @Column(name="bornCountryName")) 
    }) 
    Country bornIn; 
    ... 
} 

...

@Entity 
public class Person implements Serializable { 

    // Persistent component using defaults 
    Address homeAddress; 

    @Embedded 
    @AttributeOverrides({ 
      @AttributeOverride(name="iso2", column = @Column(name="bornIso2")), 
      @AttributeOverride(name="name", column = @Column(name="bornCountryName")) 
    }) 
    Country bornIn; 
    ... 
} 

...

@Embedded 
@AttributeOverrides({ 
     @AttributeOverride(name="city", column = @Column(name="fld_city")), 
     @AttributeOverride(name="nationality.iso2", column = @Column(name="nat_Iso2")), 
     @AttributeOverride(name="nationality.name", column = @Column(name="nat_CountryName")) 
     //nationality columns in homeAddress are overridden 
}) 
Address homeAddress; 

Bạn có thể lạm dụng này đủ rằng bạn sẽ không quan tâm ...

0

Như được đề cập trong các câu trả lời khác có vẻ như Hibernate không hỗ trợ kế thừa của các nhúng (ít nhất là tôi không thể làm cho nó hoạt động). tôi đã đưa ra vài cách giải quyết (nhờ @ ChssPly76 cho nguồn cảm hứng):

1) Thay thế @Embaddable bởi @Entity và lưu trữ PartNumber thứ bậc trong bảng chuyên dụng. Tất nhiên nó yêu cầu @OneToOne mối quan hệ được sử dụng với lớp tổng hợp (Part).

2) Giới thiệu lớp thực thể trung gian phản ánh đầy đủ cấu trúc của bảng cơ sở dữ liệu. Và sau đó ánh xạ thực thể vào các lớp mô hình miền của bạn (và ngược lại) theo cách thủ công. Cách tiếp cận này cung cấp sự linh hoạt cực độ với giá của công việc thủ công để viết và kiểm tra mã bản đồ.

3) Lưu trữ PartNumber hậu duệ ở dạng tuần tự và dựa vào bộ nối tiếp để có thể khởi tạo lớp phù hợp trong quá trình deserialization.

Đối với công việc của mình, tôi đã chọn phương pháp thứ ba. Tôi sử dụng Hibernate Types để tuần tự hóa các đối tượng vào JSON và lưu trữ kết quả trong cột jsonb của PostgreSQL. Các kiểu Hibernate sử dụng Jackson dưới mui xe để tất cả các chú thích của nó (bao gồm cả các chú thích điều khiển hành vi đa hình) đều tùy ý sử dụng. Trong mã trường hợp của bạn sẽ như thế này:

@Entity 
public class Part { 
    // ... 

    @Type(type = "com.vladmihalcea.hibernate.type.json.JsonBinaryType") 
    private PartNumber partNumber; 

} 

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") 
@JsonSubTypes(value = [ 
    JsonSubTypes.Type(value = FordPartNumber::class, name = "FordPartNumber"), 
    JsonSubTypes.Type(value = ChevyPartNumber::class, name = "ChevyPartNumber") 
]) 
public abstract class PartNumber { /* ... */ } 

public class FordPartNumber extends PartNumber { /* ... */ } 

public class ChevyPartNumber extends PartNumber { /* ... */ } 

Kết quả là JSON trong cơ sở dữ liệu sẽ được xem xét như

{ 
    "type": "FordPartNumber", 
    // ... other serialized properties of the FordPartNumber class 
} 
Các vấn đề liên quan