Skip to content

JAVA-2307: Improve @Select and @Delete by not requiring full primary key #1285

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 1, 2019

Conversation

tolbertam
Copy link
Contributor

For JAVA-2307


Motivation:

Currently @Select and @Delete annotated methods only allow
selecting/deleting individual entities when parameters are provided as
the mapper currently requires specifying all columns of the primary key as
parameters.

Since CQL allows you to select and delete multiple rows by providing a
subset of the primary key, these dao methods can be improved by
accepting a subset of the primary key as parameters, as long as
at least the partition keys are represented.

In addition @Select should be supported when providing no parameters,
which will allow querying an entire table's rows for a given entity.

Modifications:

Add EntityHelper.selectByPrimaryKeyParts and deleteByPrimaryKeyParts
methods and added generators to provide this capability.

Refactor parameter validation into EntityUtils.areParametersValid from
somewhat duplicated logic in delete and select method generators.

Add tests to exercise functionality.

Add documentation demonstrating extended capability.

Result:

@Select and @Delete now support providing a subset of primary key to
select/delete multiple entities.

@Select also supports providing zero parameters, which performs a
select on all rows in an entity's table.

Motivation:

Currently `@Select` and `@Delete` annotated methods only allow
selecting/deleting individual entities when parameters are provided as
the mapper currently requires specifying all columns of the primary key as
parameters.

Since CQL allows you to select and delete multiple rows by providing a
subset of the primary key, these dao methods can be improved by
accepting a subset of the primary key as parameters, as long as
at least the partition keys are represented.

In addition `@Select` should be supported when providing no parameters,
which will allow querying an entire table's rows for a given entity.

Modifications:

Add `EntityHelper.selectByPrimaryKeyParts` and `deleteByPrimaryKeyParts`
methods and added generators to provide this capability.

Refactor parameter validation into EntityUtils.areParametersValid from
somewhat duplicated logic in delete and select method generators.

Add tests to exercise functionality.

Add documentation demonstrating extended capability.

Result:

`@Select` and `@Delete` now support providing a subset of primary key to
select/delete multiple entities.

`@Select` also supports providing zero parameters, which performs a
select on all rows in an entity's table.
@tolbertam tolbertam requested a review from olim7t June 27, 2019 18:08
*/
@NonNull
default Select selectByPrimaryKeyParts(int parameterCount) {
throw new UnsupportedOperationException("selectbyPrimaryKeyParts is unimplemented");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done to avoid breaking changes, although it is a bit awkward.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done to avoid breaking changes, although it is a bit awkward.

Yes, and this only hides the breaking change by turning it into a runtime error. I've been thinking about other possible approaches:

  1. introduce a sub-interface and require a cast (as we've done in other cases). The problem is that this doesn't scale well: we add ExtendedProductHelper now but what if we need more methods in the next version? ExtendedExtendedProductHelper?

  2. Accept the breaking change and document it, on the assumption that clients probably don't implement this interface. The problem is in the "probably"; also, it's a slippery slope.

  3. Don't add those methods to EntityHelper. Instead, generate the query directly from the method generators. For SELECT we already have selectStart(); for DELETE we have to duplicate the whole method, but it's not huge either.

    We won't make those queries available to users through the interface (e.g. for custom query providers), but they're not that hard to rewrite from scratch anyway.

My vote goes to 3.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't add those methods to EntityHelper

You know, it didn't occur to me that generated code used the implementations instead of declaring things as EntityHelper. In this case, there is no reason to add something to the interface, so I agree this seems like a good course of action.

It looks like the only place (currently) that EntityHelper is used as an interface in other code is in QueryProvider and in DaoBase, where it's more used for setting/getting fields.

Since this method is used for query generation, I suppose it's probably fine to not put directly in EntityHelper.

But I do wonder if we should just consider removing all the query-generation based methods from EntityHelper now if we see no utility. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You know, it didn't occur to me that generated code used the implementations instead of declaring things as EntityHelper.

It didn't occur to me either 😆 I was initially thinking of moving the code to the DAO implementation class, but keeping them as "internal" methods in the helper class is even better.

removing all the query-generation based methods from EntityHelper

As we discussed privately, those methods can be useful for @QueryProvider implementors, so it's nice to have them on the interface.

@@ -2,8 +2,10 @@

<!-- Note: contrary to 3.x, insert new entries *first* in their section -->


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary change.

### 4.2.0 (in progress)

- [improvement] JAVA-2307: Improve @Select and @Delete by not requiring full primary key
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip: place all words starting with @ within backticks here and in commit messages, to avoid having Github consider that these are user handles.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'll amend the commit when merging. It looks like github doesn't hijack markdown in the same way, but agree that we shoulds use backticks here as well.

*/
@NonNull
default Select selectByPrimaryKeyParts(int parameterCount) {
throw new UnsupportedOperationException("selectbyPrimaryKeyParts is unimplemented");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done to avoid breaking changes, although it is a bit awkward.

Yes, and this only hides the breaking change by turning it into a runtime error. I've been thinking about other possible approaches:

  1. introduce a sub-interface and require a cast (as we've done in other cases). The problem is that this doesn't scale well: we add ExtendedProductHelper now but what if we need more methods in the next version? ExtendedExtendedProductHelper?

  2. Accept the breaking change and document it, on the assumption that clients probably don't implement this interface. The problem is in the "probably"; also, it's a slippery slope.

  3. Don't add those methods to EntityHelper. Instead, generate the query directly from the method generators. For SELECT we already have selectStart(); for DELETE we have to duplicate the whole method, but it's not huge either.

    We won't make those queries available to users through the interface (e.g. for custom query providers), but they're not that hard to rewrite from scratch anyway.

My vote goes to 3.

tolbertam and others added 3 commits July 1, 2019 08:42
…l/mapper/processor/dao/EntityUtils.java

Co-Authored-By: Olivier Michallat <olim7t@users.noreply.github.com>
* Use backticks for annotations in changelog.
* Use constants for dates in tests.
* Make more explicit in readme that clustering column order matters.
* Rename keyParameterSize -> primaryKeyParamterCounter
* sublist primaryKeyNames to avoid assertion error.
* Demote deleteByPrimaryKeyParts / selectByPrimaryKeyParts from
  EntityHelper interface.
*
Copy link
Contributor

@olim7t olim7t left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

void deleteByIdForCustomer(UUID productId, LocalDate day, UUID customerId);

/* Note that the clustering columns in your primary key definition are significant. All
* proceeding clustering columns must be provided if any are.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

proceeding clustering columns

Did you mean "preceding"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦‍♂️ yep, that's what I meant. Good catch, i'll fix.

@tolbertam tolbertam merged commit f26cae8 into 4.x Jul 1, 2019
@tolbertam tolbertam deleted the java2307 branch July 1, 2019 19:38
@tolbertam tolbertam added this to the 4.2.0 milestone Jul 1, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants