0

I'm looking at Apache Cayenne as my ORM layer for a new application.

Part of my DB model will be defined in a fixed model which will be known at compile time.

However, another part of the model will be defined at runtime by certain limited user operations. So, these operations will have the effect of creating certain simple tables, adding and removing columns from these tables, deleting tables etc.

It does seem that Cayenne is well-suited to this, based on its generic persistent objects feature.

https://cayenne.apache.org/docs/3.1/cayenne-guide/persistent-objects-objectcontext.html

I'm looking for some examples of how I would introduce the user-defined DbEntitys into the Cayenne runtime, generate and run the CREATE / ALTER / DROP SQL, and then specify that certain generic persistent objects are backed by certain user-defined tables.

oggotron
  • 27
  • 3

1 Answers1

2

Yes, Cayenne generic objects make it possible to alter your model in runtime. You have an additional challenge to update the actual schema. There are a few unknowns in the description (is the underlying DB shared by multiple apps/users; concurrency of the schema changes; do the changes need to be preserved in the underlying Cayenne model after they are applied). But here is a rough idea of how I would approach this:

Whenever a user is ready to make new changes, load the affected cayenne-project.xml separately from ServerRuntime using cayenne-project.jar library.

Injector i = DIBootstrap.createInjector(new CayenneProjectModule());
ProjectLoader loader = i.getInstance(ProjectLoader.class);
Project p = loader.loadProject(new URLResource(..));

Make changes:

ConfigurationNodeVisitor mapChangeAction = .. // implement this to make your changes
p.getRootNode().acceptVisitor(mapChangeAction);

Save back to file system:

i.getInstance(ProjectSaver.class).save(p);

Now you might create a second ServerRuntime using a fresh model, and then run migrations against DB using API from org.apache.cayenne.merge package. You can create MergeToken's based on the changes you've made above using DbAdapter.mergerFactory() and then execute them:

DataDomain domain = newRuntime.getDataDomain();
DataNode node = domain.getDataNode("nodename");
DataMap map = domain.getDataMap("mapname");

List<MergerToken> tokens = ...
MergerContext mergerContext = new ExecutingMergerContext(map, node);
for (MergerToken tok : tokens) {
    tok.execute(mergerContext);
}

Finally replace your original ServerRuntime that had the old model, with the 'newRuntime'.

andrus_a
  • 2,528
  • 1
  • 16
  • 10
  • Great! To clarify, I'll be able to guarantee that no other users / apps / operations are using the affected parts of the model during the changes. So I should be able to avoid any conflicts. I'm not clear about this query "do the changes need to be preserved in the underlying Cayenne model". – oggotron Jun 15 '15 at 07:54
  • It looks as though I'm only able to communicate with the ServerRuntime API and possibly the Project API via the file system. Is there any specific reason for this, for example do they poll the named file trying to pick up any changes? – oggotron Jun 15 '15 at 07:59
  • Suppose that I've added a new table using the above outlined technique. I've then switched to the new ServerRuntime which has the updated model. I now want to create a new row in that table, using a generic persisted object. I get the context from the ServerRuntime, cast it to DataContext, and use newObject(String entityName). What is the meaning of entityName in this case? – oggotron Jun 15 '15 at 08:07
  • Would you mind posting to Cayenne user mailing lists or opening a new stackoverflow question to discuss generic entity API? – andrus_a Jun 15 '15 at 12:34
  • Done: http://stackoverflow.com/questions/30862385/apache-cayenne-supplying-dynamically-generated-project-definition – oggotron Jun 16 '15 at 08:27