ビルダー .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 の連絡先があり、それぞれContact
にAbstractAddress
サブタイプ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
のサブクラス プロパティをそれぞれ取得することはできません。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]
以前にビルダー .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;
}