ヘッダーをスキップ
Oracle Fusion Middleware Oracle TopLink開発者ガイド
11gリリース1(11.1.1)
B56246-01
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

114 作業ユニットの基本APIの使用

この章では、開発サイクル全般で最も一般的に使用する可能性のある基本的な作業ユニットAPIのコールについて説明します。

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

詳細は、第115章「作業ユニットの拡張APIの使用」を参照してください。

114.1 作業ユニットの取得

次の例は、クライアント・セッション・オブジェクトから作業ユニットを取得する方法を示しています。

Server server =
    (Server) SessionManager.getManager().getSession(
        sessionName, MyServerSession.class.getClassLoader());
Session session = (Session) server.acquireClientSession();
UnitOfWork uow = session.acquireUnitOfWork();

作業ユニットは、どのセッション・タイプからも取得できます。実行時のセッションの取得の詳細は、87.2.4項「セッション・マネージャによる実行時のセッションの取得」を参照してください。

各トランザクションの前に新規セッションを作成してログインする必要はありません。通常、お薦めするのは、クライアント・アクセスまたはスレッドごとにクライアント・セッションを取得し、このクライアント・セッションから必要な作業ユニットを取得することです。

作業ユニットは、commitまたはreleaseメソッドがコールされるまで有効です。トランザクションのコミットまたは解放の後は、トランザクションが失敗してロールバックされた場合でも、作業ユニットは無効になります。

115.6項「コミット後の作業ユニットの再開」で説明するように、commitAndResumeメソッドがコールされた場合は、作業ユニットは有効なまま残ります。

作業ユニットをJTAとともに使用している場合、115.13項「作業ユニットと外部トランザクション・サービスの統合」で説明するように、拡張APIであるgetActiveUnitOfWorkメソッドを使用する必要があります。

114.2 オブジェクトの作成

作業ユニットで新規オブジェクトを作成する場合、registerObjectメソッドを使用して、作業ユニットがコミット時にオブジェクトをデータベースに書き込むようにします。

作業ユニットは、1対1マッピングと1対多マッピングの外部キー情報を使用して、コミット順序を計算します。トランザクションのコミット中に制約に関する問題が発生した場合は、マッピングの定義を確認してください。registerObjectメソッドを使用してオブジェクトを登録する順序は、コミット順序には影響を与えません。

例114-1例114-2は、作業ユニットのregisterObjectメソッドによって返されるクローンを使用し、単純な(リレーションシップを持たない)オブジェクトを作成して永続化する方法を示します。

例114-1 オブジェクトの作成: 推奨方法

UnitOfWork uow = session.acquireUnitOfWork();
    Pet pet = new Pet();
    Pet petClone = (Pet)uow.registerObject(pet);
    petClone.setId(100);
    petClone.setName("Fluffy");
    petClone.setType("Cat");
uow.commit();

例114-2は、一般的な代替方法を示します。

例114-2 オブジェクトの作成: 代替方法

UnitOfWork uow = session.acquireUnitOfWork();
    Pet pet = new Pet();
    pet.setId(100);
    pet.setName("Fluffy");
    pet.setType("Cat");
    uow.registerObject(pet);
uow.commit();

どちらの方法でも、次のSQLが生成されます。

INSERT INTO PET (ID, NAME, TYPE, PET_OWN_ID) VALUES (100, 'Fluffy', 'Cat', NULL)

例114-1の方法をお薦めします。この方法ではクローンの使用が習慣となり、将来的なコードの変更にもきわめて柔軟に対応できるためです。新規オブジェクトとクローンを組み合せて使用すると、混乱と意図しない結果が生じる原因となります。

114.3 オブジェクトの変更

例114-3Petは、作業ユニットを取得する前の読取りです。変数petは、Petのキャッシュ・コピー・クローンです。作業ユニット内では、キャッシュ・コピーを登録して作業コピー・クローンを取得します。その後、作業コピー・クローンを変更し、作業ユニットをコミットします。

