2016-11-15 130 views
1

管理PK代我:如何與卡宴4.0 + 9.4的PostgreSQL

  • 的PostgreSQL 9.4
  • 阿帕奇卡宴4.0.M3
  • 它由一個簡單的表格 「PROBA」 A模式:

    CREATE TABLE PROBA( ID BIGINT NOT NULL, 值字符改變(255), 約束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(); 
    } 
    
  • 一個簡單的辣椒-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(手maded):

    <?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序列。

所以,我有兩個問題:

  • 我怎樣才能抑制卡宴嘗試生成的ID,讓卡宴快速失敗,如果在提交時提供了具體的實體沒有身份?
  • 我可以自定義一種方式,讓Cayenne管理我的項目中的PK自動生成(最好的解決方案是不涉及Cayenne Modeller的解決方案)?

回答

1

TL; DR:「pk_proba」是用於PK生成的序列的缺省名稱。如果你想讓Cayenne默認的PK機制起作用,你需要在PostgreSQL中提供特殊的序列。

更長的版本。您需要以某種方式爲每個插入的對象提供一個PK。卡宴PK生成算法大致如下:

  • 如果PK提供的用戶作爲對象屬性,請使用它。
  • 如果通過關係從主對象傳播PK,請使用它。
  • 如果PK是數據庫中的auto_increment列,請使用它(自4.0.M4起支持PG)
  • 如果一切都失敗,請使用Cayenne PK生成器。

上一個策略需要您準備DB對象。 Cayenne根據目標數據庫使用不同的策略。對於PostgreSQL它將是序列。在Modeler中,轉到「工具>生成數據庫模式」並取消選中除「創建主鍵支持」之外的所有複選框。然後使用生成的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服務),但它應該工作。只要確定這是你真正需要的。

+0

謝謝安德魯斯提供的解決方案適用於我,至少對於案例2(自定義ID生成管理)。 – skapral

+0

對於案例1(快速失蹤身份失敗),我個人預計會在db-attribute或obj-attribute上有一些標誌(更令我驚訝的是,有一個名爲isGenerated,但沒有區別)。回答你的問題(爲什麼?畢竟你確實希望你的插入成功) - 如果我的密鑰由另一方管理:在數據庫之外的地方怎麼辦?無論如何 - 在JPA中,只需不加@GeneratedValue註解就可以實現 - 爲什麼它在Cayenne中非常重要? – skapral

+0

生成==自動增量。它確實有所作爲,但只適用於PostgreSQL的M4。 –

相關問題