本文主要介紹Java中,不使用XML和使用XML構建SqlSessionFactory,通過SqlSessionFactory 中獲取SqlSession的方法,使用SqlsessionManager管理Sqlsession復用等等..以及相關的示例代碼
SqlSession
SqlSessions 是由 SqlSessionFactory 實體創建的,SqlSessionFactory 物件包含創建 SqlSession 實體的各種方法,而 SqlSessionFactory 本身是由 SqlSessionFactoryBuilder 創建的,它可以從 XML、注解或 Java 配置代碼來創建 SqlSessionFactory,
使用 MyBatis 的主要 Java 介面就是 SqlSession,你可以通過這個介面來執行命令,獲取映射器示例和管理事務,在介紹 SqlSession 介面之前,我們先來了解如何獲取一個 SqlSession 實體,
舉個例子

public class Ttest {
private Long id;
private String context;
....
}
TestMapper.java
public interface TestMapper {
Ttest getOne(Long id);
}
TestMapper.xml
<mapper namespace="com.liangtengyu.mapper.TestMapper">
<select id="getOne" resultType="com.liangtengyu.entity.Ttest">
select * from t_test where id = #{id}
</select>
</mapper>
mybatis-config.xml
<?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>
<!--開啟日志輸出-->
<settings>
<setting name="logImpl" value="https://www.cnblogs.com/java-bible/p/STDOUT_LOGGING" />
</settings>
<!--配置類別名,配置后在Mapper組態檔(通常我們將撰寫SQL陳述句的組態檔成為Mapper組態檔)中需要使用pojo包中的類時,使用簡單類名即可-->
<typeAliases>
<package name="com.liangtengyu.entity"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="https://www.cnblogs.com/java-bible/p/com.mysql.jdbc.Driver"/>
<property name="username" value="https://www.cnblogs.com/java-bible/p/root"/>
<property name="password" value="https://www.cnblogs.com/java-bible/p/123456"/>
<property name="url" value="https://www.cnblogs.com/java-bible/p/jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.liangtengyu.mapper"/>
</mappers>
</configuration>
來個測驗方法:
@Test
public void testMyBatisBuild() throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = factory.openSession();
TestMapper mapper = sqlSession.getMapper(TestMapper.class);
Ttest one = mapper.getOne(1L);
System.out.println(one);
sqlSession.close();
}
運行測驗方法,控制臺列印日志:
Checking to see if class com.liangtengyu.mapper.TestMapper matches criteria [is assignable to Object]
Opening JDBC Connection
Created connection 2083117811. //創建的連接名
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7c29daf3]
==> Preparing: select * from t_test where id = ?
==> Parameters: 1(Long)
<== Columns: id, context
<== Row: 1, 123
<== Total: 1
Ttest{id=1, context='123'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7c29daf3]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@7c29daf3]
Returned connection 2083117811 to pool. //用完了又放回連接池中
SqlSessionFactoryBuilder 創建出SqlSessionFactory,然后從SqlSessionFactory中得到SqlSession,最后通過SqlSession得到Mapper介面物件進行資料庫操作,
我們打個斷點.來跟蹤SqlSessionFactoryBuilder的源代碼:

F7跟進 發現一堆build 而我們現在用的是傳入reader的那個方法

我們可以看到,他幫我們傳了2個Null引數給下一個build,我們跟著這個build繼續往下跟.

這個build會將xml決議.然后呼叫parser.parse()方法將xml轉化成Configuration,傳入下一個build
繼續下一個build