例114-3 オブジェクトの変更

// Read in any pet
Pet pet = (Pet)session.readObject(Pet.class);
UnitOfWork uow = session.acquireUnitOfWork();
Pet petClone = (Pet) uow.registerObject(pet);
petClone.setName("Furry");
uow.commit();

例114-4は、作業ユニットを通じて問合せを行ってクローンを取得できることを利用して、登録手順を不要にする方法を示します。ただし、この場合の短所は、キャッシュ・コピー・クローンへのハンドルがないことです。

トランザクションのコミット後に更新されたPetでなんらかの操作を行うには、セッションに対して問合せを行い、Petを取得する必要があります(作業ユニットがコミットされると、クローンは無効になるため使用できないことを忘れないでください)。

例114-4 オブジェクトの変更: 登録手順のスキップ

UnitOfWork uow = session.acquireUnitOfWork();
Pet petClone = (Pet) uow.readObject(Pet.class);
petClone.setName("Furry");
uow.commit();

どちらの方法でも、次のSQLが生成されます。

UPDATE PET SET NAME = 'Furry' WHERE (ID = 100)

作業ユニットを通じて問合せを行う場合は注意してください。問合せで読み取られたオブジェクトはすべて作業ユニットに登録されます。そのため、コミット時には変更が加えられたかどうかがチェックされます。作業ユニットを通じてReadAllQueryを実行するのではなく、セッションを通じてReadAllQueryを実行し、変更の必要のあるオブジェクトのみを作業ユニットに登録するようにアプリケーションを設計する方が、優れたパフォーマンスが得られます。

114.4 既存のソース・オブジェクトへの新規ターゲットの関連付け

この項では、新規ターゲットを既存のソース・オブジェクトに関連付ける方法について説明します。内容は次のとおりです。

どちらの方法を使用するかは、作業ユニットのコミット後にコードが新規オブジェクトのキャッシュ・コピー・クローンの参照を必要とするかどうか、またコードをどの程度柔軟に変更できるようにするかに応じて決めてください。


注意:

UnitOfWorkregisterObjectregisterNewObjectまたはregisterExistingObjectメソッドは、集約オブジェクトに対して使用できません(22.2.1.2項「リレーショナル集約ディスクリプタの作成」を参照)。これをすると、コミット時にValidationExceptionまたはその他のエラーが発生します。詳細は、115.1.4項「集約の使用方法」を参照してください。

114.4.1 単方向リレーションシップで既存のソース・オブジェクトに新規ターゲットを関連付ける方法: コミット後の新規キャッシュ・オブジェクトの参照が不要な場合

例114-5は、キャッシュ・オブジェクトの参照を保持せずに、単方向リレーションシップで新規ターゲットを既存のソースに関連付ける方法を示します。

作業ユニットを使用してPetオブジェクトが読み取られると、TopLinkによって自動的に登録されます。Petオブジェクトと、新規のPetOwnerおよびVetVisitオブジェクトの間には単方向リレーションシップがあるため、新規のPetOwnerまたはVetVisitオブジェクトを登録する必要がありません。TopLinkでは登録済のPetオブジェクトを通じてこのような新規のオブジェクトにアクセスでき、新規のオブジェクトであることを自動的に検出します。

例114-5 キャッシュ・オブジェクトの参照を伴わない関連付け

UnitOfWork uow = session.acquireUnitOfWork();
Pet petClone = (Pet)uow.readObject(Pet.class);

PetOwner petOwner = new PetOwner();
petOwner.setId(400);
petOwner.setName("Donald Smith");
petOwner.setPhoneNumber("555-1212");

VetVisit vetVisit = new VetVisit();
vetVisit.setId(500);
vetVisit.setNotes("Pet was shedding a lot.");
vetVisit.setSymptoms("Pet in good health.");
vetVisit.setPet(petClone);

petClone.setPetOwner(petOwner);
petClone.getVetVisits().add(vetVisit);
uow.commit();

