我无法使用 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
子类型AddressStd
或AddressPOB
(邮政信箱)。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;
}