這個build終于干了我們最關心的事,他創建了DefaultSqlSessionFactory 回傳SqlSessionFactory.
我們進來看看這個類:
public class DefaultSqlSessionFactory implements SqlSessionFactory {
//它是SqlSessionFactory的實作類.
private final Configuration configuration;
//通過傳入一個Configuration 來構造
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
...
這樣一看,那我們直接給它來個configuration不就可以創建一個SqlSessionFactory嗎.
我們來試試
//使用傳入Configuration方式創建SqlSessionFactoryBuilder
@Test
public void testMyBatisBuild1() throws IOException {
DataSource datasource = getDatasource();//首先創建資料源
Environment e = new Environment("test", new JdbcTransactionFactory(), datasource);//傳入datasource和JdbcTransactionFactory
Configuration configuration = new Configuration();//構建一個Configuration
configuration.setEnvironment(e);
configuration.setLogImpl(StdOutImpl.class);//使用控制臺輸出日志實作
configuration.getTypeAliasRegistry().registerAlias(Ttest.class);
configuration.addMapper(TestMapper.class);
//傳入configuration
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(configuration);
SqlSession sqlSession = build.openSession();
TestMapper mapper = sqlSession.getMapper(TestMapper.class);
Ttest one = mapper.getOne(1L);
System.out.println(one);
sqlSession.close();
}
//獲取資料源方法
public UnpooledDataSource getDatasource(){
UnpooledDataSource unpooledDataSource = new UnpooledDataSource();
unpooledDataSource.setUrl("jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8");
unpooledDataSource.setDriver("com.mysql.jdbc.Driver");
unpooledDataSource.setUsername("root");
unpooledDataSource.setPassword("123456");
return unpooledDataSource;
}
運行結果:
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
Opening JDBC Connection
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3dd4520b]
==> Preparing: select * from t_test where id = ?
==> Parameters: 1(Long)
<== Columns: id, context
<== Row: 1, 123
<== Total: 1
Ttest{id=1, context='123'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3dd4520b]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3dd4520b]
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(configuration);
//在構造SqlSessionFactory時實際呼叫的還是DefaultSqlSessionFactory 所以我們直接使用
DefaultSqlSessionFactory factory = new DefaultSqlSessionFactory(configuration);
//也是一樣的效果

那么他的內部是如何創建session的我們來看看源代碼
根據斷點我們到了factory.opensession()方法 F7進入方法

F8單步 發現呼叫了openSessionFromDataSource方法

三個引數.第一個引數是configuration.getDefaultExecutorType()
這個引數是Configuration類中定義的默認型別.
ExecutorType

package org.apache.ibatis.session;
//還有其它 的型別 如下.
/**
* @author Clinton Begin
*/
public enum ExecutorType {
SIMPLE, REUSE, BATCH
}
大家可能對 ExecutorType 引數感到陌生,這個列舉型別定義了三個值:
ExecutorType.SIMPLE:該型別的執行器沒有特別的行為,它為每個陳述句的執行創建一個新的預處理陳述句,
ExecutorType.REUSE:該型別的執行器會復用預處理陳述句,
ExecutorType.BATCH:該型別的執行器會批量執行所有更新陳述句,如果 SELECT 在多個更新中間執行,將在必要時將多條更新陳述句分隔開來,以方便理解,這里不再深入討論
level
是稱為 TransactionIsolationLevel,
事務隔離級別支持 JDBC 的五個隔離級別(NONE、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ 和 SERIALIZABLE),并且與預期的行為一致,
autoCommit
向 autoCommit 可選引數傳遞 true 值即可開啟自動提交功能
繼續往下 進到openSessionFromDataSource方法
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);
return new DefaultSqlSession(configuration, executor, autoCommit);//回傳DefaultSqlSession
} 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();
}
}
回傳的DefaultSqlSession實作了SqlSession的所有方法

我們進入到Sqlsession類中查看一下實作它的都有哪些類

