1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Spring Boot + Apache Cayenne + PostgreSQLでDB接続してみた(CRUD操作編)

1
Last updated at Posted at 2026-02-08

はじめに

前回の投稿に引き続き、Spring Boot + PostgreSQL環境でApache Cayenneを使ったDBアクセスについて解説します。
前回の記事では環境構築までを行ったため、本記事では実際にApache Cayenneを用いたCRUD操作を試していきます。

なお、本記事では、Apache Cayenneフレームワークが提供するAPI・オブジェクトを使用したCRUD操作を中心に扱います。詳細なクラス設計については本記事では触れません。

前回の記事↓

Apache CayenneにおけるCRUDの考え方

Apache CayenneでのDBアクセスの中心となるのがObjectContextです。
エンティティの生成・取得・更新・削除はすべてObjectContextを通して行います。

基本的な考え方は以下の通りです。

  • エンティティの生成・取得・変更はObjectContext上で行う
  • DBへの反映はcommitChanges()を呼び出したタイミングで行われる

以降では、このObjectContextを利用してCREATE/READ/UPDATE/DELETEの各操作を順に見ていきます。

CRUD実践

前回の記事で作成したdemoプロジェクトを使用し、早速CRUD操作を行います。
前項で紹介したObjectContextは、Apache Cayenneが提供するServerRuntimeによって生成されたものを利用します。

ServerRuntimeとは?

Apache Cayenneでは、DB接続情報やマッピング設定を保持する中核コンポーネントとしてServerRuntimeが存在します。
ServerRuntime自体がDB操作を行うわけではなく、実際の操作はこのServerRuntimeから生成されるObjectContextを通して行います。

Configクラスを作成し、以下のコードを入力します。
今回は、ObjectContextをSpringのBeanとして定義し、リポジトリ層からDIを通して利用する構成とします。
※ 本記事では検証・学習目的のためObjectContextをSingleton Beanとして扱っています。

CayenneConfig.java
@Configuration
public class CayenneConfig {

     @Bean
    public ServerRuntime cayenneRuntime() {
        return ServerRuntime.builder()
                .addConfig("cayenne-demo.xml")
                .build();
    }

    @Bean
    public ObjectContext objectContext(ServerRuntime cayenneRuntime) {
        return cayenneRuntime.newContext();
    }

}

CREATE

リポジトリ層としてUserDAO.javaを定義し、DIしたObjectContextを使ってCRUD操作を行います。
まずは、CREATE(INSERT)に該当するnewObject()を使った処理を実装します。

UserDAO.java
@Repository
@RequiredArgsConstructor
public class UserDAO {

    private final ObjectContext objectContext;

    public void save(String name){
        // 引数にCayenneで生成されたエンティティクラスを指定する
        var user = objectContext.newObject(Users.class);
        user.setName(name);
        objectContext.commitChanges();
    }

}

セッターで値をセットした後にobjectContext.commitChanges()を呼び出すことで、ObjectContext上の変更がDBに反映されます。
早速このsaveメソッドを使ってINSERT処理を実現してみます。
なお、今回はCayenneを通したCRUD操作の確認を行いたいので、CommandLineRunnerを実装したクラスを定義し、そこから直接リポジトリ層のDAOを呼び出す構成とします。

CayenneCommandLineRunner.java
@Component
@RequiredArgsConstructor
public class CayenneCommandLineRunner implements CommandLineRunner {

    private final UserDAO userDAO;

     @Override
    public void run(String... args) throws Exception {
        userDAO.save("テスト太郎");
    }
    
}

上記のrunメソッドはSpring Boot起動で動くので、Spring Bootを起動し、saveメソッドを実行してみます。

