question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

TypeHandler incorrectly selects super classes/interfaces

See original GitHub issue

MyBatis version

3.5.8 (any version above 3.4.2)

Database vendor and version

Any (tested on Postgres 12.7 and latest JDBC, but also fails in testing framework)

Test case or example project

https://github.com/matthewmillett-instaclustr/mybatis-issues/tree/master/gh-2427

Steps to reproduce

Run tests

Expected result

shouldGetAUser() passes, by using the constructor within the User class to construct the User object

shouldGetAnAccount() passes (regression check)

Actual result

shouldGetAUser() fails with the exception java.lang.ClassCastException: class test.UserPrimaryKey cannot be cast to class test.User (test.UserPrimaryKey and test.User are in unnamed module of loader 'app')

shouldGetAnAccount() passes (regression check)

Explanation

I believe that this issue was introduced with https://github.com/mybatis/mybatis-3/issues/604 (PR https://github.com/mybatis/mybatis-3/pull/859). Before 3.4.2, Mybatis would use the type User, and find the constructor within the User class to build the result object. However, in 3.4.2 and above, Mybatis attempts to search the super class UserPrimaryKey, and uses the constructor within the UserPrimaryKey class to build a User object. It builds the UserPrimaryKey object fine, but then hits an error when attempting to cast it to the expected result object of User.

Sample fix

This is far from an ideal solution, but proves to show that the above explanation holds true.

Within org.apache.ibatis.type.TypeHandlerRegistry#getJdbcHandlerMapForSuperclass, if we add the following check after the call to clazz.getSuperclass(), the code works as intended:

if (clazz.getSuperclass() != null && clazz.getSuperclass().getName().contains("PrimaryKey")) {
        return typeHandlerMap.get(clazz);
}

An alternative way of resolving this may be to check for assignability within the above function, and only if the super class is assignable to the subclass then return the typeHandlerMap of the super class. I’m not sure how backwards compatible this would be, and/or if it would work.

Other

I’m pretty sure this is not a configuration problem, but if it is, then please let me know. If it can be resolved with the correct annotations within the mapper, I will be willing to give it a go. I couldn’t see any other related tickets with a similar issue. I know the hierarchy of User extending UserPrimaryKey implementing PrimaryKey is a bit odd, but modifying our inheritance/implementation structure is not an option.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:1
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
harawatacommented, Jan 23, 2022

Hi @matthewmillett-instaclustr ,

I’m sorry about the slow response and thank you for the PR!

When your type handler is not applicable to the subclasses of the explicitly-mapped type, the type handler should not be registered globally. Instead, you need to specify typeHandler attribute explicitly.

The test passes if you change the mapping of Mapper#getCompanyByName() as follows.

@Select("...")
@ConstructorArgs({
  // ...
  @Arg(
    column = "main_user", 
    javaType = UserPrimaryKey.class, 
    typeHandler = UserPrimaryKeyHandler.class)
})
Company getCompanyByName(@Param("companyName") final String companyName);

I took a look at #2430 , but the scope of the underlying issue is wider and the proposed fix addresses only a part of it.

0reactions
matthewmillett-instaclustrcommented, Jan 24, 2022

Hi @harawata,

Thanks for the suggestion to use typeHandler = ... within ConstructorArgs. After modifying our codebase quite a bit we’ve managed to get things mostly working, although it’s not as clean as we’d hoped.

I’ve spoken with my team, and our reasoning behind the change proposed in #2430 was that when the constructor args are specified explicitly there should be no need to be searching for a typehandler. Additionally, overriding the explicitly specified constructor args doesn’t seems like desirable behaviour.

If that reasoning is incorrect or there are deeper implications of the proposed change we are happy to investigate further and work with the team to try to come up with a sound solution. We understand that changes such as these can lead to backwards compatibility issues for people who use the library; however, at the same time, resolving these issues assists with reducing technical debt, and improves the developer experience.

Let us know how the above sounds.

Thanks

Read more comments on GitHub >

github_iconTop Results From Across the Web

IntelliJ IDEA 2019.1 BETA (191.5849.21 build) Release Notes
No subsystem Feature IDEA‑138797 No subsystem Feature IDEA‑142157 No subsystem Feature IDEA‑192898 No subsystem Feature IDEA‑111333
Read more >
Uses of Class java.lang.Object (GWT 2.8.2)
A request for Super Dev Mode to compile something. static class, JobChangeListener. A callback for receiving events when a GWT compile job changes...
Read more >
Elgg Documentation - Read the Docs
Well-documented core API that allows developers to kick start their new project with a simple learning curve. • Composer is the package ...
Read more >
Oracle certified professional java se 7 programmer exams 1z0 ...
Select relevant exam from the list, say, “1Z0-804 (Java SE 7 Programmer II),” and ... The other three are incorrect for the following...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found