QueryExecuter

ModelクラスはQueryExecuterクラスを継承しています。(マジックメソッドを用いた委譲を行っています。)

よいModelを作るために、QueryExecuterについて学びましょう。

目次

QueryExecuterの生成

customersテーブルのQueryExecuterを生成する

$qe = DB::queryExecuter('customers');

Note: 戻り値のオブジェクトはテーブルごとにシングルトンなオブジェクトです。

先頭レコードの取得

指定されたインデックスの順にみて、先頭のレコードを取得します。インデックスを指定しなかった場合はprimary keyが使用されます。

primary keyを使用し、例外を発生させない

$customer = $qe->first();
$id = $customer->id;  // $customerはstdClassオブジェクト

$throwException = trueを指定した場合、レコードが見つからなかったときに例外を発生させます。

インデックス番号1を使用し、見つからなかった場合に例外を発生させる

try {
    $customer = $qe->index(1)->first(true);
} catch(\Exception $e) {
    // (省略)
}

全レコードを取得

customerテーブルの全レコードを取得

$customers = $qe->all(); // $customersはstdClassオブジェクトのCollection

複数のオブジェクトが返るメソッドは、Collectionオブジェクトを返します。

ユニークキーでのレコードの取得

findfirstメソッドはユニークなインデックスを使って高速な検索を行います。インデックス番号を指定しなかった場合は、primary keyが使用されます。メソッドのパラメータにはキーの値を指定します。

primary keyでid = 3のレコードを取得

$customer = $qe->find(3);

インデックス番号1を指定しname = 'John'のレコードを取得

$nameKeyNum = 1;
$john = $qe->index($nameKeyNum)->find('John');

マルチセグメントキーを使用しレコードを取得

$multiSegKeyNum = 2;
$customer = $qe->index($multiSegKeyNum)->find([3, '1567-900-00']);

複数のレコードを一度に取得

primary keyでid = 3id = 4id = 6のレコードを取得します。

$customers = $qe->findMany([3, 4, 6]);

マルチセグメントキーを使用し複数のレコードを一度に取得

$customers = $qe->index(2)->findMany([[3, '1567-900-00'],[9, '1467-700-01']]);

フィールドの選択

それぞれをパラメータで指定

$customers = $qe->select('id', 'name', 'note')->all();

配列で指定

$customers = $qe->select(['id', 'name', 'note'])->all();

フィールドを選択しなかった場合は、すべてのフィールドが読み取られます。

クエリの条件式

条件式のメソッドはメソッドチェーンできます。最後にgetまたはallメソッドを呼び出して実行します。実行結果は常にCollectionオブジェクトです。

条件式の基礎

これから説明する条件式は、SQLによく似ていますがSQLではありません。はじめにTransactdならではの点について説明します。

インデックスの指定

indexメソッドでインデックスの番号を指定します。省略した場合は自動でprimary keyが使用されます。

$groupKeyNum = 1;
$qe->index($groupKeyNum);

インデックスフィールドの値の指定

インデックスフィールドの値はkeyValueメソッドで指定します。keyValueのパラメータは8個まで指定可能で、マルチセグメントのキーにも対応します。

レコードの検索はこの値で指定したレコードから開始されます。

$qe->keyValue(1);

この値で指定したレコードが無い場合は、その値の直後のレコードが開始レコードになります。

リジェクトカウントの指定

条件式にindexで指定したインデックスフィールドが含まれ、それに続く条件がorでない場合は、インデックスフィールドの値が条件を満たさないレコードが見つかると、自動で検索を終了します。

また、条件にマッチしないレコード数がリジェクトカウントに達した場合も、検索が中止されます。リジェクトカウントを指定しないとデフォルトは1です。

以下の例は、name'akio'でないレコードが10レコードに達すると検索を中止し、見つかったレコードを返します。

$customers = $qe->index(1)->keyValue('')->where('name', 'akio')->reject(10);

