3

我无法使用 builder .treat() 方法获取 JPA 查询以将AbstractAddress实体转换为其子类。AddressStd

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

背景

我使用 JPA 2.1(带有 Hibernate 5.0.2 FINAL)来映射一个遗留数据库,其中 aCustomer有 0:n 联系人,每个Contact都有一个AbstractAddress子类型AddressStdAddressPOB(邮政信箱)。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),但我无法让它分别检索AddressStd( houseNo, street) 和AddressPOB( poBoxNo) 的子类属性,就像语句一样

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]

我之前使用过 builder .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