この章の内容は次のとおりです。
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バインディングの使用」を参照)。