2026-02-08T15:37:08.803+09:00  INFO 3852 --- [demo] [           main] com.example.demo.DemoApplication         : Starting DemoApplication using Java 21.0.8 with PID 3852 (C:\Users\Miyos\Desktop\demo\target\classes started by Miyos in C:\Users\Miyos\Desktop\demo)
2026-02-08T15:37:08.803+09:00  INFO 3852 --- [demo] [           main] com.example.demo.DemoApplication         : No active profile set, falling back to 1 default profile: "default"
2026-02-08T15:37:09.428+09:00  INFO 3852 --- [demo] [           main] o.a.c.c.x.XMLDataChannelDescriptorLoader : Loading XML configuration resource from file:/C:/Users/Miyos/Desktop/demo/target/classes/cayenne-demo.xml
2026-02-08T15:37:09.483+09:00  INFO 3852 --- [demo] [           main] o.a.c.c.x.XMLDataChannelDescriptorLoader : Loading XML DataMap resource from file:/C:/Users/Miyos/Desktop/demo/target/classes/demo.map.xml    
2026-02-08T15:37:09.521+09:00  INFO 3852 --- [demo] [           main] o.a.cayenne.datasource.DriverDataSource  : Connecting to 'jdbc:postgresql://localhost:5432/demo_db' as 'postgres'
2026-02-08T15:37:09.727+09:00  INFO 3852 --- [demo] [           main] o.a.cayenne.datasource.DriverDataSource  : +++ Connecting: SUCCESS.
2026-02-08T15:37:09.727+09:00  INFO 3852 --- [demo] [           main] o.a.c.c.server.DataDomainProvider        : setting DataNode 'datanode' as default, used by all unlinked DataMaps
2026-02-08T15:37:09.851+09:00  INFO 3852 --- [demo] [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.466 seconds (process running for 1.933)
2026-02-08T15:37:09.908+09:00  INFO 3852 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : Detected and installed adapter: org.apache.cayenne.dba.postgres.PostgresAdapter
2026-02-08T15:37:09.918+09:00  INFO 3852 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : --- transaction started.
2026-02-08T15:37:09.936+09:00  INFO 3852 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : INSERT INTO public.users( name) VALUES( ?)
2026-02-08T15:37:09.936+09:00  INFO 3852 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : [bind: 1->name:'テスト太郎']
2026-02-08T15:37:09.953+09:00  INFO 3852 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : Generated PK: users.id = 1
2026-02-08T15:37:09.953+09:00  INFO 3852 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : === updated 1 row.
2026-02-08T15:37:09.968+09:00  INFO 3852 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : +++ transaction committed.

image.png

ログを見ると、INSERT文の発行、バインド変数の設定、自動採番された主キーの取得、トランザクションのコミットまでが行われていることが確認できます。また、usersテーブルにも無事レコードが入りました。


READ

続いて、READ(SELECT)に該当する処理を行います。
主キーで検索を行うメソッドと名前で検索するメソッドを作成します。

UserDAO.java
public Users findById(int id){
    return Cayenne.objectForPK(objectContext, Users.class, id);
}

public Users findByName(String name){
    return ObjectSelect.query(Users.class)
    .where(Users.NAME.eq(name))
    .selectOne(objectContext);
}

objectForPKは主キー検索を実現してくれます。
ObjectSelect.queryはメソッドチェーン形式でSQLのWHERE句のような検索条件を指定した処理を行うことができます。
findByIdをCommandLineRunnerから呼び出してみます。

CayenneCommandLineRunner.java
public void run(String... args) throws Exception {
        var user = userDAO.findById(1);
        System.out.println("検索結果:" + user.getName());
    }
2026-02-08T19:17:57.854+09:00  INFO 20872 --- [demo] [           main] com.example.demo.DemoApplication         : Starting DemoApplication using Java 21.0.8 with PID 20872 (C:\Users\Miyos\Desktop\demo\target\classes started by Miyos in C:\Users\Miyos\Desktop\demo)
2026-02-08T19:17:57.854+09:00  INFO 20872 --- [demo] [           main] com.example.demo.DemoApplication         : No active profile set, falling back to 1 default profile: "default"
2026-02-08T19:17:59.022+09:00  INFO 20872 --- [demo] [           main] o.a.c.c.x.XMLDataChannelDescriptorLoader : Loading XML configuration resource from file:/C:/Users/Miyos/Desktop/demo/target/classes/cayenne-demo.xml
2026-02-08T19:17:59.071+09:00  INFO 20872 --- [demo] [           main] o.a.c.c.x.XMLDataChannelDescriptorLoader : Loading XML DataMap resource from file:/C:/Users/Miyos/Desktop/demo/target/classes/demo.map.xml   
2026-02-08T19:17:59.154+09:00  INFO 20872 --- [demo] [           main] o.a.cayenne.datasource.DriverDataSource  : Connecting to 'jdbc:postgresql://localhost:5432/demo_db' as 'postgres'
2026-02-08T19:17:59.695+09:00  INFO 20872 --- [demo] [           main] o.a.cayenne.datasource.DriverDataSource  : +++ Connecting: SUCCESS.
2026-02-08T19:17:59.698+09:00  INFO 20872 --- [demo] [           main] o.a.c.c.server.DataDomainProvider        : setting DataNode 'datanode' as default, used by all unlinked DataMaps
2026-02-08T19:18:00.072+09:00  INFO 20872 --- [demo] [           main] com.example.demo.DemoApplication         : Started DemoApplication in 2.984 seconds (process running for 4.08)
2026-02-08T19:18:00.173+09:00  INFO 20872 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : Detected and installed adapter: org.apache.cayenne.dba.postgres.PostgresAdapter
2026-02-08T19:18:00.178+09:00  INFO 20872 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : --- transaction started.
2026-02-08T19:18:00.258+09:00  INFO 20872 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : SELECT t0.name, t0.id FROM public.users t0 WHERE t0.id = ? [bind: 1->id:1]
2026-02-08T19:18:00.291+09:00  INFO 20872 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : === returned 1 row. - took 111 ms.
2026-02-08T19:18:00.291+09:00  INFO 20872 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : +++ transaction committed.
検索結果:テスト太郎

ログ内でクエリが発行され、取得した結果も表示されました。
findByNameに引数を指定した場合も同様の結果となります。


UPDATE

すでに登録されているレコードに対してUPDATE処理を行います。
Apache Cayenneでは、一度取得したエンティティのプロパティを変更しcommitChanges()を呼び出すだけでUPDATEが行われます。

まず、主キーでユーザーを取得し、名前を更新するメソッドを定義します。

UserDAO.java
public void updateName(int id, String name){
        var user = findById(id);
        user.setName(name);
        objectContext.commitChanges();
    }

次に、CommandLineRunnerからこのメソッドを呼び出します。

CayenneCommandLineRunner.java
@Override
    public void run(String... args) throws Exception {
        userDAO.updateName(1, "テスト花子");
    }

Spring Bootを起動すると、以下のログが出力されます。

2026-02-08T20:31:53.130+09:00  INFO 22896 --- [demo] [           main] com.example.demo.DemoApplication         : Starting DemoApplication using Java 21.0.8 with PID 22896 (C:\Users\Miyos\Desktop\demo\target\classes started by Miyos in C:\Users\Miyos\Desktop\demo)
2026-02-08T20:31:53.130+09:00  INFO 22896 --- [demo] [           main] com.example.demo.DemoApplication         : No active profile set, falling back to 1 default profile: "default"
2026-02-08T20:31:54.261+09:00  INFO 22896 --- [demo] [           main] o.a.c.c.x.XMLDataChannelDescriptorLoader : Loading XML configuration resource from file:/C:/Users/Miyos/Desktop/demo/target/classes/cayenne-demo.xml
2026-02-08T20:31:54.308+09:00  INFO 22896 --- [demo] [           main] o.a.c.c.x.XMLDataChannelDescriptorLoader : Loading XML DataMap resource from file:/C:/Users/Miyos/Desktop/demo/target/classes/demo.map.xml   
2026-02-08T20:31:54.366+09:00  INFO 22896 --- [demo] [           main] o.a.cayenne.datasource.DriverDataSource  : Connecting to 'jdbc:postgresql://localhost:5432/demo_db' as 'postgres'
2026-02-08T20:31:54.659+09:00  INFO 22896 --- [demo] [           main] o.a.cayenne.datasource.DriverDataSource  : +++ Connecting: SUCCESS.
2026-02-08T20:31:54.659+09:00  INFO 22896 --- [demo] [           main] o.a.c.c.server.DataDomainProvider        : setting DataNode 'datanode' as default, used by all unlinked DataMaps
2026-02-08T20:31:54.960+09:00  INFO 22896 --- [demo] [           main] com.example.demo.DemoApplication         : Started DemoApplication in 2.818 seconds (process running for 3.7)
2026-02-08T20:31:55.048+09:00  INFO 22896 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : Detected and installed adapter: org.apache.cayenne.dba.postgres.PostgresAdapter
2026-02-08T20:31:55.048+09:00  INFO 22896 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : --- transaction started.
2026-02-08T20:31:55.140+09:00  INFO 22896 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : SELECT t0.name, t0.id FROM public.users t0 WHERE t0.id = ? [bind: 1->id:1]
2026-02-08T20:31:55.178+09:00  INFO 22896 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : === returned 1 row. - took 130 ms.
2026-02-08T20:31:55.178+09:00  INFO 22896 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : +++ transaction committed.
2026-02-08T20:31:55.245+09:00  INFO 22896 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : --- transaction started.
2026-02-08T20:31:55.258+09:00  INFO 22896 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : UPDATE public.users SET name = ? WHERE id = ?
2026-02-08T20:31:55.259+09:00  INFO 22896 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : [bind: 1->name:'テスト花子', 2->id:1]
2026-02-08T20:31:55.267+09:00  INFO 22896 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : === updated 1 row.
2026-02-08T20:31:55.277+09:00  INFO 22896 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : +++ transaction committed.

image.png
UPDATE文が発行され、対象レコードが1件更新されていることが確認できます。DB上のusersテーブルを確認すると、nameカラムが「テスト花子」に更新されています。


DELETE

続いてDELETE処理を行います。
DELETEもUPDATEと同様に、まずObjectContext上で対象エンティティを取得します。

UserDAO.java
public void delete(int id){
        var user = findById(id);
        objectContext.deleteObject(user);
        objectContext.commitChanges();
    }

CommandLineRunnerからdeleteメソッドを呼び出します。

CayenneCommandLineRunner.java
@Override
    public void run(String... args) throws Exception {
        userDAO.delete(1);
    }
}

起動ログを確認すると、以下のような処理が行われています。

  • 対象ユーザーのSELECT
  • 関連テーブル(posts)の参照確認
  • usersテーブルに対するDELETE文の発行
2026-02-08T21:12:05.556+09:00  INFO 24452 --- [demo] [           main] com.example.demo.DemoApplication         : Starting DemoApplication using Java 21.0.8 with PID 24452 (C:\Users\Miyos\Desktop\demo\target\classes started by Miyos in C:\Users\Miyos\Desktop\demo)
2026-02-08T21:12:05.560+09:00  INFO 24452 --- [demo] [           main] com.example.demo.DemoApplication         : No active profile set, falling back to 1 default profile: "default"
2026-02-08T21:12:06.532+09:00  INFO 24452 --- [demo] [           main] o.a.c.c.x.XMLDataChannelDescriptorLoader : Loading XML configuration resource from file:/C:/Users/Miyos/Desktop/demo/target/classes/cayenne-demo.xml
2026-02-08T21:12:06.571+09:00  INFO 24452 --- [demo] [           main] o.a.c.c.x.XMLDataChannelDescriptorLoader : Loading XML DataMap resource from file:/C:/Users/Miyos/Desktop/demo/target/classes/demo.map.xml   
2026-02-08T21:12:06.626+09:00  INFO 24452 --- [demo] [           main] o.a.cayenne.datasource.DriverDataSource  : Connecting to 'jdbc:postgresql://localhost:5432/demo_db' as 'postgres'
2026-02-08T21:12:06.904+09:00  INFO 24452 --- [demo] [           main] o.a.cayenne.datasource.DriverDataSource  : +++ Connecting: SUCCESS.
2026-02-08T21:12:06.904+09:00  INFO 24452 --- [demo] [           main] o.a.c.c.server.DataDomainProvider        : setting DataNode 'datanode' as default, used by all unlinked DataMaps
2026-02-08T21:12:07.121+09:00  INFO 24452 --- [demo] [           main] com.example.demo.DemoApplication         : Started DemoApplication in 2.45 seconds (process running for 3.477)
2026-02-08T21:12:07.226+09:00  INFO 24452 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : Detected and installed adapter: org.apache.cayenne.dba.postgres.PostgresAdapter
2026-02-08T21:12:07.226+09:00  INFO 24452 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : --- transaction started.
2026-02-08T21:12:07.343+09:00  INFO 24452 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : SELECT t0.name, t0.id FROM public.users t0 WHERE t0.id = ? [bind: 1->id:1]
2026-02-08T21:12:07.372+09:00  INFO 24452 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : === returned 1 row. - took 126 ms.
2026-02-08T21:12:07.375+09:00  INFO 24452 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : +++ transaction committed.
2026-02-08T21:12:07.393+09:00  INFO 24452 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : --- transaction started.
2026-02-08T21:12:07.394+09:00  INFO 24452 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : SELECT t0.content, t0.user_id, t0.id FROM public.posts t0 WHERE t0.user_id = ? [bind: 1->user_id:1]
2026-02-08T21:12:07.397+09:00  INFO 24452 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : === returned 0 rows. - took 3 ms.
2026-02-08T21:12:07.399+09:00  INFO 24452 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : +++ transaction committed.
2026-02-08T21:12:07.422+09:00  INFO 24452 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : --- transaction started.
2026-02-08T21:12:07.438+09:00  INFO 24452 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : DELETE FROM public.users WHERE id = ?
2026-02-08T21:12:07.438+09:00  INFO 24452 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : [bind: 1->id:1]
2026-02-08T21:12:07.438+09:00  INFO 24452 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : === updated 1 row.
2026-02-08T21:12:07.438+09:00  INFO 24452 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : +++ transaction committed.

image.png
最終的にusersテーブルから該当レコードが削除されていることが確認できます。


リレーションの関連付け

最後に、外部キーを持つテーブルへのINSERT処理を行います。
ここでは、usersテーブルとpostsテーブルのリレーションを利用し、
ユーザーに紐づく投稿データを登録します。
Cayenneでは、外部キーを直接操作するのではなく、エンティティ同士を関連付けることでリレーションを表現します。
リポジトリ層としてPostDAOを定義し、Postsエンティティを生成します。

PostDAO.java
@Repository
@RequiredArgsConstructor
public class PostDAO {

    private final ObjectContext objectContext;

    public void save(Users user, String content){
        var post = objectContext.newObject(Posts.class);
        post.setUser(user);
        post.setContent(content);
        objectContext.commitChanges();
    }

}

PostsエンティティのsetUser()は、postsテーブルのuser_id(外部キー)に対応するリレーション設定です。

このメソッドでは、

  • 既に取得済みのUsersエンティティを指定
  • Postsエンティティ側で関連付けを行う
  • commitChanges()でINSERTを実行

という流れになります。
CommandLineRunnerから呼び出してみます。

CayenneCommandLineRunnder.java
@Component
@RequiredArgsConstructor
public class CayenneCommandLineRunner implements CommandLineRunner {