リジェクトカウントによる検索中止を無効にする場合は、noBreakRejectメソッドを使用します。noBreakRejectreject(0xffff)の別名で、リジェクトを無効にするマジックナンバー0xffffをセットします。

Where

primary keyを使用し、name = 'John'のレコードを取得

$customers = $qe->keyValue(0)->where('name', 'John')->noBreakReject()->get();

group >= 3 AND group < 5のレコードを取得

$groupKeyNum = 1;
$customers = $qe->index($groupKeyNum)->keyValue(3)
    ->where('group', '>=', 3)
    ->where('group', '<', 5)->reject(1)->get();

WhereNull

note = nullのレコードを取得

$customers = $qe->keyValue(0)->whereNull('note')->noBreakReject()->get();

WhereNotNull

note != nullのレコードを取得

$customers = $qe->keyValue(0)->whereNotNull('note')->noBreakReject()->get();

OrWhere

primary keyを使用し、name = 'John'またはname = 'Akio'のレコードを取得

$customers = $qe->keyValue(0)->where('name', 'John')
    ->orWhere('name', 'Akio')
    ->noBreakReject()->get();

OrNull

primary keyを使用し、name = 'John'またはname = 'Akio'またはnote = nullのレコードを取得

$customers = $qe->keyValue(0)->where('name', 'John')
    ->orWhere('name', 'Akio')
    ->orNull('note')
    ->noBreakReject()->get();

OrNotNull

primary keyを使用し、group = 3またはnote != nullのレコードを取得

$customers = $qe->keyValue(0)->where('group', 3)
    ->orNotNull('note')
    ->noBreakReject()->get();

WhereColumn

xxColumnはフィールド同士の値を比較します。

note = commentのレコードを取得

$customers = $qe->keyValue(0)->whereColumn('note', '=', 'comment')->noBreakReject()->get();

OrColumn

name = 'John'またはnote = commentのレコードを取得

$customers = $qe->keyValue(0)
    ->where('name', 'John')
    ->orColumn('note', '=', 'comment')->noBreakReject()->get();

WhereIn

この条件は内部的にwhereもしくはorWhereに変換されます。従って、keyValuerejectを指定する必要があります。

primary keyを使用し、id = 5のレコードを取得

$customers = $qe->keyValue(5)->whereIn('id', [5])->reject(1)->get();

primary keyを使用し、id in 5,7,10,11のレコードを取得

$customers = $qe->keyValue(5)->whereIn('id', [5,7,10,11])->reject(10)->get();

WhereNotIn

この条件は内部的にwhereもしくはorWhereに変換されます。従って、keyValuerejectを指定する必要があります。

primary keyを使用し、id not in 5,7,10,11のレコードを取得

$customers = $qe->keyValue(5)->whereNotIn('id', [5,7,10,11])->noBreakReject()->get();

WhereBetween

primary keyを使用し、id = 5 to 11のレコードを取得

$customer = $qe->keyValue(5)->whereBetween('id', [5,11])->get();

条件のフィールドがキーフィールドだった場合、rejectは必要ありません。条件のフィールドがキーフィールドではない場合は、noBreakReject()が必要になります。

WhereNotBetween

primary keyを使用し、not (id = 5 to 11)のレコードを取得

$customers = $qe->keyValue(0)->whereNotBetween('id', [5,11])->noBreakReject()->get();

WhereInKey

インデックスを使用してレコードを取得します。

groupキーを使用しgroup = 2のレコードを取得

$groupKeynum = 1; // groupキー(ユニークではないキー)のインデックス
$customers = $qe->index($groupKeynum)->whereInKey([2])->get();

マルチセグメントキーの一部を使用しレコードを取得

以下の例では先頭セグメントが34のレコードを取得しています。

$index  = 2;   // セグメント数2のユニークキーのインデックス
$segments = 1; // レコード取得に使用するセグメントの数
$customers = $qe->index(2)->whereInKey([3, 4], $segments)->get();

Skip

最初の10レコードを除外

$customers = $qe->skip(10)->all();

Take

最初の10レコードだけを取得