これにより、次の適切なSQLが実行されます。

INSERT INTO PETOWNER (ID, NAME, PHN_NBR) VALUES (400, 'Donald Smith', '555-1212')
UPDATE PET SET PET_OWN_ID = 400 WHERE (ID = 100)
INSERT INTO VETVISIT (ID, NOTES, SYMPTOMS, PET_ID) VALUES (500, 'Pet was shedding a lot.', 'Pet in good health.', 100)

新規オブジェクトを既存のオブジェクトに関連付けると、作業ユニットは新規オブジェクトをクローンと同様に扱います。つまり、トランザクションのコミット後は、次のコードが成立します。

petOwner != session.readObject(petOwner)

したがって、作業ユニットのトランザクションのコミット後、変数vetVisitおよびpetOwnerは、それぞれのキャッシュ・オブジェクトではなく、作業コピーのクローンを指すようになります。

作業ユニットのトランザクションのコミット後にキャッシュ・オブジェクトが必要な場合は、それを問い合せるか、(114.4.2項「単方向リレーションシップで既存のソース・オブジェクトに新規ターゲットを関連付ける方法: コミット後の新規キャッシュ・オブジェクトの参照が必要な場合」で説明するように)キャッシュ・オブジェクトの参照を伴う関連付けを作成する必要があります。

ソース・オブジェクトとターゲット・オブジェクトの間に双方向リレーションシップがある場合、その登録にはさらに注意が必要です(114.4.3項「双方向リレーションシップで既存のソース・オブジェクトに新規ターゲットを関連付ける方法: コミット前のターゲットへの問合せが不要な場合」を参照)。

詳細は、115.1項「オブジェクトの登録と登録解除」を参照してください。


注意:

UnitOfWorkregisterObjectregisterNewObjectまたはregisterExistingObjectメソッドは、集約オブジェクトに対して使用できません(22.2.1.2項「リレーショナル集約ディスクリプタの作成」を参照)。これをすると、コミット時にValidationExceptionまたはその他のエラーが発生します。詳細は、115.1.4項「集約の使用方法」を参照してください。

114.4.2 単方向リレーションシップで既存のソース・オブジェクトに新規ターゲットを関連付ける方法: コミット後の新規キャッシュ・オブジェクトの参照が必要な場合

例114-6は、キャッシュ・オブジェクトの参照を保持して、単方向リレーションシップで新規ターゲットを既存のソースに関連付ける方法を示します。

作業ユニットを使用してPetオブジェクトが読み取られると、TopLinkによって自動的に登録されます。Petオブジェクトと、新規のPetOwnerおよびVetVisitオブジェクトの間には単方向リレーションシップがあるため、新規のPetOwnerまたはVetVisitオブジェクトを登録する必要がありません。TopLinkでは登録済のPetオブジェクトを通じてこのような新規のオブジェクトにアクセスでき、新規のオブジェクトであることを自動的に検出します。

ただし、UnitOfWorkのメソッドregisterObjectを使用すると、コミット後もコードで使用し続ける必要がある場合(たとえば、GUIでの新しいコンテンツの表示)にコミット後のキャッシュ・オブジェクトに対するハンドルを保持できます。

ソース・オブジェクトとターゲット・オブジェクトの間に双方向リレーションシップがある場合、その登録にはさらに注意が必要です(114.4.4項「双方向リレーションシップで既存のソース・オブジェクトに新規ターゲットを関連付ける方法: コミット前のターゲット・オブジェクトへの問合せが必要な場合」を参照)。

例114-6 キャッシュ・オブジェクトの参照を伴う関連付け

UnitOfWork uow = session.acquireUnitOfWork();
Pet petClone = (Pet)uow.readObject(Pet.class);

PetOwner petOwner = new PetOwner();
PetOwner petOwnerClone = (PetOwner)uow.registerObject(petOwner);
petOwnerClone.setId(400);
petOwnerClone.setName("Donald Smith");
petOwnerClone.setPhoneNumber("555-1212");

