Skip to content
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

Interface-based projection mixes data order [DATAJPA-1247] #1582

Closed
spring-projects-issues opened this issue Jan 19, 2018 · 9 comments
Closed

Interface-based projection mixes data order [DATAJPA-1247] #1582

spring-projects-issues opened this issue Jan 19, 2018 · 9 comments
Assignees
Labels
in: core status: declined type: bug

Comments

@spring-projects-issues
Copy link

@spring-projects-issues spring-projects-issues commented Jan 19, 2018

Antti Huokko opened DATAJPA-1247 and commented

When using Interface-based projection with native query Spring generates the data proxy with data bind to incorrect getters. The problem is caused by the fact that the code that binds data to the proxy properties relies on the order. The logic that combines property names and data is in ResultProcessor inner class called ProjectingConverter. There in the toMap(...) method the property names and values are combined to a Map. The actual source of the problem is where the property names are obtained. The property names are obtained using BeanUtils.getPropertyDescriptors(Class). The problem here is that Java does not guarantee any order for the property descriptors that it returns. This means that basically each time application is started we might get different property order. As a result of this the properties of the projection will experience some very odd random behavior as data is bind to incorrect fields.

To me this sounds like a major bug that will cause unpredictable and hard-to-reproduce problems. For instance if we have a projection interface consisting of 3 int getters this bug will cause data field to switch place at random without any runtime exceptions. This is a source of some very hard to find and hard to reproduce bugs in application logic.


Affects: 1.11.9 (Ingalls SR9)

Issue Links:

  • DATAJPA-1209 Compatibility with Hibernate < 5.2.11 broken for projections on native queries

  • DATACMNS-1206 Introduce abstraction to detect declared methods in declaration order

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jan 19, 2018

Oliver Drotbohm commented

That's a sort of known problem but you can usually work around this by using explicit aliases in the query definition. Derived queries actually use those so that the problem shouldn't occur for those. DATACMNS-1206 is approaching that problem and making sure we read the property definitions in projection interfaces in stable order.

Just to make sure I completely understand your particular scenario, would you mind giving a concrete example (including samples of the projection definition and query method declaration) of the situation you're running into this problem?

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jan 19, 2018

Antti Huokko commented

I would actually prefer using aliases with projections but I did not find any code (in Spring) that would actually use then when making the projection for native queries. This made be believe that aliases can't be used with native query projections. Here are some example code how I tested this. In example there are 2 queries: one that uses aliases and one that does not. However the result of the projection is same for both queries. Sorry for the finnish language in database query :)

import java.time.LocalDateTime;

public interface TestProjection {

    long getId();

    int getVersion();

    LocalDateTime getTime();

    String getState();

}

The query methods that I have in the JpaRepository

@Query(value="SELECT hak.kihak_id, hak.versio, hak.luonti_pvm, hak.hakemustila FROM kiireellinen_hakemus hak WHERE hak.haetun_aikataulun_lahtoaika > :time", nativeQuery=true)
List<TestProjection> getTestProjection1(LocalDateTime time);

@Query(value="SELECT hak.kihak_id AS id, hak.versio AS version, hak.luonti_pvm AS time, hak.hakemustila AS state FROM kiireellinen_hakemus hak WHERE hak.haetun_aikataulun_lahtoaika > :time", nativeQuery=true)
List<TestProjection> getTestProjection2(LocalDateTime time);

Code in the service that uses the test repository to make the queries

List<TestProjection> res1 = testRepository.getTestProjection1(LocalDateTime.of(2018, 1, 1, 12, 0));
System.out.println(res1.get(0));  // {id=541626, state=3, time=2017-12-28 09:25:04.0, version=Hyväksytty} => version should be int not String

List<TestProjection> res2 = testRepository.getTestProjection2(LocalDateTime.of(2018, 1, 1, 12, 0));
System.out.println(res2.get(0));  // {id=541626, state=3, time=2017-12-28 09:25:04.0, version=Hyväksytty} => version should be int not String

Excellent news that the order problem will be fixed in the future version.

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jan 19, 2018

Oliver Drotbohm commented

I was thinking of using explicit aliases in the query, yes. So you're saying that the second query declaration really has the result mixed up, too? You shouldn't actually see a List of values being handled in the processor but a Map or a Tuple.

One thing I am curious about is what Hibernate version you're using? in DATAJPA-1209, we identified an issue that caused us to not be able to use Tuple when we should have but that required Hibernate 5.2 to actually benefit from the fix on our side

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jan 19, 2018

Antti Huokko commented

Also the query with aliases (getTestProjection2) produces the projection with incorrect property order. Also when I debug the case I see that the execution of code goes to the ProjectingConverter.toMap(...) method that is the source of the incorrect order. To me it seems that using or not using the aliases don't have any effect. The version of Hibernate that we are using is 5.2.10

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jan 19, 2018

Oliver Drotbohm commented

Okay, I just ran the test case we have for that scenario on a Hibernate 5.2, and even without explicitly defined aliases I see the following things:

  • NativeJpaQuery.createJpaQuery(…) sees a resultType of Tuple and thus calls EntityManager.createNativeQuery(…) forwarding Tuple.
  • ChainingConverter uses the Tuple handling converter to turn the Tuple into a map keyed by the aliases.
  • the created HashMap is the correctly proxied into the projection interface.

Would you mind following those corner stones and see where things start to diverge?

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jan 19, 2018

Antti Huokko commented

For me the code starts to diverge in first step. For me the

NativeJpaQuery.createJpaQuery(…) sees a resultType of null

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jan 19, 2018

Oliver Drotbohm commented

Ha! The fix in Hibernate is in 5.2.11 and newer (see our explicit check for that). Would you mind trying that?

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented Jan 19, 2018

Antti Huokko commented

I just found the same check. I updated Hibernate to version 5.2.12 and now thing start to work. The native query with aliases now return projection with correct order. Thank you for your help solving this!

@spring-projects-issues
Copy link
Author

@spring-projects-issues spring-projects-issues commented May 22, 2019

Jens Schauder commented

Batch closing resolved issue without a fix version and a resolution indicating that there is nothing to release (Won't fix, Invalid ...)

@spring-projects-issues spring-projects-issues added type: bug status: declined in: core labels Dec 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core status: declined type: bug
Projects
None yet
Development

No branches or pull requests

2 participants