如何使用 Cayenne 4.0 + PostgreSQL 9.4 管理 PK 生成

How to manage PK generation with Cayenne 4.0 + PostgreSQL 9.4

我有:

尝试了一下,我得到了以下输出:

    INFO: --- transaction started.
    Nov 15, 2016 5:06:26 PM org.apache.cayenne.log.CommonsJdbcEventLogger logQuery
    INFO: SELECT nextval('public.pk_proba')
    Exception in thread "main" org.apache.cayenne.CayenneRuntimeException: [v.4.0.M3 Feb 08 2016 16:38:05] Commit Exception
        at org.apache.cayenne.access.DataContext.flushToParent(DataContext.java:776)
        at org.apache.cayenne.access.DataContext.commitChanges(DataContext.java:693)
        at com.echelon.proba.cayenne.Main.main(Main.java:27)
    Caused by: org.postgresql.util.PSQLException: ERROR: relation "public.pk_proba" does not exist
      Position: 16
        at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2458)
        at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2158)

因此,cayenne 需要一个名为 pk_proba 的序列。为什么?我不是说要生成它。我既没有在我的架构中也没有在 Cayenne 映射中提到任何 postgresql 序列。

所以,我有两个问题:

TL;DR:"pk_proba" 是用于生成 PK 的序列的默认名称。如果想让Cayenne默认的PK机制发挥作用,需要在PostgreSQL.

中提供特殊序列

更长的版本。您需要以一种或另一种方式为每个插入的对象提供 PK。卡宴PK生成算法大致是这样的:

  • 如果PK是用户提供的对象属性,使用它。
  • 如果 PK 是通过关系从主对象传播的,请使用它。
  • 如果 PK 是数据库中的 auto_increment 列,则使用它(自 4.0.M4 起在 PG 上支持)
  • 如果一切都失败了,请使用 Cayenne PK 生成器。

最后一种策略要求您准备数据库对象。 Cayenne 根据目标数据库使用不同的策略。对于 PostgreSQL 它将是序列。在建模器中转到 "Tools > Generate Database Schema" 并取消选中除 "Create Primary Key Support" 之外的所有复选框。然后使用生成的 SQL 更新您的数据库。

现在,如果您确实希望 Cayenne 在第 4 步失败(为什么?毕竟您确实希望插入成功),您可以使用自定义 PkGenerator。以下是如何使用自定义 DI 模块通过依赖注入加载它:

class CustomAdapterFactory extends DefaultDbAdapterFactory {
    public CustomAdapterFactory(
       @Inject("cayenne.server.adapter_detectors") 
       List<DbAdapterDetector> detectors) {
        super(detectors);
    }

    @Override
    public DbAdapter createAdapter(
        DataNodeDescriptor nodeDescriptor, 
        DataSource dataSource) throws Exception {

        AutoAdapter adapter = 
           (AutoAdapter) super.createAdapter(nodeDescriptor, dataSource);

        // your PkGenerator goes here
        adapter.setPkGenerator(...);
        return adapter;
    }
}

// add this when creating ServerRuntime
Module module = new Module() {
        @Override
        public void configure(Binder binder) {

            binder.bind(DbAdapterFactory.class).to(CustomAdapterFactory.class);
        }
    };

诚然,这可以变得更容易(我们计划将 PkGenerator 作为 DI 服务公开),但它应该可以工作。只要确保这确实是您所需要的。