如何与卡宴4.0 + 9.4的PostgreSQL

问题描述:

管理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的解决方案)?

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。 –