目錄java
MyBatis是一個持久層框架,使用簡單,學習成本較低。能夠執行本身手寫的SQL語句,比較靈活。可是MyBatis的自動化程度不高,移植性也不高,有時從一個數據庫遷移到另一個數據庫的時候須要本身修改配置。mysql
一個Mybatis最簡單的使用列子以下:git
public class UserDaoTest { private SqlSessionFactory sqlSessionFactory; @Before public void setUp() throws Exception{ ClassPathResource resource = new ClassPathResource("mybatis-config.xml"); InputStream inputStream = resource.getInputStream(); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void selectUserTest(){ String id = "{0003CCCA-AEA9-4A1E-A3CC-06D884BA3906}"; SqlSession sqlSession = sqlSessionFactory.openSession(); CbondissuerMapper cbondissuerMapper = sqlSession.getMapper(CbondissuerMapper.class); Cbondissuer cbondissuer = cbondissuerMapper.selectByPrimaryKey(id); System.out.println(cbondissuer); sqlSession.close(); } }
本博客只涉及建立SessionFactory,以及從SessionFactory獲取SqlSession的流程。具體執行Sql的流程會在其餘博客中分析。github
ClassPathResource resource = new ClassPathResource("mybatis-config.xml"); InputStream inputStream = resource.getInputStream(); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
經過上面代碼發現,建立SqlSessionFactory的代碼在SqlSessionFactoryBuilder中,進去一探究竟:sql
//整個過程就是將配置文件解析成Configration對象,而後建立SqlSessionFactory的過程 //Configuration是SqlSessionFactory的一個內部屬性 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
下面咱們看下解析配置文件過程當中的一些細節。數據庫
先給出一個配置文件的列子:session
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--SqlSessionFactoryBuilder中配置的配置文件的優先級最高;config.properties配置文件的優先級次之;properties標籤中的配置優先級最低 --> <properties resource="org/mybatis/example/config.properties"> <property name="username" value="dev_user"/> <property name="password" value="F2Fa3!33TYyg"/> </properties> <!--一些重要的全局配置--> <settings> <setting name="cacheEnabled" value="true"/> <!--<setting name="lazyLoadingEnabled" value="true"/>--> <!--<setting name="multipleResultSetsEnabled" value="true"/>--> <!--<setting name="useColumnLabel" value="true"/>--> <!--<setting name="useGeneratedKeys" value="false"/>--> <!--<setting name="autoMappingBehavior" value="PARTIAL"/>--> <!--<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>--> <!--<setting name="defaultExecutorType" value="SIMPLE"/>--> <!--<setting name="defaultStatementTimeout" value="25"/>--> <!--<setting name="defaultFetchSize" value="100"/>--> <!--<setting name="safeRowBoundsEnabled" value="false"/>--> <!--<setting name="mapUnderscoreToCamelCase" value="false"/>--> <!--<setting name="localCacheScope" value="STATEMENT"/>--> <!--<setting name="jdbcTypeForNull" value="OTHER"/>--> <!--<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>--> <!--<setting name="logImpl" value="STDOUT_LOGGING" />--> </settings> <typeAliases> </typeAliases> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!--默認值爲 false,當該參數設置爲 true 時,若是 pageSize=0 或者 RowBounds.limit = 0 就會查詢出所有的結果--> <!--若是某些查詢數據量很是大,不該該容許查出全部數據--> <property name="pageSizeZero" value="true"/> </plugin> </plugins> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://10.59.97.10:3308/windty"/> <property name="username" value="windty_opr"/> <property name="password" value="windty!234"/> </dataSource> </environment> </environments> <databaseIdProvider type="DB_VENDOR"> <property name="MySQL" value="mysql" /> <property name="Oracle" value="oracle" /> </databaseIdProvider> <mappers> <!--這邊能夠使用package和resource兩種方式加載mapper--> <!--<package name="包名"/>--> <!--<mapper resource="./mappers/SysUserMapper.xml"/>--> <mapper resource="./mappers/CbondissuerMapper.xml"/> </mappers> </configuration>
下面是解析配置文件的核心方法:mybatis
private void parseConfiguration(XNode root) { try { //issue #117 read properties first //解析properties標籤,並set到Configration對象中 //在properties配置屬性後,在Mybatis的配置文件中就能夠使用${key}的形式使用了。 propertiesElement(root.evalNode("properties")); //解析setting標籤的配置 Properties settings = settingsAsProperties(root.evalNode("settings")); //添加vfs的自定義實現,這個功能不怎麼用 loadCustomVfs(settings); //配置類的別名,配置後就能夠用別名來替代全限定名 //mybatis默認設置了不少別名,參考附錄部分 typeAliasesElement(root.evalNode("typeAliases")); //解析攔截器和攔截器的屬性,set到Configration的interceptorChain中 //MyBatis 容許你在已映射語句執行過程當中的某一點進行攔截調用。默認狀況下,MyBatis 容許使用插件來攔截的方法調用包括: //Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) //ParameterHandler (getParameterObject, setParameters) //ResultSetHandler (handleResultSets, handleOutputParameters) //StatementHandler (prepare, parameterize, batch, update, query) pluginElement(root.evalNode("plugins")); //Mybatis建立對象是會使用objectFactory來建立對象,通常狀況下不會本身配置這個objectFactory,使用系統默認的objectFactory就行了 objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); //設置在setting標籤中配置的配置 settingsElement(settings); //解析環境信息,包括事物管理器和數據源,SqlSessionFactoryBuilder在解析時須要指定環境id,若是不指定的話,會選擇默認的環境; //最後將這些信息set到Configration的Environment屬性裏面 environmentsElement(root.evalNode("environments")); // databaseIdProviderElement(root.evalNode("databaseIdProvider")); //不管是 MyBatis 在預處理語句(PreparedStatement)中設置一個參數時,仍是從結果集中取出一個值時, 都會用類型處理器將獲取的值以合適的方式轉換成 Java 類型。解析typeHandler。 typeHandlerElement(root.evalNode("typeHandlers")); //解析Mapper mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
上面解析流程結束後會生成一個Configration對象,包含全部配置信息,而後會建立一個SqlSessionFactory對象,這個對象包含了Configration對象。oracle
下面是openSession的過程:app
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //獲取執行器,這邊得到的執行器已經代理攔截器的功能(見下面代碼) final Executor executor = configuration.newExecutor(tx, execType); //根據獲取的執行器建立SqlSession return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
//interceptorChain生成代理類,具體參見Plugin這個類的方法 public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
到此爲止,咱們已經得到了SqlSession,拿到SqlSession就能夠執行各類CRUD方法了。
對於MyBatis啓動的流程(獲取SqlSession的過程)這邊簡單總結下:
SQL的具體執行流程見後續博客。
一些重要類總結:
//TypeAliasRegistry registerAlias("string", String.class); registerAlias("byte", Byte.class); registerAlias("long", Long.class); registerAlias("short", Short.class); registerAlias("int", Integer.class); registerAlias("integer", Integer.class); registerAlias("double", Double.class); registerAlias("float", Float.class); registerAlias("boolean", Boolean.class); registerAlias("byte[]", Byte[].class); registerAlias("long[]", Long[].class); registerAlias("short[]", Short[].class); registerAlias("int[]", Integer[].class); registerAlias("integer[]", Integer[].class); registerAlias("double[]", Double[].class); registerAlias("float[]", Float[].class); registerAlias("boolean[]", Boolean[].class); registerAlias("_byte", byte.class); registerAlias("_long", long.class); registerAlias("_short", short.class); registerAlias("_int", int.class); registerAlias("_integer", int.class); registerAlias("_double", double.class); registerAlias("_float", float.class); registerAlias("_boolean", boolean.class); registerAlias("_byte[]", byte[].class); registerAlias("_long[]", long[].class); registerAlias("_short[]", short[].class); registerAlias("_int[]", int[].class); registerAlias("_integer[]", int[].class); registerAlias("_double[]", double[].class); registerAlias("_float[]", float[].class); registerAlias("_boolean[]", boolean[].class); registerAlias("date", Date.class); registerAlias("decimal", BigDecimal.class); registerAlias("bigdecimal", BigDecimal.class); registerAlias("biginteger", BigInteger.class); registerAlias("object", Object.class); registerAlias("date[]", Date[].class); registerAlias("decimal[]", BigDecimal[].class); registerAlias("bigdecimal[]", BigDecimal[].class); registerAlias("biginteger[]", BigInteger[].class); registerAlias("object[]", Object[].class); registerAlias("map", Map.class); registerAlias("hashmap", HashMap.class); registerAlias("list", List.class); registerAlias("arraylist", ArrayList.class); registerAlias("collection", Collection.class); registerAlias("iterator", Iterator.class); registerAlias("ResultSet", ResultSet.class);
https://blog.csdn.net/luanlouis/article/details/40422941