JPA Nhiều trường Nhúng


84

Lớp thực thể JPA có thể chứa hai trường nhúng ( @Embedded) không? Một ví dụ sẽ là:

@Entity
public class Person {
    @Embedded
    public Address home;

    @Embedded
    public Address work;
}

public class Address {
    public String street;
    ...
}

Trong trường hợp này, a Personcó thể chứa hai Addresstrường hợp - nhà riêng và cơ quan. Tôi đang sử dụng JPA với việc triển khai Hibernate. Khi tôi tạo lược đồ bằng Hibernate Tools, nó chỉ nhúng một lược đồ Address. Những gì tôi muốn là hai phiên bản nhúng Address, mỗi phiên bản có tên cột được phân biệt hoặc chờ trước với một số tiền tố (chẳng hạn như nhà riêng và cơ quan). Tôi biết @AttributeOverrides, nhưng điều này yêu cầu mỗi thuộc tính phải được ghi đè riêng. Điều này có thể trở nên cồng kềnh nếu đối tượng nhúng ( Address) trở nên lớn vì mỗi cột cần được ghi đè riêng lẻ.

Câu trả lời:


28

Nếu bạn muốn có cùng một loại đối tượng có thể nhúng hai lần trong cùng một thực thể, tên cột mặc định sẽ không hoạt động: ít nhất một trong các cột sẽ phải rõ ràng. Hibernate vượt ra ngoài thông số kỹ thuật EJB3 và cho phép bạn nâng cao cơ chế mặc định thông qua NamingStrategy. DefaultComponentSafeNamingStrategy là một cải tiến nhỏ so với EJB3NamingStrategy mặc định cho phép các đối tượng nhúng được mặc định ngay cả khi được sử dụng hai lần trong cùng một thực thể.

Từ Tài liệu chú thích Hibernate: http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#d0e714


89

Cách thực hiện JPA chung là với @AttributeOverride. Điều này sẽ hoạt động trong cả EclipseLink và Hibernate.

@Entity 
public class Person {
  @AttributeOverrides({
    @AttributeOverride(name="street",column=@Column(name="homeStreet")),
    ...
  })
  @Embedded public Address home;

  @AttributeOverrides({
    @AttributeOverride(name="street",column=@Column(name="workStreet")),
    ...
  })
  @Embedded public Address work;
  }

  @Embeddable public class Address {
    @Basic public String street;
    ...
  }
}

9
Lưu ý rằng name="street"đề cập đến tên của thuộc tính, không phải tên cột.
Bart Swennenhuis

Điều này có được coi là "lắt léo", vì nó yêu cầu nhà phát triển của Person phải biết những điều thân mật về Địa chỉ lớp (như tên của trường chứa tên đường)?
mbmast

wow, vì vậy tôi phải lặp lại tất cả bằng cách sử dụng chú thích. wtf. tôi cũng có thể tự mình khai báo toàn bộ lớp nhúng theo cách thủ công bằng String homeStreet; Thay vào đó, chuỗi workStreeet, có thể xác định hơn.
mmm

6

Khi sử dụng Liên kết Eclipse, một giải pháp thay thế cho việc sử dụng AttributeOverrides nó để sử dụng một SessionCustomizer. Điều này giải quyết vấn đề cho tất cả các thực thể trong một lần:

public class EmbeddedFieldNamesSessionCustomizer implements SessionCustomizer {

@SuppressWarnings("rawtypes")
@Override
public void customize(Session session) throws Exception {
    Map<Class, ClassDescriptor> descriptors = session.getDescriptors();
    for (ClassDescriptor classDescriptor : descriptors.values()) {
        for (DatabaseMapping databaseMapping : classDescriptor.getMappings()) {
            if (databaseMapping.isAggregateObjectMapping()) {
                AggregateObjectMapping m = (AggregateObjectMapping) databaseMapping;
                Map<String, DatabaseField> mapping = m.getAggregateToSourceFields();

                ClassDescriptor refDesc = descriptors.get(m.getReferenceClass());
                for (DatabaseMapping refMapping : refDesc.getMappings()) {
                    if (refMapping.isDirectToFieldMapping()) {
                        DirectToFieldMapping refDirectMapping = (DirectToFieldMapping) refMapping;
                        String refFieldName = refDirectMapping.getField().getName();
                        if (!mapping.containsKey(refFieldName)) {
                            DatabaseField mappedField = refDirectMapping.getField().clone();
                            mappedField.setName(m.getAttributeName() + "_" + mappedField.getName());
                            mapping.put(refFieldName, mappedField);
                        }
                    }

                }
            }

        }
    }
}

}

+1 Sẽ rất vui nếu có cái này làm DescriptorCustomizer, để có thể kiểm soát cái này cho mỗi lớp, nhưng tôi chưa tìm ra cách truy cập ClassDescriptor của lớp được nhúng từ bên trong DescriptorCustomizer của lớp chủ.
oulenz

1
Giải pháp thay thế tôi đang sử dụng bây giờ là kiểm tra classDescriptor.getJavaClass()SessionCustomizer dựa trên danh sách các lớp mà tôi muốn nó ảnh hưởng.
oulenz

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.