プライマリ・コンテンツに移動
Oracle® Fusion Middleware Oracle TopLinkによるJAXBアプリケーションの開発
12c (12.2.1.2.0)
E82672-01
  目次へ移動
目次

前
 
次
 

7 共有参照のリレーションシップのマッピング

この章の内容は次のとおりです。

7.1 キーと外部キーの理解

EclipseLinkでは、次を使用して共有参照キーと外部キーをサポートしています。

  • 単一キー

  • コンポジット・キー

  • 組込みキー・クラス

7.2 単一キーのリレーションシップのマッピング

私有ではないリレーションシップをモデル化するには、ターゲット・オブジェクトに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が含まれます。

7.2.1 @XmlListの使用

@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>

7.3 組込みキー・クラスの使用

JPAエンティティに埋込みIDクラスがある場合に、JAXBを使用して一連のJPAエンティティからXML表現を導出できます。

例7-4では、EmployeeIdEmployeeクラスの組込み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);
    }
 
}

7.4 コンポジット・キーのリレーションシップのマッピング

マップするオブジェクトにマルチパート・キー(つまり、一意性を判別するフィールドの組合せ)が含まれている場合、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を持ちます。また、Employeesidフィールドおよび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>

7.5 双方向リレーションシップのマッピング

EclipseLink MOXyで双方向リレーションシップをマップするには、バックポインタに@XmlInverseReferenceとして注釈が付けられる必要があります。この注釈がないと、マーシャリング中に循環リレーションシップの無限ループが発生します。

@XmlInverseReferencesには、リレーションシップの反対側のプロパティを示すmappedBy属性を指定する必要があります。

例7-11ではEmployeePhoneNumbersのコレクションを持ち、各PhoneNumberEmployeeへのバックポインタを持ちます。

例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バックポインタは、次のタイプのマッピングとともに使用できます。

@XmlInverseReferenceは、特にJPAエンティティをXMLにマッピングする場合に便利です(「XMLバインディングの使用」を参照)。