MyBatis the spring boot alias bug could not be scanned

Source: Internet
Author: User
Tags aliases

The reason for this problem is more complex, with 4 main conditions: Using spring boot and using the MAVEN plug-in of Spring boot to package the use of MyBatis (the current 3.3.1 version still has this problem) Configure domain configuration in a separate jar package (for example, maven multiple modules) using Sqlsessionfactorybean.settypealiasespackage to specify packet scanning domain

Then you'll find that when you use idea directly to execute the main method at development time, everything works, but the domain alias that is configured when you use Java-jar to start the jar package will fail.

For example, I have a spring boot project, which is divided into three maven modules:

Scienjus
----scienjus-domain
--------com.scienjus.domain.User
----scienjus-mapper
-------- Com.scienjus.mapper.UserMapper
--------usermapper.xml
----scienjus-web
-------- Sqlsessionfactoryconfig

Configure Sqlsesstionfactory in Sqlsessionfactoryconfig:

@Bean public
sqlsessionfactory sqlsessionfactory () throws Exception {
    final Sqlsessionfactorybean Sqlsessionfactory = new Sqlsessionfactorybean ();
    Sqlsessionfactory.setdatasource (DataSource ());
    Configure Alias
    sqlsessionfactory.settypealiasespackage ("Com.scienjus.domain");
    return Sqlsessionfactory.getobject ();
}

Use aliases in Usermapper.xml:

<select id= "Get" resulttype= "user" >
    select  * from user u where id = #{id}
</select>

Using idea to start everything will work, but if you wait until the runtime starts at the command line, you will receive the following error message:

Org.apache.ibatis.builder.BuilderException:Error resolving class. Cause:
org.apache.ibatis.type.TypeException:Could not resolve type alias ' User '.  Cause:
java.lang.ClassNotFoundException:Cannot Find Class:user

The general meaning of this error is to generate mapper error, because the user is unable to recognize the alias, and can not find the user class. You can see that the previous package scan did not scan the Com.scienjus.domain.User class at all.

To prove this, I flipped the mybatis source, and then in Org.apache.ibatis.type.TypeAliasRegistry's Registeraliases (String PackageName, Class Supertype) method has found out how MyBatis scans the alias class through the package name. Move this part of the logic directly to the main method to perform a trial:

public static void Main (string[] args) {
    Resolverutil resolverutil = new Resolverutil ();
    Resolverutil.find (New IsA (Object.class), "Com.scienjus.domain");
    Set typeset = resolverutil.getclasses ();
    Iterator i$ = Typeset.iterator ();

    while (I$.hasnext ()) {
        Class type = (Class) I$.next ();
        if (!type.isanonymousclass () &&!type.isinterface () &&!type.ismemberclass ()) {
            System.out.println (Type.getname ());}}

Executed in idea and Jar respectively, it will be found that the former will print out the Com.scienjus.domain.User, the latter does not have any output results, indicating that the problem is here.

Now that you've locked down the problem, you can take a closer look at how it happened. View the Resolverutil.find method, which obtains the class file through the Vfs.getinstance (). List (path) method, and Vfs.getinstance () returns DEFAULTVFS by default, This means that the list method for this class cannot be scanned into a class in the Spring boot dependency jar package.

Further refine the call logic, you can prepare the breakpoint debugging:

public static void Main (string[] args) throws IOException {
    Defaultvfs defaultvfs = new Defaultvfs ();
    List children = defaultvfs.list ("Com/scienjus/domain");
    for (String child:children) {
        System.out.println (child);
    }
}

Breakpoint debugging using Java-jar-initiated programs is not as difficult as it might be, and the idea and eclipse are built with very good debugging tools, a little bit about how to use idea:

Turn on debug mode to run the jar pack and listen for a specific port:

Java-xdebug-xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=y-jar Scienjus-web.jar

The idea side creates a remote application in the run-> Edit configurations, fills in the IP and listening port number, and then launches it.

Debugging through breakpoints I found a more interesting piece of code in Findjarforresource:

If the file part of the URL is itself a URL, then that URL probably points to the JAR
try {for
  (;;) {
    url = new URL (url.getfile ());
    if (log.isdebugenabled ()) {
      log.debug ("Inner url:" + URL);}}
catch (Malformedurlexception e) {//This'll happen at some point and serves as a break in the
  loop
}

This is a dead loop that only jumps out of the loop when throwing malformedurlexception exceptions, and according to the comments above we can tell that this is inevitable and will point the URL to a desired result. Compare the two ways of running the URL at the end of the result:

Idea in direct operation:

Scienjus-domain/target/classes/com/scienjus/domain

Command line run:

Scienjus-web/target/scienjus-web.jar!/lib/scienjus-domain.jar!/com/scienjus/domain

The value of the variable jarurl is then assigned to Scienjus-web/target/scienjus-web.jar!/lib/scienjus-domain.jar, but the last Listresources method returns null.

The comment that calls this method is this:

The URL of a JAR file containing the requested resource. If a jar
//file is found, then we'll list child resources by reading the jar.

That is, if the scanned file is indeed in a jar package, this method should return the URL of the jar package, and try a more brutal improvement:

public static void Main (string[] args) throws IOException {
    Defaultvfs defaultvfs = new Defaultvfs () {
        @Override 
  protected url findjarforresource (url url) throws malformedurlexception {
            String urlstr = url.tostring ();
            if (Urlstr.contains ("jar!")) {return
                new URL (urlstr.substring (0, Urlstr.lastindexof ("jar") + "jar". Length ());
            }
            return Super.findjarforresource (URL);
        }
    ;
    List children = defaultvfs.list ("Com/scienjus/domain");
    for (String child:children) {
        System.out.println (child);
    }
}

If the URL contains a jar! identifier, the address of the jar package is returned directly. I'm not sure if this is a problem, but I only use this class when I scan domain aliases, and this is normal work.

Now that this class works, just set it as the default VFS. The MyBatis document says that you can change the VFS implementation class by changing the Vfsimpl property of the configuration file, and I have no effect with this configuration because the spring configuration is performed before the MyBatis configuration file, so before reading this configuration Vfs.getinstall () has already been instantiated. Then I gave MyBatis a issue, by the way also found that this scan is not a class of bugs as early as last October was proposed, there is already a solution, just need to 3.4.1 version will not be released.

MyBatis's official solution is to first recommend the use of Mybatis-spring-boot version 1.0.1, which has been configured with a VFS implementation class that is compatible with spring boot. or add the implementation class to your project and configure it manually.

In order not to let these blind toss in vain, I decided to release the whole process, teaching you how to use open source project encountered Bugs when the (Zuo) bit (SI), which may also be the only point of the value of this article.


Article from: http://www.scienjus.com/mybatis-vfs-bug/

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.