---
title: "ActiveRecord 7.2の変更点｜トランザクションコールバック・attributes_for_inspect・with_connectionを実行して比較"
url: "https://www.issoh.co.jp/tech/details/10104/"
published: 2025-12-03
updated: 2026-10-03
categories: ["Ruby on Rails"]
publisher: "株式会社一創"
---

# ActiveRecord 7.2の変更点｜トランザクションコールバック・attributes\_for\_inspect・with\_connectionを実行して比較

ActiveRecord 7.2（Rails 7.2.0、2024年8月9日公開）では、モデルの外からコミット後の処理を登録するAPI、`inspect`に出す属性の指定、データベース接続の借り方の3点が大きく変わりました。この記事では、同じスクリプトをactiverecord 7.1.6・7.2.4・8.1.3.1で実行した結果をもとに、各変更の挙動と、アップグレード時に壊れやすい箇所を説明します。Railsフレームワーク全体の変更点やサポート期限は[Rails 7.2の主な変更点とサポート終了日](/tech/details/3362/)で扱っています。

## まとめ

- **トランザクション**：`transaction`のブロック引数が`ActiveRecord::Transaction`になり、`after_commit`と`after_rollback`をモデルの外から登録できます。`ActiveRecord.after_all_transactions_commit`は、トランザクションの外で呼ぶと即時に実行され、中で呼ぶと最も外側のコミット後に実行されます。
- **attributes\_for\_inspect**：7.2.0では既定値が`[:id]`でしたが、7.2.2で`:all`に戻りました。現在は新規アプリの`production.rb`が`[:id]`を設定します。`rails console`では基底クラスの設定が`:all`になりますが、モデル単位の上書きは有効です。
- **接続**：`ActiveRecord::Base.connection`は`lease_connection`へ改名されました（警告の出ない非推奨）。短時間だけ接続を借りるなら`with_connection`を使います。
- **クエリ**：`pluck`と`order`がテーブル名をキーにしたハッシュを受け付けるようになりました。7.1.6では同じコードが例外になります。
- **移行の注意**：`serialize :col, JSON`のように第2引数でcoderを渡す書き方は7.2で削除され、`ArgumentError`になります。
- 今回のスクリプトで確認したAPIは8.1.3.1でも同じ挙動でした。8系への移行では、アプリケーション固有の設定や依存gemも別途検証してください。7.2系自体は公式のサポート対象一覧から外れています。

以下、7.1.6との差分が大きい順に見ていきます。

## ActiveRecord 7.2で追加・変更された機能と7.1・8.1での挙動

検証に使ったのはRuby 4.0.6とSQLiteで、activerecordだけを版ごとに分けたgemディレクトリに入れています。7.1.6はsqlite3 gem 1.7.3、7.2.4と8.1.3.1は2.9系を使いました。

| 機能                                     | 7.1.6                       | 7.2.4                       | 8.1.3.1 |
| -------------------------------------- | --------------------------- | --------------------------- | ------- |
| `transaction`のブロック引数                   | `nil`                       | `ActiveRecord::Transaction` | 同左      |
| `current_transaction`                  | 未定義                         | あり                          | 同左      |
| `after_all_transactions_commit`        | 未定義                         | あり                          | 同左      |
| `attributes_for_inspect`               | 未定義                         | あり（既定`:all`）                | 同左      |
| `with_connection` / `lease_connection` | 未定義                         | あり                          | 同左      |
| `pluck(posts: [:id])`                  | `UnknownAttributeReference` | 配列を返す                       | 同左      |
| `order(comments: { id: :desc })`       | `ArgumentError`             | SQLを生成                      | 同左      |
| `in_batches.destroy_all`の戻り値           | `nil`                       | 削除件数                        | 同左      |

8.1.3.1では、版番号・生成日時・オブジェクトIDを除き、検証スクリプトで確認したAPIの出力が7.2.4と一致しました。ただし8.0は実行しておらず、アプリケーション全体の互換性を確かめた結果でもありません。

## トランザクションコールバックの新API：after\_commit・current\_transaction・after\_all\_transactions\_commit

7.1までは、コミット後に通知やジョブを送るには、モデルに`after_commit`を書くしかありませんでした。サービスオブジェクトのようにモデルの外でトランザクションを張るコードでは、処理をどこに書くかが問題でした。7.2では3つの入口が追加されています。

### transactionのブロック引数に渡るActiveRecord::Transaction

```
Post.transaction do |tx|
  tx.after_commit   { NotifyJob.perform_later }   # コミット後に1回
  tx.after_rollback { Rails.logger.warn("rolled back") }
  Post.create!(title: "t")
end
# 7.2.4 の実行結果: yield class=ActiveRecord::Transaction
# コミット時は after_commit だけ、raise ActiveRecord::Rollback 時は after_rollback だけが動く
```

7.1.6で同じブロックを実行すると、引数は`nil`です。`tx.after_commit`は`NoMethodError`になるため、このAPIを使うコードは7.2未満では動きません。

### ネストしたトランザクションで登録したコールバックの発火タイミング

内側のブロックで登録したコールバックも、内側のブロックを抜けた時点では動かず、外側がコミットされた時点で動きます。`requires_new: true`でセーブポイントを切った場合も同じでした。

```
log = []
Post.transaction do
  Post.transaction(requires_new: true) do |inner|
    inner.after_commit { log << "savepoint commit cb" }
  end
  log << "after savepoint released"
end
log # => ["after savepoint released", "savepoint commit cb"]
```

セーブポイントの解放はデータベースへの確定ではありません。内側のブロックを抜けた時点でジョブを投入する設計にすると、外側がロールバックしたときに存在しないレコードを処理するジョブが残ります。7.2のコールバックはこの順序を守っているので、手動で順序を管理する必要はありません。

### after\_all\_transactions\_commitとcurrent\_transactionの使い分け

`ActiveRecord.after_all_transactions_commit`は、トランザクションの中で呼ばれるか外で呼ばれるかがわからないメソッドのために用意されています。CHANGELOGの説明は「Useful for code that may run either inside or outside a transaction」です。

```
log = []
ActiveRecord.after_all_transactions_commit { log << "outside->immediate" }
Post.transaction do
  Post.transaction(requires_new: true) do
    ActiveRecord.after_all_transactions_commit { log << "inner->after outermost" }
  end
  log << "outer body end"
end
log # => ["outside->immediate", "outer body end", "inner->after outermost"]
```

挙動を確かめた結果は次のとおりです。

- トランザクションの外で呼ぶと、ブロックはその場で実行されます。
- 外側が`ActiveRecord::Rollback`で巻き戻ると、ブロックは実行されません。
- 内側のセーブポイントだけが巻き戻り、外側がコミットされた場合も実行されません。

`Post.current_transaction`は、トランザクションの外で呼んでも`nil`を返しません。`open?`が`false`の`ActiveRecord::Transaction`を返し、それに登録した`after_commit`は即時に実行されました。使い分けの基準は、特定の接続のトランザクションを対象にするか、登録時に開いている複数接続のトランザクションすべてを対象にするかです。データベースが1つのアプリでは、検証の範囲ではどちらも同じ順序で動きました。複数データベース構成で、特定のデータベースのコミットだけを待つなら`Post.current_transaction`、関係するすべてのコミットを待つなら`after_all_transactions_commit`を選びます。

Active Jobの`perform_later`をトランザクション内で呼んだときにコミット後まで遅らせる`enqueue_after_transaction_commit`も7.2で入りました。ただし設定値の書き方が8.0・8.1で変わっているため、詳細は[Rails 7.2の主な変更点とサポート終了日](/tech/details/3362/)と[Rails Active Jobの解説記事](/tech/details/3988/)にまとめています。

## attributes\_for\_inspect：7.2.0と7.2.2で変わったinspectの既定値

「7.2にしたら`inspect`がidしか出さなくなった」という報告は、7.2.0と7.2.1に特有の現象です。既定値は7.2の途中で変わっているので、使っているパッチ版を先に確認してください。

### 7.2.0〜7.2.1と7.2.2以降の既定値の違い

| 版           | クラス属性の既定 | 開発・テスト             | 本番                            | rails console     |
| ----------- | -------- | ------------------ | ----------------------------- | ----------------- |
| 7.2.0〜7.2.1 | `[:id]`  | `:all`（railtieが設定） | `[:id]`                       | 環境の設定に従う          |
| 7.2.2〜8.1   | `:all`   | `:all`             | 新規アプリの`production.rb`で`[:id]` | 既定`:all`（モデル設定優先） |

7.2.0の`core.rb`は`class_attribute :attributes_for_inspect, instance_accessor: false, default: [:id]`で、開発環境だけrailtieの`consider_all_requests_local`判定で`:all`に切り替えていました。7.2.2（2024年10月31日公開）の[activerecord CHANGELOG](https://github.com/rails/rails/blob/v7.2.2/activerecord/CHANGELOG.md)には「Set `.attributes_for_inspect` to `:all` by default.」「In the console all the attributes are always shown.」とあり、railtieの`console`ブロックに`ActiveRecord::Base.attributes_for_inspect = :all`が追加されています。

今の構成でidしか出ないのは、本番のログやエラー通知に記録されるときです。新規アプリの`config/environments/production.rb`には次の1行が生成されます。7.2.0以前に作ったアプリにはこの行がないため、7.2.2以降へ上げると本番でも全属性が出るようになります。

```
# config/environments/production.rb（7.2.2以降の生成テンプレート）
# Only use :id for inspections in production.
config.active_record.attributes_for_inspect = [ :id ]

# モデル単位で上書きする場合
class Post < ApplicationRecord
  self.attributes_for_inspect = [:id, :title]
end
Post.first.inspect       # => #<Post id: 1, title: "Hello">
Post.first.full_inspect  # 設定を無視して全属性を返す
```

### 本番でinspectを絞る理由：配列の文字列化で起きる処理時間の差

この機能を実装したShopifyのAndrew Novoselac氏は、[Rails at Scaleの記事](https://railsatscale.com/2024-04-30-fixing-a-footgun-in-activerecord-core-inspect/)で、あるリクエストで`inspect`に約8秒かかっていた事例を紹介しています。原因は、数百件のモデルを含む配列に`to_s`を呼んでいたことでした。配列の`to_s`は`inspect`の別名なので、文字列への埋め込みのように`inspect`を呼んでいるとは見えない書き方でも、要素ごとに全属性が展開されます。`filter_attributes`による秘匿処理も属性ごとに走ります。

配列の文字列化による負荷を調べるため、33列・500件の合成データを用意し、`filter_attributes`に6項目を指定して測定しました。`"#{users}"`をウォームアップ後に5回実行した最短時間は次のとおりです。測定時間は実行環境によって変わります。

| attributes\_for\_inspect | 処理時間    | 生成される文字列 |
| ------------------------ | ------- | -------- |
| `:all`                   | 111.2ms | 350,512字 |
| `[:id]`                  | 3.4ms   | 8,392字   |

例外メッセージやログでモデルが展開される経路は、意外と多くあります。本番は`[:id]`のままにし、調査では`full_inspect`を明示して呼ぶのが安全です。7.2.2より前のアプリを上げる場合は、`production.rb`にこの1行を追加し忘れないでください。

## with\_connectionとlease\_connection：データベース接続の借り方の変更

7.1までの`ActiveRecord::Base.connection`は、接続をプールから取り出すと、リクエストやジョブが終わるまで保持し続けます。スレッドやFiberの数が接続数を上回る構成では、これがプールの枯渇につながっていました。7.2ではこのメソッドが`lease_connection`へ改名され、短時間だけ接続を借りる`with_connection`が追加されています。

```
# トランザクション外で、既存の接続を保持していない状態で実行する
ActiveRecord::Base.connection_handler.clear_active_connections!
pool = ActiveRecord::Base.connection_pool

ActiveRecord::Base.with_connection { |c| c.select_value("SELECT 1") }
pool.active_connection?   # => nil（ブロックを抜けると返却される）

Post.count
pool.active_connection?   # => nil（7.2のモデルのクエリも保持しない）

ActiveRecord::Base.lease_connection
pool.active_connection?   # => SQLite3Adapter（リクエスト終了まで保持）
```

CHANGELOGには、`.connection`の非推奨は「soft deprecation」で警告を出さず、削除の予定もないと書かれています。既存コードを洗い出したい場合は、`config.active_record.permanent_connection_checkout`を使います。7.2.4で切り替えた結果は次のとおりでした。

- `true`（既定）：何も出ません。
- `:deprecated`：`` Called deprecated `ActiveRecord::Base.connection` method. ``の警告が出て、処理は続きます。
- `:disallowed`：同じ文言で`ActiveRecord::ActiveRecordError`が発生します。

まずテスト環境で`:deprecated`にして呼び出し箇所を一覧にし、自前のコードを`with_connection`へ置き換えてから`:disallowed`にする順番が無難です。gemが内部で`.connection`を呼んでいる場合、`:disallowed`にすると本番でエラーになります。依存gemを含む呼び出し箇所の検証が済むまでは、本番で`:disallowed`を有効にしないでください。

## pluckとorderのハッシュ指定：結合先テーブルの列指定と並べ替え

`joins`した先の列を取得・並べ替えるとき、7.1までは`"comments.body"`のような文字列やArel属性で指定していました。7.2では`where`と同じ形のハッシュで書けます。

```
Post.joins(:comments).order("comments.id")
    .pluck(posts: [:id], comments: [:id, :body])
# => [[1, 1, "c1"], [1, 2, "c2"], [2, 3, "c3"]]

Post.joins(:comments).order(comments: { created_at: :desc }, title: :asc).to_sql
# => ... ORDER BY "comments"."created_at" DESC, "posts"."title" ASC
```

7.1.6で同じコードを実行すると、`pluck`は`ActiveRecord::UnknownAttributeReference`（Dangerous query method）、`order`は`ArgumentError: Direction "{created_at: :desc}" is invalid.`になります。`includes`と組み合わせた`order(comments: { created_at: :desc })`は、LEFT OUTER JOINでeager loadするSQLになりました。ハッシュ指定ではテーブル名と列名がクオートされますが、列名のタイプミスをSQL実行前に検出する機能ではありません。

## バッチ処理・関連付け・PostgreSQLまわりの細かな変更

- `in_batches.destroy_all`は削除件数（Integer）を返すようになりました。7.1.6では常に`nil`です。削除件数をログに残す処理を自前で数えていた場合は置き換えられます。
- `in_batches.touch_all`が使えるようになりました。
- `counter_cache: { active: false }`で、値の埋め込みが終わるまでカウンタ列を読まずに済むようになりました。手順は[counter\_cacheの実装手順と後付けの対処](/tech/details/3727/)で扱っています。
- `strict_loading!(mode: :n_plus_one_only)`は子の関連を先読みしなくなり、`person.posts.first.firm`が例外にならなくなりました。N+1の検出方法は[N+1問題の実測と検出方法](/tech/details/4262/)を参照してください。
- PostgreSQLアダプタは、`select_value`などで取得したdate型の値を文字列でなく`Date`で返します。生SQLの結果を文字列として比較していたコードは、7.2で比較結果が変わります。

## 7.1から7.2へ上げるときにActiveRecord側で壊れやすい箇所

アップグレード手順の全体は[Rails 7.2の移行判断の記事](/tech/details/3362/)に任せます。ここでは、ActiveRecordの範囲で実際にエラーや挙動の変化が起きる箇所を挙げます。

- **serializeの第2引数**：`serialize :body, JSON`は7.2.4で`ArgumentError: wrong number of arguments (given 2, expected 1)`になります。`serialize :body, coder: JSON`に書き換えてください。7.1までは非推奨警告だけで動いていたため、起動するまで気づきにくい箇所です。
- **本番のinspect**：7.2.0か7.2.1へ上げると本番のログやエラー通知からid以外の属性が消え、7.2.2以降へ上げると今度は全属性が出ます。エラー通知サービスに個人情報が流れていないかを、上げた直後に確認してください。
- **マイグレーションの版指定**：7.2.4の`Migration::Compatibility::V7_1`は中身のない`class V7_1 < V7_2`です。既存の`Migration[7.1]`を`[7.2]`へ書き換えても生成されるDDLは変わらないので、一括で置換する必要はありません。
- **gemの対応版**：`Base.connection`はそのまま動くため互換性の問題は出にくいものの、`permanent_connection_checkout = :disallowed`を本番に入れるとgem側の呼び出しで落ちます。

7.2系は、2026年10月時点で公式のメンテナンスページの「List of currently supported releases」に載っておらず、載っているのは8.1.xと8.0.xだけです。2026年9月24日に7.2.4（PostgreSQLアダプタの再接続処理の修正など）が出ましたが、[公式のリリース告知](https://rubyonrails.org/2026/9/24/Rails-Version-7-2-4-has-been-released)は「this is the last release of the 7.2.x series」と明記しています。今回のスクリプトで確認したAPIの挙動は8.1.3.1でも一致しましたが、移行時にはアプリケーション全体の回帰テストが必要です。

## よくある質問

### config.active\_record.attributes\_for\_inspectを本番でも全属性にするには？

`config/environments/production.rb`の`config.active_record.attributes_for_inspect = [ :id ]`を削除するか、`:all`に書き換えます。7.2.2以降の`rails console`では基底クラスの設定が`:all`になりますが、モデル単位の上書きは有効です。一時的に全属性を確認したいだけなら、設定は変えずに`record.full_inspect`を呼んでください。

### after\_commitとafter\_all\_transactions\_commitの違いは？

モデルの`after_commit`は、そのレコードを保存したトランザクションのコミットに紐づきます。`ActiveRecord.after_all_transactions_commit`は特定のレコードに紐づかず、開いているトランザクションがすべてコミットされた後に実行されます。トランザクションの外で呼んだ場合は即時に実行され、ロールバックされた場合は実行されません。

### ActiveRecord::Base.connectionは使えなくなりますか？

使えます。7.2のCHANGELOGは、警告を出さない非推奨で削除の予定もないとしており、8.1.3.1でも既定の設定では警告なしで動きます。ただし接続をリクエストの終わりまで保持するので、新しく書くコードでは`with_connection`を使ってください。

### ActiveRecord 7.2の新機能はRails 8でも使えますか？

使えます。この記事で扱った`Transaction`のコールバック、`after_all_transactions_commit`、`attributes_for_inspect`、`with_connection`、`pluck`と`order`のハッシュ指定は、8.1.3.1でも同じ出力になりました。

### Migration\[7.2\]と\[7.1\]で書き方は違いますか？

違いはありません。7.2.4の互換クラス`V7_1`は空のサブクラスで、`[7.1]`と`[7.2]`は同じ挙動になります。マイグレーションの基本的な書き方は[Active Recordの基本を実例で解説した記事](/tech/details/2913/)を参照してください。

## 関連記事

- [Rails 7.2の主な変更点とサポート終了日｜Rails 8.1への移行判断](/tech/details/3362/)
- [Active Record（アクティブレコード）とは？Railsを支える設計パターンと基本を実例で解説](/tech/details/2913/)
- [Rails Active Jobとは？非同期ジョブ処理の実装とSolid Queue・リトライ設定](/tech/details/3988/)
- [counter\_cacheとは？Railsでの実装手順とカウントずれ・後付けの対処](/tech/details/3727/)
- [N+1問題とは？発行クエリ数の実測と検出方法・ORM別の対策](/tech/details/4262/)

---

出典: [ActiveRecord 7.2の変更点｜トランザクションコールバック・attributes\_for\_inspect・with\_connectionを実行して比較](<https://www.issoh.co.jp/tech/details/10104/>)（株式会社一創）
