Skip to content
This repository has been archived by the owner on Nov 23, 2021. It is now read-only.

spring-data and more #627

Merged
merged 14 commits into from
Jul 31, 2018
Merged

spring-data and more #627

merged 14 commits into from
Jul 31, 2018

Conversation

hohwille
Copy link
Member

@hohwille hohwille commented Jan 19, 2018

Here is a great PR with the following features:

Have fun.

Questions:

  • IMHO we actually made a design flaw by putting the SearchCriteria stuff into jpa module and jpa specific package even though it is pretty generic and can also be used for NoSQL DBs. Should have actually be placed in oasp4j-basic.
  • Further, spring(-data) has its own support for paging. IMHO we are forced to take a strategic decision.
    See https://github.com/hohwille/oasp4j/wiki/paging

As we voted to go for pagination support of spring-data, I deprecated all the TOs in oasp4j-jpa and moved all the other code that is still valid to oasp4j-jpa-basic. Now we have oasp4j-jpa-dao and oasp4j-jpa-spring-data that both depend on oasp4j-jpa-basic and give you the option to choose between spring-data or our own DAO support. If you want to have compatibility support for SearchCriteriaTo with the old pagination support, you can still depend on oasp4j-jpa module where this stuff is still contained but deprecated. For your DOA Implementations you can use LegacyDaoQuerySupport that still contains the old paging support as static methods. However, on the long run, we want to get rid of oasp4j-jpa module itself.

#616: moved DAO stuff to new module
#626: added spring-data support
* @since 3.0.0
*/
// QueryDslJpaRepository<E, ID> forces you to use QueryDSL APT generation what is not desired
// therefore no support for QueryDslPredicateExecutor (we offer more flexible QueryDSL support anyhow)
Copy link
Member

Choose a reason for hiding this comment

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

We should put this remark into javadocs. Otherwise, we might get features requests for this as the docs were not accessible easily.

Copy link
Member Author

Choose a reason for hiding this comment

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

fine. I will do that.

if (size <= maxSizeOfInClause) {
return expression.in(inValues);
}
LOG.warn("Creating workaround for IN-clause with {} items - this can cause performance problems.", size);
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure, whether we should do this automatically.

  • I think this should be opt-in. Not opt-out.
  • The default getInstance() constructor even does not restrict anything as it will set maxSize to INT_MAX.
  • The DatabaseWorkaround class comes with a strange name, which does not correlate to the function of the class. It is just a value holder, not a workaround implementation.
  • How can I adapt the value of DatabaseWorkaround maxSize? I think it is not possible besides calling the constructor in bootstrapping phase of my application. However, this is not documented anywhere and I think it is a bad design. We should go back and provide config properties for maxInClauseSize and/or allowArbitraryLongInClauses. However, as you can set the first property to MAX_INT, it is even up to discussion if you want to have the second property, which just makes sense if we have a good default value in mind besides MAX_INT.

Copy link
Member Author

Choose a reason for hiding this comment

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

But this feature is already opt-in as it is disabled by default:
https://github.com/hohwille/oasp4j/blob/2fe1d69736fc62ed5e91ef5e6845587e8f7e654d/modules/jpa/src/main/java/io/oasp/module/jpa/dataaccess/api/DatabaseWorkaround.java#L29

In order to enable the feature you can subclass DatabaseWorkaround and instantiate it during bootstrap (e.g. via spring).

So all I am seeing valid here is that DatabaseWorkaround is an odd name. We should then agree on what would be a better name for it. A neutral name such as DatabaseConfig?
I would have used application.properties and use a custom spring property but that would IMHO not work well with the static access (and might make the solution unusable without spring-boot).

Copy link
Member

Choose a reason for hiding this comment

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

But this feature is already opt-in as it is disabled by default

ok, got it. I thought the "workaround" is to support a larger amount of IN clauses than a preconfigured maximum. So the "workaround" here is to restrict IN clauses manually to assure database misbehavior? This was not really clear for me by reading the javadocs.

So all I am seeing valid here is that DatabaseWorkaround is an odd name. We should then agree on what would be a better name for it. A neutral name such as DatabaseConfig?

What about SqlInClauseRestriction? This is the only value, which is hold by this class.

I would have used application.properties and use a custom spring property but that would IMHO not work well with the static access (and might make the solution unusable without spring-boot).

I do not get this entirely. You mean, as you don't have access to the config bean if also supporting non-spring frameworks with this? There are two a little bit ugly solutions:

  1. Why not supporting both by configuring the class as SpringBean as well as general singleton? This is kind of a hack, but you could set the workaround beans instance on bootstrap when the bean is created and read the spring config at this time.
  2. You could even just access the properties file in a static instance by the general properties interface of the jdk.

Both solutions do not provide any configuration at run-time, but at bootstrap time. However, I like them more to have the configuration in the general config in contrast to inherit some class to set one property. If we decide for this solution, we should also state in a comment in the properties, that to change this property, you have to restart the application.

