Reputation: 1217
I have:
A schema which consists of one simplest table "proba":
CREATE TABLE proba ( id bigint NOT NULL, value character varying(255), CONSTRAINT proba_pkey PRIMARY KEY (id) )
A simple Main method:
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();
}
A simple 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>
A simple datamap.map.xml (hand-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>
Trying it, I got the following output:
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)
So, cayenne expects a sequence named pk_proba. Why? I did't mean it to be generated. I didn't mentioned any postgresql sequences neither in my schema, nor in the Cayenne mappings.
So, I have two questions:
Upvotes: 1
Views: 822
Reputation: 2563
TL;DR: "pk_proba" is the default name of a sequence to use for PK generation. If you want Cayenne default PK mechanism to function, you need to provide special sequences in PostgreSQL.
A longer version. You need to provide a PK to each inserted object one way or another. Cayenne PK generation algorithm roughly works like this:
The last strategy requires you to prepare DB objects. Cayenne uses different strategies depending on the target DB. For PostgreSQL it will be sequences. In the Modeler go to "Tools > Generate Database Schema" and uncheck all checkboxes except "Create Primary Key Support". Then use the generated SQL to update your DB.
Now if you indeed want Cayenne to fail at step 4 (why though? you do want your insert to succeed after all), you can use a custom PkGenerator. Here is how you can load this via dependency injection using a custom DI module:
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);
}
};
Admittedly this can be made easier (and we are planning to expose PkGenerator as a DI service), but it should work. Just make sure this is really what you need.
Upvotes: 1