0

I'm building an ORM based on the repository pattern. Inside the repository I create something that I call a "data source" to fetch results from the db and map them to entity objects. Each data source is created with a query, and is responsible for getting either a single result for the first row, or all results. While the data source is mainly responsible to fetch get either a single or all objects from a (prepared) query, it also works as a proxy to a "cursor" that is setup during creation of the data source. I put the cursor into quotes, because it actually is an array of primary keys, which are later used to fetch entities from the repository using these IDs. This cursor-like implementation is due to SQLite which just doesn't offer anything better in that case in regards to cursors.

So the data source provides singleEntity(), allEntities(), firstEntity(), nextEntity(), previousEntity(), entityAtIndex(). As said before, all but the first two are actually utilizing the cursor. The cursor is created from the same query as the data source (but auto-optimized to limit selected fields for the cursor).

The repository itself provides the CRUD functionality (getById(), remove(), create(), update()).

That could be all good and well, most of that is just common pattern implementation... But just now I realize the following problem:

Even though it is the the repository itself that should be used to fetch entities by ID, a data source within the repository may just use a different projection for the data - or only a subset of the available. Thus it may return different or just some alternate mapping to what the default repository getEntityById() would return.

How should the design be changed? 1) Disallow other mappings than the default repo getter? => Would be bad design, probably. How to control queries for data sources etc. 2) Force the user to setup custom queries for the cursor for creating the index + fetcher query for fetching individual results by id? => Better than previous idea, but would also mean probable place for inconsistency in case the cursor fetcher is setup different from the data source result mapping. 3) Hopefully something that I didn't think about yet. I still can easily redesign the complete API

benjist
  • 2,740
  • 3
  • 31
  • 58

1 Answers1

0

To answer my own question, I came to the conclusion that the only valid way to handle it is to require the user to create the SQL cursor with a fetch-query, which by convention must be able to fetch a single entity using the very same projection that the data source uses to fetch records from the db. The cursor index (array with pair of key, rowid) cache is created by reusing the very same query of the data source. For convenience an entities array is created from the original data source query, which will be created when the user uses one of the cursor methods without - having created an optimized SQL cursor.

That way it will always have proper mapping in default mode (using the array of entites from the data source when no special cursor was created as mentioned before), and a mapping in sql cursor mode which is the user's responsibility to create it.

It just doesn't make sense to assume anything different, as there is no real cursor in SQLite.

benjist
  • 2,740
  • 3
  • 31
  • 58