Copy link
Member Author

Choose a reason for hiding this comment

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

I see your point and agree. Will need some more effort and rework but will be worth it. Then also the class connected with the spring config can be package visible and then also the name is less important. Further you only have to set a property in application.properties to activate the feature.

For the problem itself: The IN clause bind variable design is entirely flawed. An IN clause should actually take one parameter which is a list of values such as primary keys of type Long. However, it is not like this. Instead for each value in the list a separate bind variable is created. This already leads to bad performance and spoofing of statement and execution plan caches. DBs also limit the number of values in an IN clause. Actually that is for reason and therefore we do not support any workaround for it by default. In most cases this is an indicator that you are doing something odd in your queries. Still there are project situations where you have to workaround this limitations. If that is the case, you have to write a lot of code. That is what is offered here only for those that actually need it. But if you ever run into this ugly situation you will love it. Otherwise just ignore it.

@@ -0,0 +1,512 @@
package io.oasp.module.jpa.dataaccess.api;
Copy link
Member

Choose a reason for hiding this comment

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

I think we should extract the search pattern query support to a seperate util to also document in the class javadocs, that this is not for general use, but the newXClause functions come with a special use case.
Otherwise, you will end up in users writing their queries just which this provided functions, which would end up in a code mess, which cannot be understood easily as you cannot trace the query construction anymore.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry, but I do not really get this point. Should the QueryDslHelper class go to a different package, a different module (and package) or should it be split into multiple classes? Can you give a more concrete suggestion how to improve?

Copy link
Member

Choose a reason for hiding this comment

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

The QueryDslHelper contains more than just the newXClause methods. It also contains the pagination creation methods. The latter once might be access by programmers frequently. The newXClause methods however should not as they follow a very specific use case. So I would simply differentiate between both functionalities by seperating them in two different util classes.

Copy link
Member Author

Choose a reason for hiding this comment

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

Should then the separate class holding the newXClause methods extend the other class? Otherwise writing repositories gets unnecessary more complicated as you have to know where to find which method. I will give it a try this way. However, then one can still use the extended class in the same way as the current class QueryDslHelper as it is now anywhere. I know that separating aspects is a good idea in general. But this will also raise naming issues and might even lead to more complexity and confusion as the developers using this have to understand what to use when and how the relation between these separated classes is actually designed.
Currently QueryDslHelper is quite obvious: You get a lot of utility/helper methods that help you with QueryDsl. How should we name the extracted class?

Copy link
Member

Choose a reason for hiding this comment

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

However, then one can still use the extended class in the same way as the current class QueryDslHelper as it is now anywhere.

yes, maybe we are talking about a matter of taste. I just wanted to point out, that there is a certain probability of misuse of the newXClause. But as long as we want keeping inheritance as the way to probagate common API, there is no way out. I would have extracted this to a static util class as this functionality serves a very specific need, i.e. supporting advanced query syntax entered by a user. So why not point our users, who really need it to the util class?
Would even make the API a little bit clearer as the general context is defined by the util class. By using static imports, you even do not produce more unreadable in your dao methods.

query.where(Alias.$(orderPosition.getOrder().getId()).eq(orderId));
return query.list(alias);
EntityPathBase<OrderPositionEntity> alias = $(orderPosition);
JPAQuery<OrderPositionEntity> query = new JPAQuery<OrderPositionEntity>(getEntityManager()).from(alias);
Copy link
Member

Choose a reason for hiding this comment

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

duplicate type parameter declaration as also in other Dao's multiple times.
Use diamond operator.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, seems that my eclipse is misconfigured (in some cases it is also a java7 type inference issue but surely not here). I will fix this.

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually the entire restaurant sample is going to be deleted very soon. We should not make any fuss about this code. I just invest what is necessary to not break the build. See PR #629 - if that has been merged, we can start PR to delete restaurant.

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually you are even wrong here. In this case the Diamond Operator can not be used. This would lead to a compiler error. Yes, Java is sometimes sick.

<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
-->
Copy link
Member

Choose a reason for hiding this comment

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

remove unused code

Copy link
Member Author

Choose a reason for hiding this comment

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

yep


private final int maxSizeOfInClause;
@Value("${database.query.in-clause.max-values:2147483647}")
private int maxSizeOfInClause;

/** Die maximale Anzahl von Werten, welche Oracle in einer IN-Klausel verarbeiten kann. */
public static final int MAX_SIZE_OF_IN_CLAUSE_IN_ORACLE = 1000;
Copy link
Member

Choose a reason for hiding this comment

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

I think we do not need this field anymore as we cannot set it programmatically on runtime now.
Maybe worth to add it commented to a basic application.properties as well as the property itself also commented.

Copy link
Member Author

Choose a reason for hiding this comment

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