VetVisit vetVisit = new VetVisit();
VetVisit vetVisitClone = (VetVisit)uow.registerObject(vetVisit);
vetVisitClone.setId(500);
vetVisitClone.setNotes("Pet was shedding a lot.");
vetVisitClone.setSymptoms("Pet in good health.");
vetVisitClone.setPet(petClone);

petClone.setPetOwner(petOwnerClone);
petClone.getVetVisits().add(vetVisitClone);
uow.commit();

ここで、作業ユニットのトランザクションのコミット後は、次のコードが成立します。

petOwner == session.readObject(petOwner)

つまり、トランザクションのコミット後にクローンではなくキャッシュ・コピーへのハンドルを取得できます。

詳細は、115.1項「オブジェクトの登録と登録解除」を参照してください。


注意:

UnitOfWorkregisterObjectregisterNewObjectまたはregisterExistingObjectメソッドは、集約オブジェクトに対して使用できません(22.2.1.2項「リレーショナル集約ディスクリプタの作成」を参照)。これをすると、コミット時にValidationExceptionまたはその他のエラーが発生します。詳細は、115.1.4項「集約の使用方法」を参照してください。

114.4.3 双方向リレーションシップで既存のソース・オブジェクトに新規ターゲットを関連付ける方法: コミット前のターゲットへの問合せが不要な場合

実装済のEmployeeクラスを考えてみます(例114-7を参照)。setManagerメソッドによって、渡したEmployeeインスタンスが変更されます。

例114-7 Employeeクラス

public class Employee {

    private Collection managedEmployees = new ArrayList();
    private Emplyoee myManager;

    ...

    public setManager(Employee manager) {
        myManager = manager;
        manager.addManagedEmployee(this);
    }

    public addManagedEmployee(Employee employee) {
        managedEmployees.add(employee);
    }

    ...

}

例114-8は、マネージャと従業員の間のような双方向リレーションシップが存在するときに、新規オブジェクトを登録する方法を示します。

EmployeeのメソッドsetManagerは渡されるEmployeeを変更するため(例114-7を参照)、registerObjectによって返されるmanagerCloneに渡す必要があります。

setManagerをコールした後、newEmployeemanagerCloneの間に双方向リレーションシップを確立します。newEmployeeは、作業ユニットに登録済のmanagerオブジェクトから到達可能であるため、TopLinkではそれが新規オブジェクトであることを自動的に検出します。このため、newEmployeeを登録する必要がなく、この場合は、事実上newEmployeeに対するregisterObjectコール・エラーです。

コードがコミット前に新規子オブジェクトに対して問合せできる必要がある場合は、114.4.4項「双方向リレーションシップで既存のソース・オブジェクトに新規ターゲットを関連付ける方法: コミット前のターゲット・オブジェクトへの問合せが必要な場合」を参照してください。

作業ユニットのトランザクションのコミット後にキャッシュ・オブジェクトが必要な場合は、問合せを行う必要があります。

例114-8 新規オブジェクトを追加する際の問題の解決

// Get an employee read from the parent session of the unit of work
Employee manager = (Employee)session.readObject(Employee.class);

// Acquire a unit of work
UnitOfWork uow = session.acquireUnitOfWork();

// Register the manager to get its clone
Employee managerClone = (Employee)uow.registerObject(manager);

// Create a new employee
Employee newEmployee = new Employee();
newEmployee.setFirstName("Spike");
newEmployee.setLastName("Robertson");

/* INCORRECT: Do not associate the new employee with the original manager.
 This will cause a QueryException when TopLink detects this error 
during  commit */
//newEmployee.setManager(manager);

/* CORRECT: Associate the new object with the clone. Note that in this example, 
the setManager method is maintaining the bidirectional managedEmployees relationship and adding 
the new employee to its managedEmployees. At commit time, the unit of work will detect that 
this is a new object and will take the appropriate action */
newEmployee.setManager(managerClone);

