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

IncompleteElementException occurs temporarily when Spring Application starts #696

Open
be-hase opened this issue Jul 21, 2022 · 6 comments

Comments

@be-hase
Copy link

be-hase commented Jul 21, 2022

MyBatis version

mybatis: 3.5.10
mybatis-spring-boot-starter: 2.2.2
org.mybatis:mybatis-spring:2.0.7

Database vendor and version

mysql 5.7.27
(But this issue is not related to mysql)

Test case or example project

It's a little difficult, so let me just report first.
If my explanation is confusing, I'll do my best to write a small sample code.

Steps to reproduce

In my application, I use mapper definition with annotation like below.

@Select("... ommited ...")
@Results(id = "someId", ... ommited ...)
fun select1(...): SomePojo?
 
@Select("... ommited ...")
@ResultMap("someId")
fun select2(...): SomePojo?

When loading this Mapper Interface, the order returned by Java Class.getMethods is undefined, so incompleteMethods occurs in this process.
https://github.com/mybatis/mybatis-3/blob/84cc6a1c1682fd5619348c6eb3a6378f69483ddf/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java#L123-L136

Subsequent parsePendingMethods resolve this incompleteMethods.
https://github.com/mybatis/mybatis-3/blob/84cc6a1c1682fd5619348c6eb3a6378f69483ddf/src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java#L138

However, other mepper Interface methods may be executed at other threads before incompleteMethods are resolved.
At this time, an error(IncompleteElementException) will occur because incompleteMethods exists.
https://github.com/mybatis/mybatis-3/blob/84cc6a1c1682fd5619348c6eb3a6378f69483ddf/src/main/java/org/apache/ibatis/session/Configuration.java#L848-L853
https://github.com/mybatis/mybatis-3/blob/84cc6a1c1682fd5619348c6eb3a6378f69483ddf/src/main/java/org/apache/ibatis/session/Configuration.java#L918-L925

Normally, all DI of spring is done in main thread.
However, if we prepare an instance that executes Mapper Interface in the background thread in DI processing, we will encounter the above problem.

Expected result

The method call of Mapper Interface succeeds without error.

Actual result

_

@harawata
Copy link
Member

Hello @be-hase ,

It seems logically impossible to guarantee 'no-error' with your usage (i.e. a thread calling a statement while the main thread is loading mappers).
The reported case is about loading order within a single mapper, but it is theoretically possible that the result map referenced in @ResultMapis defined in another mapper which is not loaded yet, for example.
Or, am I missing something?

I'm not a Spring guru, but there might be some Spring side solution which ensures the background thread waits for the main thread's task completion.
It could also be a viable workaround for the background thread to simply retry after a while, assuming that the error is temporary.

@be-hase
Copy link
Author

be-hase commented Jul 21, 2022

@harawata
Thank you for reply.
Yes, I think it can be avoided by devising a spring layer.

However, in my opinion, incomplete instances should not be registered in DI.
mybatis-spring should register in DI once it is ready for use. And once registered in DI, it should be usable correctly after that.

BTW, should I make an issue here and have a discussion? I think it's a spring integration issue, not a mybatis-core issue.
https://github.com/mybatis/spring

@harawata
Copy link
Member

Please give me some time to think about this.
It might take a while.

@tokuhirom
Copy link

If my understanding is correct, the problem is...
The Configuration object becomes
incomplete state when the user calls addMapper from another thread
. This problem is hard to solve without drastic architecture change.
Making configuration object as an immutable is too hard at this time(This is a major change that would be implemented at the time of a major version upgrade).

BTW, adding eager loading feature to the mybatis-spring is easier than changing mybatis-core. I want an eager loading option~

@harawata
Copy link
Member

@tokuhirom ,

Having incomplete statements is part of the normal process and it could happen even when the mappers are loaded by a single thread.

Let's say there are two mappers:

public interface FirstMapper {
  @Select("...")
  @ResultMap("mapper.SecondMapper.someResultMap")
  SomePojo select1();
}

public interface SecondMapper {
  @Select("...")
  @Results(id = "someResultMap", ...)
  SomePojo select2();
}

Assuming that the FirstMapper is loaded first, the statement FirstMapper.select1 becomes incomplete after the FirstMapper is loaded because the specified result map mapper.SecondMapper.someResultMap does not exist in the registry yet.
Once the SecondMapper is loaded (and Configuration#buildAllStatements() is called), the statement will be 'completed'.

In a multi-threaded situation, another thread could call FirstMapper.select1 before SecondMapper is loaded and then an exception is thrown.

The fundamental difficulty is that there is no way to know if all mappers are loaded or not (note that addMapper could be called at any time, even from a user's code).
So, it probably does not work if you try to load statements eagerly, but please let me know if I misunderstood what you mean.


I explored an idea of blocking the getStatement() call for certain amount of time, but it probably does more harm than good.

I'll transfer the issue to MyBatis-Spring as it seems to be the main concern here.
If anyone comes up with a solution or idea, feel free to chime in.

@harawata harawata transferred this issue from mybatis/mybatis-3 Jul 30, 2022
@tokuhirom
Copy link

@harawata

by the way, I came up with an idea.
For an IncompleteElementException mybatis might use a message like "This error is usually caused by registering a Mapper from another thread while MyBatis is in use" to help with troubleshooting.

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

No branches or pull requests

3 participants