3

ビルダー .treat() メソッドを使用してAbstractAddressエンティティをそのサブクラスにキャストする JPA クエリを取得できません。AddressStd

builder.equal( builder.treat(contracts.get("address"), 
                             AddressStd.class
                            ).get("houseNo"), std.getHouseNo()); 

バックグラウンド

JPA 2.1 (Hibernate 5.0.2 FINAL を使用) を使用しCustomerて、0:n の連絡先があり、それぞれContactAbstractAddressサブタイプAddressStdまたはAddressPOB(PO ボックス) があるレガシーデータベースをマップしました。Contact には追加のプロパティがありますが、簡単にするために以下には示していません。

ドメイン モデル

@Entity
@Table(name = "Customer")
public class Customer

    @Id @Column(name = "ID", nullable = false)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_CUSTOMER_ID")
    @SequenceGenerator(name="SEQ_CUSTOMER_ID", sequenceName="SEQ_CUSTOMER_ID")
    private Long   id;

    @OneToMany(fetch = FetchType.LAZY, mappedBy="Customer", orphanRemoval=true, cascade=CascadeType.ALL)
    private Set<Contact> contacts;   
    ...
}


@Entity
@Table(name = "CONTACT")
public class Contact {

    @Id @Column(name = "CONTACT_ID", nullable = false)
    private Long  id;

    @Basic @Column(name = "ROLE", length = 30)
    @Convert(converter = ProfileTypeConverter.class)
    private ContactType type;

    @OneToOne(fetch = FetchType.LAZY, mappedBy="contact", orphanRemoval=true, cascade=CascadeType.ALL)
    private AbstractAddress address;
}    

@Entity
@Table(name = "ADDRESS")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorFormula( "case when PO_BOX_NUMBER is null then 'ADDRESS_STD' ELSE 'ADDRESS_POB' end" )
public abstract class AbstractAddress {

    @Id @Column(name = "ID", nullable = false, precision = 0)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ADDRESS")
    @SequenceGenerator(name = "SEQ_ADDRESS", sequenceName = "SEQ_ADDRESS")
    private Long id;

    @OneToOne
    @PrimaryKeyJoinColumn(name = "CONTACT_ID", referencedColumnName = "ID")
    private Contact contact;

    @Basic @Column(name = "POSTAL_CODE", length = 10)
    private String postcode;

    @Basic @Column(name = "CITY", length = 40)
    private String city;

    @Basic @Column(name = "COUNTRY", length = 2)
    @Convert(converter = CountryConverter.class)
    private Country country;

    @Basic(optional=false)
    @Column(name = "LNG_CODE", length = 2)
    @Convert(converter = LanguageConverter.class)
    private Language language;

    @Basic(optional=false)
    @Column(name = "STATUS", length = 1)
    @Convert(converter = BooleanActiveConverter.class)
    private Boolean isActive = true;
}

@Entity
@DiscriminatorValue("ADDRESS_STD")
public class AddressStd extends AbstractAddress {
    @Basic @Column(name = "HOUSE_NUMBER", length = 8)
    private String houseNo;

    @Basic @Column(name = "STREET_NAME", length = 30)
    private String street;

    ...
} 

@Entity
@DiscriminatorValue("ADDRESS_POB")
public class AddressPOB extends AbstractAddress {
    @Basic
    @Column(name = "PO_BOX_NUMBER", length = 8)
    private String poBoxNo; 
    ... 
}

JPA クエリ

ビルド クエリはAbstractAddressプロパティ ( ) を取得するために機能しますが、次のようなステートメントとして、( ) と( )city, postcode, country, language, isActiveのサブクラス プロパティをそれぞれ取得することはできません。AddressStdhouseNo, streetAddressPOBpoBoxNo

builder.equal(builder.treat(contracts.get("address"), 
                             AddressStd.class
                            ).get("houseNo"), std.getHouseNo());

実行時例外をスローします

java.lang.IllegalArgumentException: Unable to locate Attribute  with the 
given name [houseNo] on this ManagedType [com.compX.appY.domain.contacts.AbstractAddress]

以前にビルダー .treat() メソッドを使用して型をキャストしましたが、正常に動作します。しかし、この場合、 .treat() はアドレスをAddressStd.class?にキャストできません。JPAの第一人者であるあなたの中に、その理由を知っている人はいますか? どうもありがとう、DaveT.

こちらの CriteriaQuery ビルダー コードを参照してください

private CriteriaQuery<Customer> createCriteria(CustomerSearchCriteria search) {

    CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
    CriteriaQuery<Customer> criteria = builder.createQuery(Customer.class);
    Root<Customer> root = criteria.from(Customer.class);

    List<Predicate> where = new ArrayList<Predicate>();

    ....

    if( search.getContactAddress() != null ) {
        AbstractAddress a = search.getContactAddress();

        Join<Customer, Contact> contracts = root.join("contacts"); // Customer.contacts

        if(a instanceof AddressStd){
            AddressStd std = (AddressStd)a;
            if(std.getHouseNo() != null) where.add(builder.equal(builder.treat(contracts.get("address"), AddressStd.class).get("houseNo"), std.getHouseNo()));
            if(std.getStreet()  != null) where.add(builder.equal(builder.treat(contracts.get("address"), AddressStd.class).get("street"),  std.getStreet()));
        }else if(a instanceof AddressPOB){
            AddressPOB pob = (AddressPOB)a;
            if(pob.getPoBoxNo() != null) where.add(builder.equal(builder.treat(contracts.get("address"), AddressPOB.class).get("poBoxNo"), pob.getPoBoxNo()));
        }

        if(a.getCity()     != null) where.add(builder.equal(contracts.get("address").get("city"), a.getCity()));
        if(a.getPostcode() != null) where.add(builder.equal(contracts.get("address").get("postcode"), a.getPostcode()));
        if(a.getCountry()  != null) where.add(builder.equal(contracts.get("address").get("country"), a.getCountry()));

        if(a.getLanguage() != null) where.add(builder.equal(contracts.get("address").get("language"), a.getLanguage()));
        if(a.isActive()    != null) where.add(builder.equal(contracts.get("address").get("isActive"), a.isActive()));
    }

    criteria.where( where.toArray(new Predicate[where.size()]) );

    return criteria;
} 
4

0 に答える 0