1

私は持っている:

  • PostgreSQL 9.4
  • アパッチ カイエン 4.0.M3
  • 1 つの最も単純なテーブル「proba」で構成されるスキーマ:

    CREATE TABLE proba (id bigint NOT NULL, 値の文字が変化する(255), CONSTRAINT proba_pkey PRIMARY KEY (id) )

  • シンプルな Main メソッド:

    public static void main(String[] args) {
        ServerRuntime runtime = ServerRuntimeBuilder.builder()
                .addConfig("cayenne-project.xml")
                .build();
    
        ObjectContext ctx = runtime.newContext();
    
        CayenneDataObject newObject = new CayenneDataObject();
        newObject.writeProperty("value", "proba1");
        ctx.registerNewObject(newObject);
        ctx.commitChanges();
    }
    
  • シンプルな cayenne-project.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <domain project-version="7">
        <map name="datamap"/>
        <node name="datanode" 
              factory="org.apache.cayenne.configuration.server.XMLPoolingDataSourceFactory" 
              schema-update-strategy="org.apache.cayenne.access.dbsync.SkipSchemaUpdateStrategy">
            <map-ref name="datamap"/>
            <data-source>
                  ....
            </data-source>
        </node>
    </domain>
    
  • シンプルな datamap.map.xml (手作り):

    <?xml version="1.0" encoding="utf-8"?>
    <data-map xmlns="http://cayenne.apache.org/schema/7/modelMap"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://cayenne.apache.org/schema/7/modelMap http://cayenne.apache.org/schema/7/modelMap.xsd"
              project-version="7">
        <property name="defaultPackage" value="ru.xxx"/>
        <property name="defaultSchema" value="public"/>
        <db-entity name="proba" schema="public">
            <db-attribute name="id" type="BIGINT" isPrimaryKey="true" isGenerated="false" length="19"/> 
            <db-attribute name="value" type="VARCHAR" length="255"/>
        </db-entity>
        <obj-entity name="Proba" dbEntityName="proba">
            <obj-attribute name="value" type="java.lang.String" db-attribute-path="value"/>
        </obj-entity>
    </data-map>
    

試してみると、次の出力が得られました。

    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)

そのため、カイエンは pk_proba という名前のシーケンスを期待しています。なんで?生成するつもりはありませんでした。私のスキーマでも、Cayenne マッピングでも、postgresql シーケンスについて言及しませんでした。

だから、私は2つの質問があります:

  • コミット時に特定のエンティティに対して ID が提供されない場合、Cayenne が ID を生成しようとする試みを抑制し、Cayenne を高速で失敗させるにはどうすればよいですか?
  • 私のプロジェクトで Cayenne が PK 自動生成を管理する方法をカスタマイズできますか?
4

1 に答える 1

1

TL;DR: 「pk_proba」は、PK 生成に使用するシーケンスのデフォルト名です。Cayenne のデフォルト PK メカニズムを機能させるには、PostgreSQL で特別なシーケンスを提供する必要があります。

より長いバージョン。挿入された各オブジェクトに何らかの方法で PK を提供する必要があります。Cayenne PK 生成アルゴリズムは、大まかに次のように機能します。

  • ユーザーがオブジェクト プロパティとして提供する PK の場合は、それを使用します。
  • PK がリレーションシップを介してマスター オブジェクトから伝達される場合は、それを使用します。
  • PK が DB の auto_increment 列である場合は、それを使用します (4.0.M4 以降の PG でサポートされています)。
  • 他のすべてが失敗した場合は、Cayenne PK ジェネレーターを使用してください。

最後の戦略では、DB オブジェクトを準備する必要があります。Cayenne は、ターゲット DB に応じて異なる戦略を使用します。PostgreSQL の場合はシーケンスになります。Modeler で [ツール] > [データベース スキーマの生成] に移動し、[主キー サポートの作成] 以外のすべてのチェックボックスをオフにします。次に、生成された SQL を使用して DB を更新します。

ここで、ステップ 4 で Cayenne を失敗させたい場合 (なぜですか? 結局、挿入を成功させたいのですが)、カスタム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 サービスとして公開する予定です) が、うまくいくはずです。これが本当に必要なものであることを確認してください。

于 2016-11-15T14:50:58.420 に答える