/* INCORRECT: Do not register the newEmployee: this will create two 
copies and cause a QueryException when TopLink detects this error during commit */
//uow.registerObject(newEmployee);

// Commit the unit of work
uow.commit();

詳細は、115.1項「オブジェクトの登録と登録解除」を参照してください。


注意:

UnitOfWorkregisterObjectregisterNewObjectまたはregisterExistingObjectメソッドは、集約オブジェクトに対して使用できません(22.2.1.2項「リレーショナル集約ディスクリプタの作成」を参照)。これをすると、コミット時にValidationExceptionまたはその他のエラーが発生します。詳細は、115.1.4項「集約の使用方法」を参照してください。

114.4.4 双方向リレーションシップで既存のソース・オブジェクトに新規ターゲットを関連付ける方法: コミット前のターゲット・オブジェクトへの問合せが必要な場合

実装済のEmployeeクラスを考えてみます(例114-7を参照)。setManagerメソッドによって、渡したEmployeeインスタンスが変更されます。

例114-9 Employeeクラス

public class Employee
{
    private Collection managedEmployees = new ArrayList();
    private Emplyoee myManager;

    ...

    public setManager(Employee manager)
    {
        myManager = manager;
        manager.addManagedEmployee(this);
    }

    public addManagedEmployee(Employee employee)
    {
        managedEmployees.add(employee);
    }

    ...

}

例114-8は、マネージャと従業員の間のような双方向リレーションシップが存在するときに、新規オブジェクトを登録する方法を示します。

例114-10 新規オブジェクトを追加する際の問題の解決

// Get an employee read from the parent session of the unit of work
Employee manager = (Employee)session.readObject(Employee.class);

// Acquire a unit of work
UnitOfWork uow = session.acquireUnitOfWork();

// Register the manager to get its clone
Employee managerClone = (Employee)uow.registerObject(manager);

// Create a new employee
Employee newEmployee = new Employee();
newEmployee.setFirstName("Spike");
newEmployee.setLastName("Robertson");

/* INCORRECT: Do not associate the new employee with the original manager. 
This will cause a QueryException when TopLink detects this error during  
commit */
//newEmployee.setManager(manager);

/* CORRECT: Associate the new object with the clone. Note that in this example, 
the setManager method is maintaining the bidirectional managedEmployees relationship and adding 
the new employee to its managedEmployees. At commit time, the unit of work will detect that 
this is a new object and will take the appropriate action */
newEmployee.setManager(managerClone);

/* INCORRECT: Do not register the newEmployee: this will create two 
copies and cause a QueryException when TopLink detects this error during commit */
//uow.registerObject(newEmployee);

/* CORRECT: In the above setManager call, 
if the managerClone's managedEmployees was not maintained by 
the setManager method, then you should call registerObject before the new employee is 
related to the manager. If in doubt, you could use the  registerNewObject method to 
ensure that the newEmployee is registered in the unit of work. The registerNewObject 
method registers the object, but does not make a clone */
uow.registerNewObject(newEmployee);

// Commit the unit of work
uow.commit();

EmployeeのメソッドsetManagerは渡されるEmployeeを変更するため(例114-7を参照)、registerObjectによって返されるmanagerCloneに渡す必要があります。

setManagerをコールした後、newEmployeemanagerCloneの間に双方向リレーションシップを確立します。newEmployeeは、作業ユニットに登録済のmanagerオブジェクトから到達可能であるため、TopLinkではそれが新規オブジェクトであることを自動的に検出します。このため、newEmployeeを登録する必要がなく、この場合は、事実上newEmployeeに対するregisterObjectコール・エラーです。

コードがコミット前に新規子オブジェクトに対して問合せできる必要がある場合は、UnitOfWorkのメソッドregisterNewObjectを使用して新規オブジェクトを登録します。registerObjectとは異なり、このメソッドではクローンは作成されません。