    private final UserDAO userDAO;

    private final PostDAO postDAO;

     @Override
    public void run(String... args) throws Exception {
        var user = userDAO.findById(1);
        postDAO.save(user, "テスト投稿");
    }
    
}
2026-02-08T22:35:35.225+09:00  INFO 15984 --- [demo] [           main] com.example.demo.DemoApplication         : Starting DemoApplication using Java 21.0.8 with PID 15984 (C:\Users\Miyos\Desktop\demo\target\classes started by Miyos in C:\Users\Miyos\Desktop\demo)
2026-02-08T22:35:35.229+09:00  INFO 15984 --- [demo] [           main] com.example.demo.DemoApplication         : No active profile set, falling back to 1 default profile: "default"
2026-02-08T22:35:36.386+09:00  INFO 15984 --- [demo] [           main] o.a.c.c.x.XMLDataChannelDescriptorLoader : Loading XML configuration resource from file:/C:/Users/Miyos/Desktop/demo/target/classes/cayenne-demo.xml
2026-02-08T22:35:36.433+09:00  INFO 15984 --- [demo] [           main] o.a.c.c.x.XMLDataChannelDescriptorLoader : Loading XML DataMap resource from file:/C:/Users/Miyos/Desktop/demo/target/classes/demo.map.xml   
2026-02-08T22:35:36.487+09:00  INFO 15984 --- [demo] [           main] o.a.cayenne.datasource.DriverDataSource  : Connecting to 'jdbc:postgresql://localhost:5432/demo_db' as 'postgres'
2026-02-08T22:35:36.761+09:00  INFO 15984 --- [demo] [           main] o.a.cayenne.datasource.DriverDataSource  : +++ Connecting: SUCCESS.
2026-02-08T22:35:36.761+09:00  INFO 15984 --- [demo] [           main] o.a.c.c.server.DataDomainProvider        : setting DataNode 'datanode' as default, used by all unlinked DataMaps
2026-02-08T22:35:36.986+09:00  INFO 15984 --- [demo] [           main] com.example.demo.DemoApplication         : Started DemoApplication in 2.741 seconds (process running for 3.936)
2026-02-08T22:35:37.110+09:00  INFO 15984 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : Detected and installed adapter: org.apache.cayenne.dba.postgres.PostgresAdapter
2026-02-08T22:35:37.110+09:00  INFO 15984 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : --- transaction started.
2026-02-08T22:35:37.234+09:00  INFO 15984 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : SELECT t0.name, t0.id FROM public.users t0 WHERE t0.id = ? [bind: 1->id:1]
2026-02-08T22:35:37.256+09:00  INFO 15984 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : === returned 1 row. - took 146 ms.
2026-02-08T22:35:37.269+09:00  INFO 15984 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : +++ transaction committed.
2026-02-08T22:35:37.348+09:00  INFO 15984 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : --- transaction started.
2026-02-08T22:35:37.354+09:00  INFO 15984 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : INSERT INTO public.posts( content, user_id) VALUES( ?, ?)
2026-02-08T22:35:37.356+09:00  INFO 15984 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : [bind: 1->content:'テスト投稿', 2->user_id:1]
2026-02-08T22:35:37.369+09:00  INFO 15984 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : Generated PK: posts.id = 1
2026-02-08T22:35:37.370+09:00  INFO 15984 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : === updated 1 row.
2026-02-08T22:35:37.379+09:00  INFO 15984 --- [demo] [           main] org.apache.cayenne.log.JdbcEventLogger   : +++ transaction committed.