一共有兩個,而且SqlSessionManager還實作了SqlSessionFactory
SqlSessionManager
public class SqlSessionManager implements SqlSessionFactory, SqlSession {
private final SqlSessionFactory sqlSessionFactory;
private final SqlSession sqlSessionProxy;//這里使用了代理,來增強sqlsession
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<>();
//使用Threadlocal管理本執行緒的sqlsession來復用sqlsession
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionInterceptor());
}
//這個方法幫我們直接創建了sqlSessionFactory并且將傳入的sqlSessionFactory的SqlSession進行了代理
public static SqlSessionManager newInstance(Reader reader) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, null));
}
.....
public static SqlSessionManager newInstance(Reader reader) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, null));
}
public static SqlSessionManager newInstance(Reader reader, String environment) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, environment, null));
}
public static SqlSessionManager newInstance(Reader reader, Properties properties) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, properties));
}
public static SqlSessionManager newInstance(InputStream inputStream) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, null));
}
public static SqlSessionManager newInstance(InputStream inputStream, String environment) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, environment, null));
}
public static SqlSessionManager newInstance(InputStream inputStream, Properties properties) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, properties));
}
public static SqlSessionManager newInstance(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionManager(sqlSessionFactory);
}
.....
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //使用Threadlocal管理本執行緒的sqlsession來復用sqlsession
final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
if (sqlSession != null) {//獲取本執行緒的sqlsession
try {
return method.invoke(sqlSession, args);//實際呼叫
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} else {
try (SqlSession autoSqlSession = openSession()) {
try {
final Object result = method.invoke(autoSqlSession, args);
autoSqlSession.commit();
return result;
} catch (Throwable t) {
autoSqlSession.rollback();
throw ExceptionUtil.unwrapThrowable(t);
}
}
}
}
SqlSessionManager
他實作了Session介面,意味著,SqlSessionManager集成了 sqlSessionFactory和session 的功能,通過SqlSessionManager,開發者可以不在理會SqlSessionFacotry的存在,直接面向Session編程,
SqlSessionManager 內部提供了一個sqlSessionProxy,這個sqlSessionProxy提供了所有Session介面的實作,而實作中正是使用了上面提到的本地執行緒保存的session實體,
這樣,在同一個執行緒實作不同的sql操作,可以復用本地執行緒session,避免了DefaultSqlSessionFactory實作的每一個sql操作都要創建新的session實體
下面讓我們用一個簡單的實體來試試
@Test
public void testMyBatisBuild3() throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
// SqlSessionFactory build = new SqlSessionFactoryBuilder().build(reader);
// 不使用SqlSessionFactory 使用SqlSessionManager.newInstance();
SqlSessionManager sqlSessionManager = SqlSessionManager.newInstance(reader);
SqlSession sqlSession = sqlSessionManager.openSession();
TestMapper mapper = sqlSession.getMapper(TestMapper.class);
Ttest one = mapper.getOne(1L);
System.out.println(one);
sqlSession.close();
}
運行結果:
Reader entry: ????1 getOne0(Ljava/lang/Long;)Lcom/liangtengyu/entity/Ttest;
Find JAR URL: file:/Users/tengyu/IdeaProjects/mybatis-study/target/classes/com/liangtengyu/mapper/TestMapper.xml
Not a JAR: file:/Users/tengyu/IdeaProjects/mybatis-study/target/classes/com/liangtengyu/mapper/TestMapper.xml
Reader entry: <?xml version="1.0" encoding="UTF-8"?>
Checking to see if class com.liangtengyu.mapper.TestMapper matches criteria [is assignable to Object]
Opening JDBC Connection
Created connection 1585787493.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e853265]
==> Preparing: select * from t_test where id = ?
==> Parameters: 1(Long)
<== Columns: id, context
<== Row: 1, 123
<== Total: 1
Ttest{id=1, context='123'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e853265]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e853265]
Returned connection 1585787493 to pool.
本章主要講了MyBatis構建SqlSessionFactory方式,程序,和sqlsession的創建,以及使用SqlSessionManager管理session復用的實作方式.
下一篇研究資料源的池化和資料源加載程序.
加群一起學習吧
關注公眾號:java寶典
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/228030.html
標籤:Java
上一篇:python辦公入門6:xlrd操作excel單元格
下一篇:java物件指向問題