registerNewObjectregisterObjectの別の相違点は、registerNewObjectでは子オブジェクトに登録をカスケードしないことです。親オブジェクトでregisterNewObjectをコールする場合、コードがコミット前に新規子オブジェクトに対して問合せできる必要があり、一致する問合せを使用しないときは、新規の子インスタンスでもregisterNewObjectをコールする必要があります。

作業ユニットのトランザクションのコミット後にキャッシュ・オブジェクトが必要な場合は、問合せを行う必要があります。

詳細は、115.1項「オブジェクトの登録と登録解除」を参照してください。


注意:

UnitOfWorkregisterObjectregisterNewObjectまたはregisterExistingObjectメソッドは、集約オブジェクトに対して使用できません(22.2.1.2項「リレーショナル集約ディスクリプタの作成」を参照)。これをすると、コミット時にValidationExceptionまたはその他のエラーが発生します。詳細は、115.1.4項「集約の使用方法」を参照してください。

114.5 既存のターゲット・オブジェクトへの新規ソースの関連付け

この項では、1対多および1対1リレーションシップによって新規ソース・オブジェクトを既存のターゲット・オブジェクトに関連付ける方法を説明します。

TopLinkでは、登録されている全オブジェクトの全リレーションシップを作業ユニット内で深くまで追跡し、どれが新しく、どれが変更されているかを計算します。これは、到達可能性による永続性と呼ばれます。114.4項「既存のソース・オブジェクトへの新規ターゲットの関連付け」では、新規ターゲットを既存のソースに関連付ける際に、オブジェクトを登録するかどうかを選択できると説明しました。新規オブジェクトを登録しない場合、そのオブジェクトは依然としてソース・オブジェクト(クローンであるため、登録されている)から到達可能です。ただし、新規ソース・オブジェクトを既存のターゲットに関連付ける必要がある場合は、必ず新規オブジェクトを登録してください。新規オブジェクトを登録しないと、作業ユニット内でオブジェクトに到達できないため、そのオブジェクトはデータベースに書き込まれません。

たとえば、例114-11のコードは、新しいPetを作成し、それを既存のPetOwnerに関連付ける方法を示します。

例114-11 既存のターゲット・オブジェクトへの新規ソースの関連付け

UnitOfWork uow = session.acquireUnitOfWork();
PetOwner existingPetOwnerClone =
        (PetOwner)uow.readObject(PetOwner.class);

Pet newPet = new Pet();
Pet newPetClone = (Pet)uow.registerObject(newPet);
newPetClone.setId(900);
newPetClone.setType("Lizzard");
newPetClone.setName("Larry");
newPetClone.setPetOwner(existingPetOwnerClone);
uow.commit();

これにより、次の適切なSQLが生成されます。

INSERT INTO PET (ID, NAME, TYPE, PET_OWN_ID) VALUES (900, 'Larry', 'Lizzard', 400)

この場合、新規オブジェクトを登録して、新規オブジェクトの作業コピーを使用する必要があります。新規オブジェクトを登録せずにPetOwnerクローンに関連付けた場合、そのオブジェクトはデータベースに書き込まれません。


注意:

UnitOfWorkregisterObjectregisterNewObjectまたはregisterExistingObjectメソッドは、集約オブジェクトに対して使用できません(22.2.1.2項「リレーショナル集約ディスクリプタの作成」を参照)。これをすると、コミット時にValidationExceptionまたはその他のエラーが発生します。詳細は、115.1.4項「集約の使用方法」を参照してください。

クローンを登録せずに、誤って既存のオブジェクトのキャッシュ・バージョンを新規オブジェクトに関連付けると、(親セッションの)オブジェクトのキャッシュ・バージョンをこの作業ユニットのクローンに関連付けたというエラーが生成されます。作業ユニットでは、必ず作業コピーを使用してください。

詳細は、次を参照してください。

114.6 既存のターゲット・オブジェクトへの既存のソースの関連付け

この項では、1対多および1対1リレーションシップによって既存のソース・オブジェクトを既存のターゲット・オブジェクトに関連付ける方法を説明します。