$customers = $qe->take(10)->all();

21番目から10レコードだけを取得

$customers = $qe->skip(20)->take(10)->all();

フィールド名のエイリアス

フィールドに別名を指定した場合、クエリ内ではすべて別名を使用してください。

$qg = DB::queryExecuter('groups');
$qg->setAliases(['name' => 'groupName']);

クエリのパラメータを確認する

get()chunk()cursor()update()delete()の代わりにqueryDescription()を使用し、結果を表示します。

queryDescriptionを取得

$params = $qe->keyValue(0)
    ->where('name', 'John')
    ->orColumn('note', '=', 'comment')
    ->noBreakReject()
    ->queryDescription();
echo $params;

結果の例

tablename      : customers
key read       : index = 0: id = 0
key write      : index = 0: id =
conditions     : name >= 'John' or note = [comment],
                 reject = 655361, limit = 0, stopAtLimit = 0

大量のレコードを扱う

Chunk

結果に大量のレコードが含まれる場合は、サーバーへのアクセスを分割することで、メモリを節約することができます。

一度に100行ずつ取得し、合計を計算する

$amount = 0;
$qe->keyValue(0)->where('id' ,'>=', 50)->chunk(100,
    function($records) use (&$amount) {
        foreach($records as $record) {
            $amount += $record->amount;
        }
        return true; // falseを返した場合、処理が中断される
    });

Cursor

結果に大量のレコードが含まれる場合は、カーソルを使用することで、メモリを節約することができます。このメソッドで得られるカーソルは読み取り専用のクライアントカーソルです。

カーソルを使用し合計を計算する

$cr = $qe->keyValue(0)->where('id' ,'>=', 50)->cursor();
$amount = 0;
foreach ($cr as $row) {
    $amount += $row->amount;
}

Join

TransactdのJoinはすべてクライアント側で行われます。異なるデータべースやサーバー間のデータであってもJoinすることができます。

Joinには大きく分けて2種類あります。

それぞれ、Table JoinRecordset Joinと呼びます。

QueryExecuterでは、右側のtableまたはrecordsetjoinメソッドで事前にセットし、読み取りのトリガーとなるメソッドで左側のrecordsetを取得したあと、Joinを行います。

Table Join

Table Joinは、結合先のテーブルのインデックスを使って高速に処理できます。この方法を使用するには以下の制約があります。

テーブルとその関連が正しく設計されていれば、ほとんどのJoinはこの方法で行うことができます。

Table InnerJoin

Table InnerJoinでは、通常のクエリーの条件に加えて以下の2つを指定します。

  1. JoinするテーブルのQueryExecuterrecordset - tabletable側)
  2. 結合に使用するフィールド名

JoinするQueryExecuterindexselectのみ指定します。getの呼び出しは行いません。

グループIDを使用しグループ名をJoinする

// recordset - table のtable側
$qg = DB::queryExecuter('groups');
$qg->setAliases(['name' => 'groupName'])
    ->select('groupName');
    ->index(0);

// recordset - table の recordset側
$qe = DB::queryExecuter('customers');
$customers = $qe->keyValue(1)
    ->select('id', 'group')
    ->where('id', '>=', 1)
    ->where('id', '<=', 10)
    ->join($qg, 'group')
    ->get();
結果
+------+-------+-----------+
|   id | group | groupName |
+------+-------+-----------+
|    1 |     1 | Users     |
|    2 |     2 | Guests    |
|    3 |     2 | Guests    |
|    4 |     3 | Unknowns  |
|    7 |     2 | Guests    |
|    8 |     3 | Unknowns  |
|    9 |     1 | Users     |
|   10 |     1 | Users     |
+------+-------+-----------+

上記の例では、customersテーブルからid = 1-10のレコードを取得し、group列をキーに $qggroupsテーブルのレコードを取得して結合します。キーとなる列名は、複数を配列で指定するも可能です。

Table OuterJoin

Table OuterJoinは、結合する行がない場合にも元の行を削除しないという点以外は、Table InnerJoinと同じです。 joinメソッドをouterJoinに変えて呼び出します。

結合する行がない場合、そのフィールドの値はnullになります。

グループIDを使用しグループ名をOuterJoinする

$qg = DB::queryExecuter('groups');
$qg->setAliases(['name' => 'groupName'])
    ->select('groupName');
    ->index(0);
$qe = DB::queryExecuter('customers');
$customers = $qe->keyValue(1)
    ->select('id', 'group')
    ->where('id', '>=', 1)
    ->where('id', '<=', 10)
    ->outerJoin($qg, 'group')
    ->get();
結果
+------+-------+-----------+
|   id | group | groupName |
+------+-------+-----------+
|    1 |     1 | Users     |
|    2 |     2 | Guests    |
|    3 |     2 | Guests    |
|    4 |     3 | Unknowns  |
|    5 |  NULL | NULL      |
|    6 |  NULL | NULL      |
|    7 |     2 | Guests    |
|    8 |     3 | Unknowns  |
|    9 |     1 | Users     |
|   10 |     1 | Users     |
+------+-------+-----------+

Recordset Join

Recordset JoinはTable Joinに比べてほとんど制約がなく、自由自在に2つのrecordsetをJoinできます。結合条件には、列と列の関係のみ指定可能です。

Recordset InnerJoin

Recordset Joinでは、事前にJoinするデータをrecordset型で取得しておきます。レコードセットの取得にはgetでなくrecordsetメソッドを使用します。

すべてのgroupレコードをrecordset型で取得する

$qg = DB::queryExecuter('groups');
$group_rs = $qg->setAliases(['id' => 'group_id', 'name' => 'groupName'])
    ->select('group_id', 'groupName')
    ->index(0)
    ->recordset(); // すべてのgroupレコード

idが1~10のcustomerと$group_rscustomer.group = groups.group_idでJoinする

$rq = new RecordsetQuery();
$rq->when('group', '=', 'group_id')
$customers = $qe->keyValue(1)
    ->select('id', 'group')
    ->where('id', '>=', 1)
    ->where('id', '<=', 10)
    ->join($group_rs, $rq)
    ->get();
結果
+------+-------+----------+-----------+
|   id | group | group_id | groupName |
+------+-------+----------+-----------+
|    1 |     1 |        1 | Users     |
|    2 |     2 |        2 | Guests    |
|    3 |     2 |        2 | Guests    |
|    4 |     3 |        3 | Unknowns  |
|    7 |     2 |        2 | Guests    |
|    8 |     3 |        3 | Unknowns  |
|    9 |     1 |        1 | Users     |
|   10 |     1 |        1 | Users     |
+------+-------+----------+-----------+

結合条件は、RecordsetQueryで指定します。whenの最初のパラメータに結合元の列名、3番目に結合先の列名を指定します。条件は=だけでなくすべて使用できます。複数の条件の指定も可能です。

また、事前に2つのレコードセットを取得してからJoinすることもできます。この場合はRecordset::joinメソッドを使用します。

事前に取得済みの2つのレコードセット$group_rs$customers_rscustomer.group = groups.group_idでJoinする

$rq = new RecordsetQuery();
$rq->when('group', '=', 'group_id')
$customers_rs->join($group_rs, $rq);
$customers = $customers_rs->toArray(); // recordsetからオブジェクトの配列に変換

この方法だと、事前に不要な行をRecordset::matchByを使って取り除いておくことができます。

recordset::toArrayrecordsetからオブジェクトの配列に変換できます。

Recordset OuterJoin

Recordset OuterJoinは、結合する行がない場合にも元の行を削除しないという点以外は、Recordset InnerJoinと同じです。 joinメソッドをouterJoinに変えて呼び出します。

結合する行がない場合、そのフィールドの値はnullになります。

GroupBy

GroupByの条件はGroupQueryで指定します。

グループごとの人数を数える

$gq = new GroupQuery();
$gq->keyField('group')->addFunction(new Count('memberCount'));
$result = $qe->groupBy($gq)->all();
echo 'group ='. $result[0]->group . ' member count = '. $result[0].memberCount;

OrderBy

グループと名前でソートする

$asc = true;
$customers = $qe
    ->orderBy('group', $asc) // 1番目のソートキー
    ->orderBy('name', $asc)  // 2番目のソートキー
    ->all();

MatchBy

MatchByはローカルでレコードをフィルタリングします。条件はRecordsetQueryで指定します。

データ取得後、ローカルでnote = nullのレコードを残し他を除外する

$rq = new RecordsetQuery();
$rq->whenIsNotNull('note');
$customers = $qe->keyValue(5)
     ->whereBetween('id', [5,11])
     ->matchBy($rq)
     ->get();

Union

id = 1 to 10のレコードセットとid = 31 to 40のレコードセットを結合する

$rs = $qe->keyValue(1)
    ->where('id', '>=', 1)
    ->where('id', '<=', 10)
    ->recordset();
$customers = $qe->keyValue(31)
    ->where('id', '>=', 31)
    ->where('id', '<=', 40)
    ->union($rs)
    ->get();

Calculate

salesテーブルの金額について様々な計算をしてみましょう。

$qa = DB::queryExecuter('sales');

Count

'2016-11'の売上の数を数える

$lines = $qa::index(1)->keyValue('2016-11-01')
    ->where('date', '>=', '2016-11-01')
    ->where('date', '<=', '2016-11-30')
    ->count();

Sum

'2016-11'の売上高を合計する

$amount = $qa::index(1)->keyValue('2016-11-01')
    ->where('date', '>=', '2016-11-01')
    ->where('date', '<=', '2016-11-30')
    ->sum('amount');

Min

'2016-11'の売上高の最小値を取得

$amount = $qa::index(1)->keyValue('2016-11-01')
    ->where('date', '>=', '2016-11-01')
    ->where('date', '<=', '2016-11-30')
    ->min('amount');

Max

'2016-11'の売上高の最大値を取得

$amount = $qa::index(1)->keyValue('2016-11-01')
    ->where('date', '>=', '2016-11-01')
    ->where('date', '<=', '2016-11-30')
    ->max('amount');

Average

'2016-11'の売上高の平均値を取得

$amount = $qa::index(1)->keyValue('2016-11-01')
    ->where('date', '>=', '2016-11-01')
    ->where('date', '<=', '2016-11-30')
    ->average('amount');

スナップショット

スナップショットを使って、複数の読み取りを、同一のトランザクションで保存された一貫性のあるものにします。(一貫性読み取り、CONSISTENT_READ)

DB::beginSnapshot(); // スナップショット開始
$rs = $qe->keyValue(1)
    ->where('id', '>=', 1)
    ->where('id', '<=', 10)
    ->recordset();

$customers = $qe->keyValue(31)
    ->where('id', '>=', 31)
    ->where('id', '<=', 40)
    ->union($rs)
    ->get();
DB::endSnapshot(); // スナップショット終了

データベースアクセスのトリガー

QueryExecuterに対するオペレーションはキューに蓄積され、トリガーとなるメソッドが呼ばれた際に実行され、データベースにアクセスします。トリガーとなるメソッドは以下の通りです。

実行順序

トリガーメソッドが呼ばれた際、キューのオペレーションは蓄積された順に実行されます。

例:

$results = $qe->groupBy(xx)->orderBy(xx)->all();

上記のコードは以下のように実行されます:

$rs = $at->read($q);
$rs->groupBy(xx);
$rs->orderBy(xx);
$results = $rs->toArray();

書き込みオペレーション

Insert

新しいレコードを挿入し、auto-incrementのidを取得します。

$customer = $qe->insert(['id' => 0, 'name' => 'akio']);
echo $customer->id; // 新しいidを表示

Update

条件に合ったレコードを更新します。

id = 100の顧客の名前を'hanako'に更新

$count = $qe->keyValue(100)->update(['name' => 'hanako']);

group = 3の顧客すべてのグループを5に更新

$count = $qe->index(1)->keyValue(3)->where('group', 3)->update(['group' => 5]);

Delete

条件に合ったレコードを削除します。

id = 100の顧客を削除

$count = $qe->keyValue(100)->delete();

group = 5の顧客すべてを削除

$count = $qe->index(1)->keyValue(5)->where('group', 5)->delete();

ServerCursor

複雑な更新や削除などは、サーバーカーソルを使用すると簡単に効率よく処理できます。サーバーカーソルはインデックス順に1レコードずつ移動し、値を判定しながら更新や削除などを行うことができます。

トランザクション中であればカレントレコードはロックされ、安全に更新できます。

カーソルの取得

$op = self::SEEK_EQUAL;
$lockBias = Transactd::LOCK_BIAS_DEFAULT;
$forword = true;
$scr = $qe->index(0)->keyValue(5)->serverCursor($op, $lockBias, $forword);

カーソルの取得には、使用するインデックスとキー値を指定してserverCursorを呼び出します。最初のカーソル位置を決める$opパラメータには以下の値を指定できます:

$lockBiasにはロックオプションを指定します。ロックオプションの詳細はTransactdでのロック制御を参照してください。

カーソルには順方向カーソルと逆方向カーソルがあります。方向は$forwordパラメータで指定します。

SEEK_FIRSTSEEK_LASTでは$forwordパラメータは無視され、それぞれ順方向と逆方向のカーソルが返ります。

カレントの取得と移動

カレントレコードの値はcurrentメソッドで取得します。prevnextで前後のレコードに移動できます。

$scr = $qe->index(0)->keyValue(5)->serverCursor(self::SEEK_EQUAL);
if ($scr->valid()) {                // 移動できたかどうか
    $customer = $scr->current();    // 値の取得
    if ($customer->active) {
        $scr->next();               // 次のレコードに移動
        if ($scr->valid()) {
            $customer = $scr->current();
            // (省略)
            $scr->prev();           // 前のレコードに移動
            // (省略)
        }
    }
}

移動できたかどうかはvalidメソッドで確認します。 validOrFailを使用すると、無効なカーソルの場合にIOExceptionをスローできます。

$scr = $qe->index(0)->keyValue(5)->serverCursor(self::SEEK_EQUAL);
$scr->validOrFail(); // 無効な場合IOExceptionをスローする
$customer = $scr->current();

また、foreachでループもできます。

$scr = $qe->index(0)->keyValue(5)->serverCursor(self::SEEK_EQUAL);
foreach($scr as $customer) {
   if ($customer->id > 10) {
       break;
   }
   // (省略)
}

更新と削除

updatedeleteでカレントレコードの更新や削除が行えます。

更新オペレーションは失敗するとIOExceptionをスローします。

$scr = $qe->index(0)->keyValue(5)->serverCursor(self::SEEK_EQUAL);
foreach($scr as $customer) {
   if ($customer->id > 100) {
       break;
   }
   if (!$customer->active) {
       $scr->delete();          // 現在のレコードを削除
   } elseif ($customer->zip === '390-831') {
       $customer->zip = '390-0831';
       $scr->update($customer); // 現在のレコードを$customerで更新
   }
}

更新時にキー値を変更した場合でも、デフォルトではカレントレコードの位置は変化しません。削除もカレントレコードの位置は変化しません。どちらも継続して処理を行うことができます。

追加

insertでレコードの追加ができます。

追加オペレーションは失敗するとIOExceptionをスローします。

$scr = $qe->index(0)->keyValue(5)->serverCursor(self::SEEK_EQUAL);
if (!$scr->valid()) {
    $customer = new stdClass;
    $customer->name = 'abc';
    $scr->insert($customer); // $customerを追加
}

追加後も、カレントレコードの位置は変化しません。

トランザクションとロック

