この章の内容は次のとおりです。
EclipseLinkでは、次を使用して共有参照キーと外部キーをサポートしています。
単一キー
コンポジット・キー
組込みキー・クラス
私有ではないリレーションシップをモデル化するには、ターゲット・オブジェクトにID(キー)が定義されており、かつソース・オブジェクトがこれらのIDを使用してリレーションシップをマップする必要があります。
キーとともに示されるリレーションシップは、@XmlID
および@XmlIDREF
注釈を使用します。JAXB仕様では@XmlID
のマークの付いているプロパティは文字列である必要がありますが、MOXy JAXBではこの制約は強制されません。
例7-1では、各Employeeは単一のmanagerと複数のreportsを持ちます。
例7-1 @XmlIDおよび@XmlIDREF注釈の使用
package example; import javax.xml.bind.annotation.*; @XmlAccessorType(XmlAccessType.FIELD) public class Employee { @XmlAttribute @XmlID private Integer id; @XmlAttribute private String name; @XmlIDREF private Employee manager; @XmlElement(name="report") @XmlIDREF private List<Employee> reports; ... }
次の例では、EclipseLinkのOXMメタデータ形式でこのマッピング情報を定義する方法を示します。
例7-2 XMLマッピングのサンプル
... <java-type name="Employee"> <java-attributes> <xml-attribute java-attribute="id" type="java.lang.Integer" xml-id="true"/> <xml-attribute java-attribute="name" type="java.lang.String"/> <xml-element java-attribute="manager" type="mypackage.Employee" xml-idref="true"/> <xml-element java-attribute="reports" type="mypackage.Employee" container-type="java.util.ArrayList" xml-idref="true"/> </java-attributes> </java-type> ...
これにより、次のXMLが生成されます。
<company> <employee id="1" name="Jane Doe"> <report>2</report> <report>3</report> </employee> <employee id="2" name="John Smith"> <manager>1</manager> </employee> <employee id="3" name="Anne Jones"> <manager>1</manager> </employee> </company>
managerおよびreports要素には、参照先のEmployeeインスタンスのIDが含まれます。
@XmlIDREF
注釈もまた@XmlList
注釈と互換性があり、Employeeオブジェクトは次のようにモデル化できます。
例7-3 @XmlList注釈の使用
package example;
import javax.xml.bind.annotation.*;
@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
@XmlID
@XmlAttribute
private Integer id;
@XmlAttribute
private String name;
@XmlIDREF
private Employee manager;
@XmlIDREF
@XmlList
private List<Employee> reports;
...
}
これにより、次のXMLが生成されます。
<company> <employee id="1" name="Jane Doe"> <reports>2 3</reports> </employee> <employee id="2" name="John Smith"> <manager>1</manager> </employee> <employee id="3" name="Anne Jones"> <manager>1</manager> </employee> </company>
JPAエンティティに埋込みIDクラスがある場合に、JAXBを使用して一連のJPAエンティティからXML表現を導出できます。
例7-4では、EmployeeId
はEmployeeクラスの組込みIDです。
例7-4 組込みIDのサンプル
@Entity
public class PhoneNumber {
@ManyToOne
@JoinColumns({
@JoinColumn(name="E_ID", referencedColumnName = "E_ID"),
@JoinColumn(name="E_COUNTRY", referencedColumnName = "COUNTRY")
})
private Employee contact;
}
@Entity
@IdClass(EmployeeId.class)
public class Employee {
@EmbeddedId
private EmployeeId id;
@OneToMany(mappedBy="contact")
private List<PhoneNumber> contactNumber;
}
@Embeddable
public class EmployeeId {
@Column(name="E_ID")
private BigDecimal eId;
private String country;
}
JAXBバインディングでは、XMLアクセッサ・タイプはすべてのモデル・クラスのFIELDに設定されます。これは、次に示すようにパッケージ・レベルのJAXB注釈として設定できます。
@XmlAccessorType(XmlAccessType.FIELD) package com.example.model; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType;
例7-5では、JAXB仕様を拡張するEclipseLink拡張機能の@XmlCustomizer
を使用します。contact属性は双方向リレーションシップのため、EclipseLink拡張機能の@XmlInverseReference
を含みます。
例7-5 @XmlCustomizer注釈の使用
@Entity
@IdClass(EmployeeId.class)
@XmlCustomizer(EmployeeCustomizer.class)
public class Employee {
@EmbeddedId
private EmployeeId id;
@OneToMany(mappedBy="contact")
@XmlInverseReference(mappedBy="contact")
private List<PhoneNumber> contactNumber;
}
EmployeeIdクラスのコンテンツをEmployeeクラスに関連付けられた複合型に組み込むには、id
プロパティのマッピングのXPathを自己( . )に変更します。その後、IDを表すXMLノードをXPathに指定します。
例7-6 XPathの変更
import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
public class EmployeeCustomizer implements DescriptorCustomizer {
public void customize(ClassDescriptor descriptor) throws Exception {
XMLCompositeObjectMapping idMapping =
(XMLCompositeObjectMapping) descriptor.getMappingForAttributeName("id");
idMapping.setXPath(".");
descriptor.addPrimaryKeyFieldName("eId/text()");
descriptor.addPrimaryKeyFieldName("country/text()");
}
}
ターゲット・オブジェクトが単一のIDを持つ場合は、@XmlIDREF
を使用します。ターゲット・オブジェクトに複合キーがあるので、フィールド@XmlTransient
にマークを付け、EclipseLink拡張機能の@XmlCustomizer
を使用してマッピングを設定します。
例7-7 @XmlTransient注釈の使用
@Entity
@XmlCustomizer(PhoneNumberCustomizer.class)
public class PhoneNumber {
@ManyToOne
@JoinColumns({
@JoinColumn(name="E_ID", referencedColumnName = "E_ID"),
@JoinColumn(name="E_COUNTRY", referencedColumnName = "COUNTRY")
})
@XmlTransient
private Employee contact;
}
XMLObjectReferenceMapping
が作成されます。マッピングには複数のキー・マッピングが含まれます。
import org.eclipse.persistence.config.DescriptorCustomizer; import org.eclipse.persistence.descriptors.ClassDescriptor; import org.eclipse.persistence.oxm.mappings.XMLObjectReferenceMapping; public class PhoneNumberCustomizer implements DescriptorCustomizer { public void customize(ClassDescriptor descriptor) throws Exception { XMLObjectReferenceMapping contactMapping = new XMLObjectReferenceMapping(); contactMapping.setAttributeName("contact"); contactMapping.setReferenceClass(Employee.class); contactMapping.addSourceToTargetKeyFieldAssociation("contact/@eID", "eId/text()"); contactMapping.addSourceToTargetKeyFieldAssociation("contact/@country", "country/text()"); descriptor.addMapping(contactMapping); } }
マップするオブジェクトにマルチパート・キー(つまり、一意性を判別するフィールドの組合せ)が含まれている場合、EclipseLinkの@XmlKey
および@XmlJoinNodes
を使用してこのリレーションシップを設定できます。
特定のクラス内で主キーを宣言する場合、1つ以上の@XmlKey
注釈を使用できます。単一キーの場合は、@XmlID
または@XmlKey
のいずれかを使用できます。コンポジット主キーの場合、複数の@XmlKey
注釈を使用するか、単一の@XmlID
を1つ以上の@XmlKey
注釈と組み合せることができます。
注意: コンポジット・キーは、JAXBを使用してJPAエンティティをマップする場合に便利です。詳細は、『Converting JPA entities to/from XML (via JAXB)』を参照してください。 |
例7-8では、各Employeeは単一のmanagerと複数のreportsを持ちます。また、Employeesはid
フィールドおよびname
フィールドの組合せによって一意に識別されます。
例7-8 @XmlKeyおよび@XmlJoinNodes注釈の使用
package example; import javax.xml.bind.annotation.*; import org.eclipse.persistence.oxm.annotations.*; @XmlAccessorType(XmlAccessType.FIELD) public class Employee { @XmlID @XmlAttribute private Integer id; @XmlKey @XmlAttribute private String name; @XmlJoinNodes( { @XmlJoinNode(xmlPath = "manager/@id", referencedXmlPath = "@id"), @XmlJoinNode(xmlPath = "manager/@name", referencedXmlPath = "@name") }) public Employee manager; @XmlJoinNodes( { @XmlJoinNode(xmlPath = "report/@id", referencedXmlPath = "@id"), @XmlJoinNode(xmlPath = "report/@name", referencedXmlPath = "@name") }) public List<Employee> reports = new ArrayList<Employee>(); ... }
例7-9では、EclipseLinkのOXMメタデータ形式でこのマッピング情報を定義する方法を示します。
例7-9 XMLマッピングのサンプル
... <java-type name="Employee"> <java-attributes> <xml-attribute java-attribute="id" xml-id="true" /> <xml-attribute java-attribute="name" xml-key="true" /> <xml-join-nodes java-attribute="manager"> <xml-join-node xml-path="manager/@id" referenced-xml-path="@id" /> <xml-join-node xml-path="manager/@name" referenced-xml-path="@name" /> </xml-join-nodes> <xml-join-nodes java-attribute="reports" container-type="java.util.ArrayList"> <xml-join-node xml-path="report/@id" referenced-xml-path="@id" /> <xml-join-node xml-path="report/@name" referenced-xml-path="@name" /> </xml-join-nodes> </java-attributes> </java-type> ...
これにより、次のXMLが生成されます。
<company> <employee id="1" name="Jane Doe"> <report id="2" name="John Smith"/> <report id="3" name="Anne Jones"/> </employee> <employee id="2" name="John Smith"> <manager id="1" name="Jane Doe"/> </employee> <employee id="3" name="Anne Jones"> <manager id="1" name="Jane Doe"/> </employee> </company>
EclipseLink MOXyで双方向リレーションシップをマップするには、バックポインタに@XmlInverseReference
として注釈が付けられる必要があります。この注釈がないと、マーシャリング中に循環リレーションシップの無限ループが発生します。
@XmlInverseReferences
には、リレーションシップの反対側のプロパティを示すmappedBy
属性を指定する必要があります。
例7-11ではEmployee
はPhoneNumbers
のコレクションを持ち、各PhoneNumber
はEmployee
へのバックポインタを持ちます。
例7-10 @XMlInverseReference注釈の使用
@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
private String name;
private List<PhoneNumber> phones = new ArrayList<PhoneNumber>();
...
}
@XmlAccessorType(XmlAccessType.FIELD)
public class PhoneNumber {
private String number;
@XmlInverseReference(mappedBy="phones")
private Employee employee;
...
}
例7-11では、EclipseLinkのOXMメタデータ形式でこのマッピングを定義する方法を示します。
例7-11 XMLマッピングのサンプル
... <java-type name="Employee"> <java-attributes> <xml-element java-attribute="name" type="java.lang.String"/> <xml-element java-attribute="phones" type="PhoneNumber" container-type="java.util.ArrayList"/> </java-attributes> </java-type> <java-type name="PhoneNumber"> <java-attributes> <xml-element java-attribute="number" type="java.lang.String"/> <xml-inverse-reference java-attribute="employee" type="Employee" mapped-by="phones" /> </java-attributes> </java-type> ... In addition, when using @XmlInverseReference, it is not necessary to explicitly set the back-pointer in your Java code; EclipseLink will do this for you automatically:
Employee emp = new Employee(); emp.setName("Bob Smith"); PhoneNumber p = new PhoneNumber(); p.setNumber("555-1212"); emp.getPhones().add(p); // Not Necessary // p.setEmployee(emp);
@XmlInverseReference
バックポインタは、次のタイプのマッピングとともに使用できます。
1対1リレーションシップ(「私有の1対1リレーションシップのマッピング」を参照)
1対多リレーションシップ(「私有の1対多リレーションシップのマッピング」を参照)
単一キーのリレーションシップ(「単一キーのリレーションシップのマッピング」を参照)
コンポジット・キーのリレーションシップ(「コンポジット・キーのリレーションシップのマッピング」を参照)
@XmlInverseReference
は、特にJPAエンティティをXMLにマッピングする場合に便利です(「XMLバインディングの使用」を参照)。