例114-12に示すように、作業ユニット内で既存のオブジェクトを相互に関連付けるのは、Javaでオブジェクトを関連付ける場合と同様に単純です。必ずオブジェクトの作業コピーのみを使用してください。

例114-12 既存のターゲット・オブジェクトへの既存のソースの関連付け

// Associate all VetVisits in the database to a Pet from the database
UnitOfWork uow = session.acquireUnitOfWork();
Pet existingPetClone = (Pet)uow.readObject(Pet.class);
List allVetVisitClones;
allVetVisitClones = uow.readAllObjects(VetVisit.class);
Iterator iter = allVetVisitClones.elements();
while(iter.hasNext()) {
    VetVisit vetVisitClone =(VetVisit)iter.next();
    existingPetClone.getVetVisits().add(vetVisitClone);
    vetVisitClone.setPet(existingPetClone);
};
uow.commit();

既存のオブジェクトを関連付ける際の最も一般的なエラーは、作業コピーを使用しないことです。誤ってオブジェクトのキャッシュ・バージョンを作業コピーに関連付けると、コミット時に、親セッションのオブジェクト(キャッシュ・バージョン)をこの作業ユニットのクローンに関連付けたというエラーが発生します。

例114-13は、既存のソースを既存のターゲット・オブジェクトに関連付けるもう1つの例を示します。

例114-13 既存のオブジェクトの関連付け

// Get an employee read from the parent session of the unit of work
Employee employee = (Employee)session.readObject(Employee.class)

// Acquire a unit of work
UnitOfWork uow = session.acquireUnitOfWork();
Project project = (Project) uow.readObject(Project.class);

/* When associating an existing object (read from the session) with a clone, 
we must make sure we register the existing object and assign its clone into a unit of work */

/* INCORRECT: Cannot associate an existing object with a unit of work clone. 
A QueryException will be thrown */
//project.setTeamLeader(employee);

/* CORRECT: Instead register the existing object then associate the clone */
Employee employeeClone = (Employee)uow.registerObject(employee);
project.setTeamLeader(employeeClone);
uow.commit();

詳細は、次を参照してください。

114.7 オブジェクトの削除

作業ユニット内のオブジェクトを削除するには、deleteObjectまたはdeleteAllObjectsメソッドを使用します。まだ作業ユニットに登録されていないオブジェクトを削除する場合、そのオブジェクトは作業ユニットに自動的に登録されます。

オブジェクトを削除すると、そのオブジェクトに私有された子の部分も削除されます。これは、私有の子の部分が所有側(親)のオブジェクトなしでは存在できないためです。コミット時には、作業ユニットがデータベース制約を考慮して、オブジェクトを削除するためのSQLを生成します。

オブジェクトを削除する際には、オブジェクト・モデルを考慮に入れる必要があります。削除されるオブジェクトの参照をnullに設定することが必要な場合もあります(たとえば、114.7.1項「privateOwnedRelationship属性の使用方法」を参照)。

この項では、次のように作業ユニット内でのオブジェクトの削除方法について説明します。

114.7.1 privateOwnedRelationship属性の使用方法

リレーショナル・データベースは、JVMのようなガベージ・コレクション機能を備えていません。Javaでオブジェクトを削除するには、オブジェクトの参照を削除するだけです。リレーショナル・データベースで行を削除するには、行を明示的に削除する必要があります。リレーショナル・データベースのデータを削除するタイミングを手間をかけて管理するかわりに、マッピング属性privateOwnedRelationshipを使用して、リレーショナル・データベースでのガベージ・コレクションをTopLinkで自動的に管理します。

例114-14に示すように、Javaを使用してマッピングを作成する際には、privateOwnedRelationshipメソッドを使用して、参照オブジェクトが私有されている(つまり、参照される子オブジェクトは親オブジェクトなしでは存在できない)ことをTopLinkに指示します。

例114-14 マッピングを私有として指定