トランザクション内で、書き込みオペレーションおよびサーバーカーソルで読み取ったレコードは必ずロックされます。ロックはデフォルトで排他ロックです。各オペレーションでのロック指定は不要です。

サーバーカーソルではオペレーションごとに、共有ロックの指定を行うこともできます。

また、トランザクション開始時にロックタイプと開放について指定することができます。

Note: Transactd PHP ORMには、読み取りはスレーブ、書き込みはマスターへ自動で振り分ける機能があります。

トランザクションで読み取りロックされるのはマスターへのアクセスのみです。マスターにアクセスするのは、queryExecuter::getWritableTable()で取得したテーブルへのオペレーションです。具体的にはinsertupdatedelete系のオペレーションとサーバーカーソルです。それ以外のfindgetなどによる読み取りはスレーブアクセスであり、トランザクション中に呼び出してもロックはされません。

ただし、スレーブホストを省略した場合や、スレーブとマスターが同じホストの場合は、読み取りアクセスに使用するテーブルと書き込みアクセスに使用するテーブルが同じであるため、findgetなどによる読み取りでもロックされます。

ロックタイプ

トランザクション時のロックタイプは以下の2種類があります。

row lockは、読み取ったレコードをロックしたい場合に使用します。

next key lockは、読み取ったレコードのロックおよびその直後への挿入のブロックを行いたい場合に使用します。

トランザクション内で、両方必要になる場合は、next key lockを指定しましょう。

ロック開放

ロック開放には以下の2種類があります。

シングルレコードロックは、ロックが保持されるレコードが少ないため同時実行性に優れています。可能であればこの方法を選択しましょう。

なお、シングルレコードロックを選択しても、insertdeleteupdateされたレコードはトランザクションが終了するまでロックが保持されます。

トランザクションの例

シングルレコードロックでトランザクションを使用

try {
    DB::beginTransaction();
    $count = $qe->index(1)->keyValue(5)->where('group', 5)->delete();
    DB::commit();
} catch(\Exception $e) {
    DB::rollBack();
}

マルチレコードロック、ギャップロックでトランザクションを使用

try {
    DB::beginTransaction(Transactd::MULTILOCK_GAP);
    $count = $qe->index(1)->keyValue(5)->where('group', 5)->delete();
    DB::commit();
} catch(\Exception $e) {
    DB::rollBack();
}

トランザクションロックの詳細はInnoDBのロックとその制御を参照してください。

例外

読み取りオペレーションで該当するレコードが見つからなかった場合、例外は発生しません。ただし、xxxOrFailメソッドを使用した場合および引数に$throwException = trueを指定した場合は、例外がスローされます。また、処理でエラーが発生した場合は例外がスローされます。

書き込みオペレーションでは、処理でエラーが発生した場合に例外がスローされます。

レコードをPHPの配列で取得する

1行をオブジェクトではなくPHPのハッシュ配列で取得できます。

$recordset = $qe->keyValue(1)->where('id', '>=', 1)->recordset(); // recordsetで取得

/* フィールド名とフィールド番号をキーにした配列でフェッチするようモードを指定 */
$recordset->fetchMode = transactd::FETCH_VAL_BOTH; 
$records = $recordset->toArray(); // transactd::FETCH_VAL_BOTHで取得

foreach($records as $record) {
    $id = $record['id'];
    // (省略)
}

Transcatdのネイティブオブジェクトを取得する

TransactdのネイティブAPIも簡単に使用できます。

Table

書き込みも可能なtableオブジェクトを取得します。

$table = $qe->table();

ActiveTable

読み取り専用(書き込み不可)なactiveTableオブジェクトを取得します。

$at = $qe->activeTable();

Recordset

get()の代わりにrecordset()を使用します。

$recordset = $qe->keyValue(1)->where('id', '>=', 1)->recordset();

Database

デフォルトのコネクションを使用しmasterのdatabaseオブジェクトを取得

$database = DB::master();

コネクションInternalを使用しslaveのdatabaseオブジェクトを取得

$database = DB::connection('Internal')->slave();