実行ログを確認すると、以下のように

  • usersテーブルから対象ユーザーをSELECT
  • postsテーブルに対してuser_idを含むINSERT

が順に実行されていることが分かります。
テーブルにも無事レコードが挿入されています。
image.png
このように、Cayenne では外部キーをセットするのではなくエンティティ同士を関連付けることでリレーションを扱う点が特徴です。
JPA の @ManyToOne / @OneToMany に近い感覚で扱えます。

まとめ

本記事では、Spring Boot + PostgreSQL環境においてApache Cayenneを用いたCRUD操作を確認しました。

  • CREATE:newObject()でエンティティを生成し、commitChanges()でINSERT
  • READ:objectForPKやObjectSelectを用いた取得
  • UPDATE:取得済みエンティティのプロパティ変更後にcommitChanges()
  • DELETE:deleteObject()後にcommitChanges()

Apache Cayenneでは、ObjectContext上でのオブジェクト操作がそのままDB操作につながる点が特徴です。SQLを直接記述せずとも、状態管理とトランザクション制御をフレームワークに任せることができます。

おわりに

本記事では、Apache Cayenneを使用したSpring Boot環境において、ObjectContextを起点とした基本的なCRUD操作と、エンティティ間のリレーションを用いた永続化処理を確認しました。

JPA/Hibernateと比較すると、Cayenneは、

  • ObjectContext を通じて明示的に操作する
  • エンティティの状態遷移がログで分かりやすい

といった特徴があり、ORMの内部挙動を意識しながら開発できる点が印象的です。

なお、本記事では学習・検証を目的としてObjectContextをSpringのBeanとして定義し、CommandLineRunnerから直接DAOを呼び出す構成としています。
実際のWebアプリケーションでは、トランザクション境界やObjectContextのライフサイクルを考慮し、Controller → Service → Repositoryといった層構造で利用することが一般的です。
Apache Cayenneは日本語情報が少なく取っ付きにくい印象がありますが、一度動かしてみると、ORMとしての基本的な考え方は他フレームワークと共通しています。

本記事が、Cayenneを使った開発やORMの理解を深めるきっかけになれば幸いです。
最後まで読んでいただきありがとうございました!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?