OneToOneMapping petOwnerMapping = new OneToOneMapping();
petOwnerMapping.setAttributeName("petOwner");
petOwnerMapping.setReferenceClass(com.top.uowprimer.model.PetOwner.class);
petOwnerMapping.privateOwnedRelationship();
petOwnerMapping.addForeignKeyFieldName("PET.PET_OWN_ID", "PETOWNER.ID");
descriptor.addMapping(petOwnerMapping);

TopLink Workbenchを使用してマッピングを作成する場合、「一般」タブの「私有」チェック・ボックスを選択できます。

リレーションシップが私有されていることをTopLinkに指示する場合、次のことを指示していることになります。

  • プライベートに所有されたリレーションシップのソースが削除されている場合は、ターゲットを削除します。

  • ソースからのターゲットの参照を削除する場合は、ターゲットを削除します。

共有される可能性のあるオブジェクトへの私有されたリレーションシップは構成しないでください。オブジェクトがプライベートに所有されたリレーションシップでターゲットになっている場合は、そのオブジェクトを複数のリレーションシップでターゲットにしないでください。

このルールの例外は、リレーション・オブジェクトがリレーション表にマップされ、1対多リレーションシップを通じてソースとターゲットの両方によって参照されている、多対多リレーションシップが存在する場合です。この場合、1対多マッピングが私有として構成されていれば、ソースを削除したときに関連オブジェクトがすべて削除されます。

例114-15について考えてみます。

例114-15 私有のリレーションシップ

// If the Pet-PetOwner relationship is privateOwned
// then the PetOwner will be deleted at uow.commit()
// otherwise, just the foreign key from PET to PETOWNER will
// be set to null.  The same is true for VetVisit
UnitOfWork uow = session.acquireUnitOfWork();
Pet petClone = (Pet)uow.readObject(Pet.class);
petClone.setPetOwner(null);
VetVisit vvClone =
        (VetVisit)petClone.getVetVisits().get(0);
vvClone.setPet(null);
petClone.getVetVisits().remove(vvClone);
uow.commit();

PetからPetOwnerへ、およびPetからVetVisitへのリレーションシップがプライベートに所有されていない場合、このコードにより次のSQLが生成されます。

UPDATE PET SET PET_OWN_ID = NULL WHERE (ID = 150)
UPDATE VETVISIT SET PET_ID = NULL WHERE (ID = 350)

リレーションシップがプライベートに所有されている場合、このコードにより次のSQLが生成されます。

UPDATE PET SET PET_OWN_ID = NULL WHERE (ID = 150)
UPDATE VETVISIT SET PET_ID = NULL WHERE (ID = 350)
DELETE FROM VETVISIT WHERE (ID = 350)
DELETE FROM PETOWNER WHERE (ID = 250)

114.7.2 データベースからの明示的な削除方法

私有のリレーションシップを通じてガベージ・コレクションが行われないオブジェクト(特に、オブジェクト・モデルのルート・オブジェクト)がある場合、例114-16に示すように、deleteObject APIを使用してそのオブジェクトを表す行を削除するよう、TopLinkに明示的に指示できます。

例114-16 明示的な削除

UnitOfWork uow = session.acquireUnitOfWork();
pet petClone = (Pet)uow.readObject(Pet.class);
uow.deleteObject(petClone);
uow.commit();

このコードでは、次のSQLが生成されます。

DELETE FROM PET WHERE (ID = 100)

114.7.3 オブジェクトの削除順序について

作業ユニットは、変更内容や操作の順序を追跡しません。これは、データベースが求める順序でオブジェクトを変更する必要をなくすためです。

デフォルトでは、作業ユニットはスキーマで定義された制約を使用して、コミット時にすべての挿入操作と更新操作を正しく並べ替えます。挿入操作と更新操作がすべて完了したら、必要な削除操作を発行します。

制約は、1対1マッピングと1対多マッピングから推論されます。そのようなマッピングがない場合は、115.10項「削除操作順序の操作」で説明するように、TopLinkに制約情報を追加できます。