I do not see why we do not need this field anymore. Would you suggest rather to inject the entire spring boot application.properties as a Map or Environment instead and get it manually from there? IMHO using the @Value is more compact, more expressive and simpler. Also it is the suggested way to do it according to the spring documentation.

I do agree that we need some kind of documentation for this feature. Once this PR is agreed and merged, I would also provide documentation for the entire feature in the forge wiki. We could also add a comment to the application.properties of our archetype but as we have so many PRs open at the moment I would prefer to get things done and merged first. For nice-to-haves we should create issues and/or follow up PRs. Currently I feel like being in a kind of cage to get fast progress and improvements into OASP4J. Also the PR #629 for our archetype was a nightmare and turned again into a monster PR with tons of different aspects. However, we also fail to get PRs reviewed and merged quickly so in such cases it would even take way longer to create small PRs as I would need >10 PRs then and wait weeks up to moths for each to get merged while I am blocked for the follow up PR as it depends on the previous PR to be done.

@maybeec
Copy link
Member

maybeec commented Feb 28, 2018

Further, spring(-data) has its own support for paging. Should we also align something here?

IMHO, we should go with spring data paging support. Spring data already provides connectors to nearly ever database of interest. However, we should keep the existing mechanism but not make it compatible with spring data but keep it seperately. Thus, we can keep the protocol for more exotic projects or even for the case, where spring data may get deprectated.

@hohwille
Copy link
Member Author

hohwille commented Mar 8, 2018

IMHO, we should go with spring data paging support.

I fully agree. That means you vote for solution 1 or 3 (https://github.com/hohwille/oasp4j/wiki/paging). We need to raise a vote/poll in the community for this as this is IMHO a really important decision.
However, IMHO not something that would need to block this PR. We could actually easily merge this and other PRs first and then redo PR #630...
On the one side you are saying that PRs get too large on the other hand your review implies PRs to be "perfect" before merging. These two do not perfectly go together. Of course I do not want to merge things that bring "bad code" into our mainlines and I am happy for all the feedback even if it forces me to put even more effort into all my PRs. But still I think we need to do things a bit more light-weight and efficient as currently many open PRs somehow closely depend or at least influence on each other. Even in cases where we have "crap" on the mainline we argue in long rounds before we merge even though already the first state of the PR was a great improvement.

@maybeec
Copy link
Member

maybeec commented Apr 17, 2018

On the one side you are saying that PRs get too large on the other hand your review implies PRs to be "perfect" before merging. These two do not perfectly go together. Of course I do not want to merge things that bring "bad code" into our mainlines and I am happy for all the feedback even if it forces me to put even more effort into all my PRs. But still I think we need to do things a bit more light-weight and efficient as currently many open PRs somehow closely depend or at least influence on each other. Even in cases where we have "crap" on the mainline we argue in long rounds before we merge even though already the first state of the PR was a great improvement.

Totally agree. The issue here is that for large refactorings or rework, PRs are simply not the right tool. I also had issues with that at cobigen repository and thus switching to the "all commit to one branch" mode for a short time. In such a scenario as we have currently, PRs start to diverge all the time and you have to create merge commits all over the place, which already indicates the root cause.
IMHO, we should merge all strongly related PRs to the development branch in a controlled and intregrated way and continue work based on that. Whether working in a CI approach (all commit to one branch) or non-CI approach (PRs), depends on the workload.

@hohwille hohwille added this to the oasp:EVE milestone May 8, 2018
@hohwille
Copy link
Member Author

@maybeec
Copy link
Member

maybeec commented Jun 28, 2018

I don't think that the example of integrating QueryDSL and Spring Data as proposed here is a good example. It even hides database operations completely. I don't think it will be of help for developers. In the contrary, I think it would be easier to use a language like SQL or queryDSL in its original API to define queries.

Eventually, I think your implementation is a good compromise. For me this PR is fine.

@hohwille
Copy link
Member Author

hohwille commented Jul 4, 2018

I need 2.6.1 being released (#656 and #650) and merged onto develop. Then I will merge the changes into my PR and resolve the conflicts. Hope with all our improvements in the future we will gain more speed and thereby also reduce complex PR dependencies.

@hohwille
Copy link
Member Author

hohwille commented Jul 4, 2018

I don't think that the example of integrating QueryDSL and Spring Data as proposed ...

@maybeec thanks for your feedback. I have the same impression and it is good to see you came to the same point. However, one thing I learned from the example you linked is that you can create predicates without the need to reference the entitymanager and can then let the QueryDslPredicateExecutor perform the actual query. At least that confirmed my idea how that interface was meant to be used. Still I also agree that this approach is very limited compared to what the full QueryDSL API offers and what we now offer with our custom integration.

@vapadwal
Copy link
Contributor

vapadwal commented Jul 23, 2018

I tried to resolve the above conflicts locally, so that I could test it further, but after resolving it there still some modules like jpa-basics and jpa-dao that are removed in develop and that are still there in this PR

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants