diff --git a/docs/ja/reference/framework-modules.md b/docs/ja/reference/framework-modules.md index 195a383..29d3ca6 100644 --- a/docs/ja/reference/framework-modules.md +++ b/docs/ja/reference/framework-modules.md @@ -148,6 +148,29 @@ rows = executor.fetch_all("SELECT * FROM notes WHERE id = :id", {"id": 1}) executor.write("INSERT INTO notes (title, body) VALUES (:t, :b)", {"t": "t", "b": "b"}) ``` +#### `write()` の返り値 + +| 操作 | 返り値 | +|---|---| +| `AUTOINCREMENT` / `SERIAL` 付き `INSERT` | `lastrowid` — 新規行の主キー(常に > 0) | +| auto-PK なし、またはマルチ行 `INSERT` | `rowcount` — 挿入行数 | +| `UPDATE` / `DELETE` | `rowcount` — 影響行数(0 は該当なし) | + +INSERT 後にエンティティを再構築する場合: + +```python +new_id = executor.write("INSERT INTO notes (title) VALUES (:title)", {"title": "Hello"}) +return Note(id=new_id, title="Hello") +``` + +UPDATE / DELETE で存在しないリソースを検出する場合: + +```python +affected = executor.write("UPDATE notes SET title=:title WHERE id=:id", {"title": t, "id": pk}) +if affected == 0: + raise NoteNotFoundException(pk) +``` + ### `SqlAlchemyTransactionManager` トランザクションを管理します。手動の `begin/commit/rollback` より `transactional()` を推奨します。 diff --git a/docs/reference/framework-modules.md b/docs/reference/framework-modules.md index e58d1d4..6a690e8 100644 --- a/docs/reference/framework-modules.md +++ b/docs/reference/framework-modules.md @@ -203,6 +203,31 @@ rows = executor.fetch_all("SELECT * FROM notes WHERE id = :id", {"id": 1}) executor.write("INSERT INTO notes (title, body) VALUES (:t, :b)", {"t": "t", "b": "b"}) ``` +#### `write()` return value + +`write()` returns an `int` whose meaning depends on the SQL operation: + +| Operation | Return value | +|---|---| +| `INSERT` with `AUTOINCREMENT` / `SERIAL` | `lastrowid` — the new row's primary key (always > 0) | +| `INSERT` without auto-PK, or multi-row `INSERT` | `rowcount` — number of rows inserted | +| `UPDATE` / `DELETE` | `rowcount` — rows affected (0 if nothing matched) | + +Use `lastrowid` to reconstruct the entity after a single-row INSERT: + +```python +new_id = executor.write("INSERT INTO notes (title) VALUES (:title)", {"title": "Hello"}) +return Note(id=new_id, title="Hello") +``` + +Use `rowcount` to detect a missing row on UPDATE / DELETE: + +```python +affected = executor.write("UPDATE notes SET title=:title WHERE id=:id", {"title": t, "id": pk}) +if affected == 0: + raise NoteNotFoundException(pk) +``` + ### `SqlAlchemyTransactionManager` Manages transactions. Prefer `transactional()` over manual `begin/commit/rollback`. diff --git a/src/nene2/database/sqlalchemy_executor.py b/src/nene2/database/sqlalchemy_executor.py index 5f0472f..40b1b37 100644 --- a/src/nene2/database/sqlalchemy_executor.py +++ b/src/nene2/database/sqlalchemy_executor.py @@ -37,6 +37,19 @@ def fetch_one(self, sql: str, params: dict[str, Any] | None = None) -> dict[str, raise DatabaseConnectionException(str(exc)) from exc def write(self, sql: str, params: dict[str, Any] | None = None) -> int: + """Execute INSERT / UPDATE / DELETE and return a meaningful int. + + Return value semantics: + - INSERT with AUTOINCREMENT/SERIAL column → ``lastrowid`` (the new row's PK, always > 0) + - INSERT without auto-PK, or multi-row INSERT → falls back to ``rowcount`` + - UPDATE / DELETE → ``rowcount`` (number of rows affected; 0 means nothing matched) + + Use the return value to detect missing rows:: + + affected = executor.write("UPDATE ... WHERE id = :id", {"id": pk}) + if affected == 0: + raise NotFoundException(pk) + """ try: with self._engine.begin() as conn: result = conn.execute(text(sql), params or {})