Analysis of MyBatis Learning query mapping process

Source: Internet
Author: User
Tags flush static class throwable
1. Functional Architecture

MyBatis's functional architecture is divided into three tiers:

(1) API interface layer: interface APIs for external use, such as the DAO layer interface.

(2) Data processing layer: responsible for the specific SQL lookup, SQL parsing, SQL execution and execution result mapping processing and so on. Its primary purpose is to complete a database operation at the request of the call.

(3) Base support layer: responsible for the most basic functional support, including connection management, transaction management, configuration loading and caching processing, logs, these are common things, they are extracted out as the most basic components. Provides the most basic support for the data processing layer of the upper layer.

2. Core principle: Java Dynamic agent

Proxy mode is a common Java design pattern, its characteristic is that the proxy class and the delegate class have the same interface, the proxy class is mainly responsible for the delegate class preprocessing messages, filtering messages, forwarding messages to the delegate class, and post-processing messages. There is usually an association between the proxy class and the delegate class, and the object of a proxy class is associated with an object of a delegate class, and the object of the proxy class does not actually implement the service, but instead provides a specific service by invoking the related method of the object of the delegate class. Divided into dynamic agents and static agents.

The bytecode of the dynamic proxy class is dynamically generated by the Java reflection mechanism when the program is run, without the programmer writing its source code manually.

Implementation method: JDK Dynamic Proxy implementation

Relatively low efficiency, the proxy class needs to implement the corresponding interface Cglib dynamic proxy implementation

Relatively high efficiency, generating subclasses of the target class

public class Userproxy<t> implements Invocationhandler {private final sqlsession sqlsession;
 
    Private final class<t> Mapperinterface;
        Public Userproxy (sqlsession sqlsession, class<t> mapperinterface) {this.sqlsession = sqlsession;
    This.mapperinterface = Mapperinterface; } public object invoke (object proxy, Method method, object[] args) throws Throwable {System.out.println (met
        Hod.getname () + ":" + arrays.tostring (args));
    return null;
  
}} public interface Usermapper {UserInfo Getuserbyid (long id);} public class Proxytest {public static void main (string[] args) {userproxy userproxy = new Userproxy (null, NU
        ll); Usermapper Usermapper = (usermapper) proxy.newproxyinstance (ProxyTest.class.getClassLoader (), New Class[]{u
        Sermapper.class}, Userproxy);
    System.out.println (Usermapper.getuserbyid (1l)); }
}
Simple query process:

public class Orderinfotest {
    static Logger log = Logmanager.getlogger (Orderinfotest.class);
    public static void Main (string[] args) throws IOException {
        //load config file and get session
        String resource = "Mybatis-confi G.xml ";
        Sqlsessionfactory sessionfactory = new Sqlsessionfactorybuilder (). Build (Resources.getresourceasstream (Resource));
        sqlsession session = Sessionfactory.opensession ();
        Get an operable interface
        orderinfomapper orderinfomapper = Session.getmapper (Orderinfomapper.class);//Generate Dynamic proxy class
        //  Execute Query
        //OrderInfo OrderInfo = Session.selectone (" Mybatis.study.customer.mapper.OrderInfoMapper.getOrderInfoById ", 1L);
        OrderInfo OrderInfo = Orderinfomapper.getorderinfobyid (1l);
        System.out.println (Orderinfo.getmoney ());
    }
}

By invoking the DAO interface method, and using the Statementid identity directly, the result is consistent, and the middle is definitely a mapping relationship.

This explains why the namespace in the mapper file must be consistent with the fully qualified name of the DAO layer interface. Here's a look at the mapping process. 3.mapper (DAO) To session mapping process using Statementid queries 3.1 sqlsession Description of the main class of the creation process

Sqlsessionfactorybuilder: The data stream used to create the Sqlsessionfactory instance, build method into the parameter configuration file
Sqlsessionfactory is the factory interface that creates the sqlsession instance, and the implementation class has two

The default implementation is Defaultsqlsessionfactory, calling Opensession to get the session object, to manipulate

/** * * Get the environment, data source, transaction type from the configuration file to create sqlsession * * @param exectype Actuator Type * @param level transaction levels * @param autocommit Auto Commit * @return Session */Private sqlsession Opensessionfromdatasource (Executortype exectype, transactionisolationlevel level
    , Boolean autocommit) {Transaction tx = null;  Try {Final Environment environment = configuration.getenvironment ();  Get the configured environment final transactionfactory transactionfactory = gettransactionfactoryfromenvironment (Environment); The transaction factory for the environment, default transaction management Managedtransactionfactory tx = Transactionfactory.newtransaction (Environment.getdatasource (), le  Vel, autocommit);    New Transaction Object Final Executor Executor = Configuration.newexecutor (TX, exectype);  Build Actuator return new Defaultsqlsession (configuration, executor, autocommit);
        Returns the default sqlsession} catch (Exception e) {closetransaction (TX);  Throw Exceptionfactory.wrapexception ("Error opening session.
    Cause: "+ E, E); } finally {ErroRcontext.instance (). reset (); }
}

And Sqlsessionmanager, realizes the Sqlsessionfactory and sqlsession interface, directly has the function of the session, the internal package defaultsqlsessionfactory, It's a defaultsqlsessionfactory version of the building.

In short, through the above process to get an operable session, the most important of which is the construction of the configuration, the following description of the structure of the parsing process 3.2 config file parsing

As you can see from the code above, the parsing of the configuration file is implemented through Xmlconfigbuilder.

public class Xmlconfigbuilder extends Basebuilder {private Boolean parsed;
    Private final Xpathparser parser;
    Private String environment;
    Private final Reflectorfactory localreflectorfactory = new Defaultreflectorfactory ();
        Private Xmlconfigbuilder (xpathparser parser, String environment, Properties props) {Super (New Configuration ());
        Errorcontext.instance (). Resource ("SQL Mapper Configuration");
        This.configuration.setVariables (props);
        this.parsed = false;
        this.environment = environment;
    This.parser = parser;  The public Configuration of parse () {if (parsed) {throw new Builderexception ("Each Xmlconfigbuilder
        Can only be used once. ");}
        parsed = true;
        Parseconfiguration (Parser.evalnode ("/configuration"));
    return configuration; } private void Parseconfiguration (XNode root) {try {propertieselement (Root.evalnode ("Propertie  S ")); SolutionAnalysis Properties Settings = Settingsasproperties (Root.evalnode ("Settings"));
            Parse Set item LOADCUSTOMVFS (settings);   Typealiaseselement (Root.evalnode ("typealiases"));      Parse alias Pluginelement (Root.evalnode ("plugins"));    Parse plugin objectfactoryelement (Root.evalnode ("objectfactory"));   Parse Object Factory objectwrapperfactoryelement (Root.evalnode ("objectwrapperfactory"));  Objectwrapper Factory, the results can be carried out some special treatment reflectorfactoryelement (Root.evalnode ("reflectorfactory"));
            Reflector factory settingselement (settings);   Environmentselement (Root.evalnode ("Environments"));   Analytic Environment databaseidproviderelement (Root.evalnode ("Databaseidprovider"));    Parsing database provider Typehandlerelement (Root.evalnode ("typehandlers"));    Resolves the configured type processor mapperelement (Root.evalnode ("mappers")); Parse SQL Map file} catch (Exception e) {throw new Builderexception ("Error parsing SQL Mapper COnfiguration.
        Cause: "+ E, E); }
    }
}

By parsing the MyBatis configuration file and the mapper file,

1. Register the Mapper interface information with the Mapperregistry,//class<t> and proxy factory to the Mapper library

public class Mapperproxyfactory<t> {
 
  private final class<t> mapperinterface; 
  Private final Map<method, mappermethod> methodcache = new Concurrenthashmap<method, mappermethod> (); 
 
  Public mapperproxyfactory (class<t> mapperinterface) {
    this.mapperinterface = mapperinterface;
  }
 
  Public class<t> Getmapperinterface () {
    return mapperinterface;
  }
 
  Public Map<method, Mappermethod> Getmethodcache () {
    return methodcache;
  }
 
  @SuppressWarnings ("unchecked")
  protected T newinstance (mapperproxy<t> mapperproxy) {
    return (T) Proxy.newproxyinstance (Mapperinterface.getclassloader (), new class[] {mapperinterface}, Mapperproxy);
  }
 
  Public T newinstance (sqlsession sqlsession) {
    final mapperproxy<t> mapperproxy = new Mapperproxy<t> ( Sqlsession, Mapperinterface, Methodcache);
    Return newinstance (Mapperproxy);
  }
}

In this way, the dynamic proxy class is generated for Session.getmapper (Orderinfomapper.class), which corresponds to the

2.Statement information was registered to Mappedstatements

Public final class Mappedstatement {private String resource;
    private configuration configuration;
    Private String ID;
    Private Integer fetchsize;
    Private Integer timeout;
    Private StatementType StatementType;
    Private ResultsetType ResultsetType;
    Private Sqlsource Sqlsource;
    Private cache cache;
    Private Parametermap Parametermap;
    Private list<resultmap> resultmaps;
    Private Boolean flushcacherequired;
    Private Boolean UseCache;
    Private Boolean resultordered;
    Private Sqlcommandtype Sqlcommandtype;
    Private Keygenerator Keygenerator;
    Private string[] keyproperties;
    Private string[] KeyColumns;
    Private Boolean hasnestedresultmaps;
    Private String databaseId;
    Private Log Statementlog;
    Private Languagedriver Lang;
  
    Private string[] resultsets;
        Public Boundsql Getboundsql (Object parameterobject) {Boundsql boundsql = Sqlsource.getboundsql (Parameterobject); List<parametermapping> parametermappings = Boundsql.getparametermappings (); if (parametermappings = = NULL | | parametermappings.isempty ()) {boundsql = new Boundsql (Configuration, BOUNDSQ
        L.getsql (), Parametermap.getparametermappings (), parameterobject);
            } for (ParameterMapping pm:boundSql.getParameterMappings ()) {String rmId = Pm.getresultmapid ();
                if (rmId! = null) {Resultmap RM = Configuration.getresultmap (rmId);
                if (rm! = NULL) {hasnestedresultmaps |= rm.hasnestedresultmaps ();
    }}} return boundsql; }
}
3.3 Call Map

session gets Mapper, calls the configuration from the Mapper library to query to Mapperproxyfactory object, calling method, executing mapperproxy in the Invoke method

public class Mapperproxy<t> implements Invocationhandler, Serializable {private static final long serialversion
  UID = -6424540398559729838l;
  Private final sqlsession sqlsession;
  Private final class<t> Mapperinterface;
 
  Private final Map<method, mappermethod> Methodcache; Public Mapperproxy (sqlsession sqlsession, class<t> mapperinterface, Map<method, mappermethod> MethodCache
    ) {this.sqlsession = sqlsession;
    This.mapperinterface = Mapperinterface;
  This.methodcache = Methodcache; @Override public Object Invoke (object proxy, Method method, object[] args) throws Throwable {try {if (
      Object.class.equals (Method.getdeclaringclass ())) {return Method.invoke (this, args);
      } else if (Isdefaultmethod (method)) {return Invokedefaultmethod (proxy, method, args);
    }} catch (Throwable t) {throw exceptionutil.unwrapthrowable (t); } final Mappermethod Mappermethod = Cachedmappermethod(method);
  Return Mappermethod.execute (sqlsession, args);
    } Private Mappermethod Cachedmappermethod (method) {Mappermethod Mappermethod = Methodcache.get (method); if (Mappermethod = = null) {Mappermethod = new Mappermethod (Mapperinterface, method, Sqlsession.getconfiguration (
      ));
    Methodcache.put (method, Mappermethod);
  } return Mappermethod; }

Execute method for executing Mappermethod

public class Mappermethod {private final SqlCommand command;
 
  Private final Methodsignature method; Public Mappermethod (Class<?> mapperinterface, method, Configuration config) {this.command = new Sqlcomma
    nd (config, mapperinterface, method);
  This.method = new Methodsignature (config, mapperinterface, method);
    } public Object Execute (sqlsession sqlsession, object[] args) {object result;
        Switch (Command.gettype ()) {case INSERT: {Object param = method.convertargstosqlcommandparam (args);
        result = Rowcountresult (Sqlsession.insert (Command.getname (), param));
      Break
        } case UPDATE: {Object param = method.convertargstosqlcommandparam (args);
        result = Rowcountresult (Sqlsession.update (Command.getname (), param));
      Break
        } case DELETE: {Object param = method.convertargstosqlcommandparam (args); result = Rowcountresult (Sqlsession.delete (Command.getname (), param));
      Break } Case Select:if (Method.returnsvoid () && Method.hasresulthandler ()) {Executewithresulth
          Andler (sqlsession, args);
        result = NULL;
        } else if (Method.returnsmany ()) {result = Executeformany (sqlsession, args);
        } else if (Method.returnsmap ()) {result = Executeformap (sqlsession, args);
        } else if (Method.returnscursor ()) {result = Executeforcursor (sqlsession, args);
          } else {Object param = method.convertargstosqlcommandparam (args);
        result = Sqlsession.selectone (Command.getname (), param);
      } break;
        Case flush:result = sqlsession.flushstatements ();
      Break
    Default:throw new Bindingexception ("Unknown execution method for:" + command.getname ()); } if (result = = null && method.getreturntype (). Isprimitive () &&!method.returnsvoid ()) {throw n EW bindingexception ("MappeR method ' "+ command.getname () +" attempted to return null from a method with a primitive return type ("+ Met
    Hod.getreturntype () + ").");
  } return result; }
}
public static class SqlCommand {private final String name;
 
  Private final Sqlcommandtype type; Public SqlCommand (Configuration configuration, Class<?> Mapperinterface, method) {final String Methodnam
    E = Method.getname ();
    Final class<?> Declaringclass = Method.getdeclaringclass ();
    Mappedstatement ms = Resolvemappedstatement (Mapperinterface, MethodName, Declaringclass, configuration);
        if (ms = = null) {if (method.getannotation (flush.class) = null) {name = NULL;
      Type = Sqlcommandtype.flush; } else {throw new bindingexception ("Invalid bound statement (not found):" + mapperinterface.getname (
      ) + "." + MethodName);
      }} else {name = Ms.getid ();
      Type = Ms.getsqlcommandtype ();
      if (type = = Sqlcommandtype.unknown) {throw new bindingexception ("UNKNOWN execution method for:" + name); }
    }
  }
}

4. Summary

Mainly carding the next from the DAO interface to the MyBatis query process, explaining the MyBatis used dynamic proxy mode, the DAO interface method map to the session using the Statementid query process, MyBatis source involves a lot of factory class and construction class, Can learn from.





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.