mybatis源碼考究一
1.項目結構
2.mybatis使用步驟介紹
-
獲取SqlSessionFactory
- 這裏面就會用到全局配置,本文的con1.xml
-
使用SqlSessionFactory打開sqlSession
-
使用sqlSession操作數據庫
3.快速入門
1.構建SqlSessionFactory官網介紹
-
使用xml構建
static SqlSessionFactory sqlSessionFactory = null; static{ SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); try { sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis/con1.xml")); } catch (IOException e) { e.printStackTrace(); } }
-
不使用xml構建
static SqlSessionFactory sqlSessionFactory = null; static{ SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); //構建配置類 Configuration configuration = new Configuration(); //數據源 DataSource dataSource = new PooledDataSource("com.mysql.cj.jdbc.Driver","jdbc:mysql://localhost:3306/no_name","root","root"); //事務類型 JdbcTransactionFactory jdbcTransaction = null; jdbcTransaction = new JdbcTransactionFactory(); //新建環境,就是settings裏面的配置 Environment dev = new Environment("dev", jdbcTransaction, dataSource); configuration.setEnvironment(dev); //添加接口文件 configuration.addMapper(UserMapper.class); //這個類的作用暫時研究不多,看名字就知道對拍錯有幫助 ErrorContext.instance().resource("mybatis/UserMapper.xml"); InputStream inputStream = null; try { inputStream = Resources.getResourceAsStream("mybatis/UserMapper.xml"); } catch (IOException e) { e.printStackTrace(); } XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, "mybatis/UserMapper.xml", configuration.getSqlFragments()); //添加xml,有時候需要結合註解和xml mapperParser.parse(); //添加攔截器 configuration.addInterceptor(new MyInterceptor1()); configuration.addInterceptor(new MybatisOptimisticInterceptor()); //構建 sqlSessionFactory = sqlSessionFactoryBuilder.build(configuration); }
2.獲取sqlsession
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
public static SqlSession getSessionByType(ExecutorType execType){
return sqlSessionFactory.openSession(execType);
}
public static SqlSession getSessionByTypeAndAutoCommit(ExecutorType execType,boolean autoCommit){
return sqlSessionFactory.openSession(execType,autoCommit);
}
3.使用案例
/**
* 新增測試
* @param user
* @return
*/
public int insert(User user){
SqlSession session = MybatisSessionUtil.getSession();
int rint = session.insert("user.insertUser",user); // 第一個參數是mapper xml裏的namespace+MappedStatement對應的id
session.commit();// 不要忘記提交
return rint;
}
//測試使用xml構建的
@Test
public void testXml() {
UserTest1 userDao = new UserTest1();
User user = new User();
user.setName("wwwttt");
user.setPassword("33333");
if (userDao.insert(user) == 1) {
System.out.println("insert success...");
}else{
System.out.println("insert fail...");
}
}
//測試不使用xml構建的
@Test
public void testAnnoAndXml() {
SqlSession session = AnnoTest.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User userById = mapper.getUserById(1);
userById.setId(null);
userById.setName("渣渣輝");
mapper.insertUser(userById);
session.commit();
}
4.補全上面的幾個xml
1.con1.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>
<!– 這個可以用log4j –>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>-->
<!-- 和Spring整合後environment配置都會被幹掉 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事務管理,目前由mybatis來管理 -->
<transactionManager type="JDBC" />
<!-- 數據庫連接池,目前由mybatis來管理 -->
<dataSource type="POOLED"><!--有關於mysql數據庫的各種信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/no_name" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<mappers>
<!--將操作配置文件User.xml系添加進mapper-->
<mapper resource="mybatis/user.xml" />
<!--<mapper class="mybatis/user.xml" />
<mapper url="mybatis/user.xml" />-->
<!--<package name=""/>-->
</mappers>
</configuration>
2.user.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>
<!– 這個可以用log4j –>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>-->
<!-- 和Spring整合後environment配置都會被幹掉 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事務管理,目前由mybatis來管理 -->
<transactionManager type="JDBC" />
<!-- 數據庫連接池,目前由mybatis來管理 -->
<dataSource type="POOLED"><!--有關於mysql數據庫的各種信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/no_name" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<mappers>
<!--將操作配置文件User.xml系添加進mapper-->
<mapper resource="mybatis/user.xml" />
<!--<mapper class="mybatis/user.xml" />
<mapper url="mybatis/user.xml" />-->
<!--<package name=""/>-->
</mappers>
</configuration>
3.UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.UserMapper">
<!--插入用戶信息-->
<insert id="insertUser" parameterType="com.qdz.entity.User">
<!--<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
select uuid()
<!– 這裏是對於主鍵屬性的id進行賦值 –>
</selectKey>-->
insert into user(name,password) values(#{name},#{password})
</insert>
<select id="getUserListPageByIds" resultType="com.qdz.entity.User">
select * from user where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
<update id="updateUsers1">
update user set name='夏侯惇',password='54321',create_time=now() where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</update>
<update id="updateUsers2">
update user set name='姜維',password='54321',create_time=now() where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</update>
</mapper>
4.大致執行流程(本人debug出來,以查詢爲例)
import org.apache.ibatis.executor.SimpleExecutor;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.PreparedStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionManager;
import org.apache.ibatis.session.defaults.DefaultSqlSession;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import org.apache.ibatis.transaction.TransactionFactory;
import javax.sql.DataSource;
/**
* mybatis查詢過程
* @author authorZhao
* @date 2020年05月23日
*/
public class MyBatisQueryDetail {
/**
* 1.構建{@link SqlSessionFactory}
* -取配置文件 Configuration
* {@link Configuration}裏面包含了所有
* - {@link Environment} 這就是setting連的 Environment配置
* - {@link TransactionFactory} 事務
* - {@link DataSource} 數據源
* -{@link Configuration#logImpl} 日誌實現
* -{@link Configuration#defaultExecutorType} 執行器類型,默認defalut
* -{@link Configuration#mapperRegistry} mapper註冊器
* -{@link Configuration#interceptorChain} 攔截器鏈
* -{@link Configuration#mappedStatements} 就是每一個具體的mapper,包含.java和.xml的
* - 構建{@link SqlSessionFactory}
* ->{@link DefaultSqlSessionFactory}
* ->{@link SqlSessionManager}
*
*
* 2.獲取sqlSession{@link SqlSessionFactory#openSession()}
* ->{@link DefaultSqlSessionFactory#openSessionFromDataSource(org.apache.ibatis.session.ExecutorType, org.apache.ibatis.session.TransactionIsolationLevel, boolean)}
* ->{@link Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)}
* -> 這裏面根據執行器類型創建執行器,然後執行執行器的方法 interceptorChain.pluginAll(executor)
*
* 3.獲取mapper/selectone
* ->{@link DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)}
* ->{@link SimpleExecutor#doQuery(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.mapping.BoundSql)}
* ->{@link Configuration#newStatementHandler(org.apache.ibatis.executor.Executor, org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.mapping.BoundSql)}
* -> 上一步也有一個 pluginAll方法
* ->{@link org.apache.ibatis.executor.SimpleExecutor#prepareStatement(org.apache.ibatis.executor.statement.StatementHandler, org.apache.ibatis.logging.Log)}
* -> 默認實現 RoutingStatementHandler,會構建PreparedStatement
* -{@link StatementHandler},構建這個的時候會構建 ParameterHandler、 ResultSetHandler
* ->{@link Configuration#newParameterHandler(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.mapping.BoundSql)}
* ->{@link Configuration#newResultSetHandler(org.apache.ibatis.executor.Executor, org.apache.ibatis.mapping.MappedStatement, org.apache.ibatis.session.RowBounds, org.apache.ibatis.executor.parameter.ParameterHandler, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.mapping.BoundSql)}
* ->{@link PreparedStatementHandler#query(java.sql.Statement, org.apache.ibatis.session.ResultHandler)}
* ->{@link ResultSetHandler#handleResultSets(java.sql.Statement)}
*
*
*
*/
/**
*
* 攔截器四大接口
* 1.Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) 攔截執行器的方法;
* 4.StatementHandler (prepare, parameterize, batch, update, query) 攔截Sql語法構建的處理
* 2.ParameterHandler (getParameterObject, setParameters) 攔截參數的處理;
* 3.ResultSetHandler (handleResultSets, handleOutputParameters) 攔截結果集的處理;
*/
}
5.單機mybatis常見問題(單獨使用情況)
-
sqlsession線程安全問題
關於sqlsession線程的安全先行問題
/** * 測試多線程插入時的sqlsession線程安全問題 */ @Test public void testThread(){ ExecutorService executorService = Executors.newCachedThreadPool(); SqlSession session = AnnoTest.getSession(); List<Future> futures = new ArrayList<>(); for (int i = 0; i < 10; i++) { int j = i; //如果在此獲取sessionn那麼每個線程都是單獨的sqlsession Future<?> submit = executorService.submit(() -> save(j,session)); futures.add(submit); } while(futures.stream().anyMatch(i -> i.isDone() == false)){ try { Thread.currentThread().sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } } private void save(int i,SqlSession session) { UserMapper mapper = session.getMapper(UserMapper.class); User userById = mapper.getUserById(1); userById.setId(null); userById.setName("渣渣輝"+i+": "+Thread.currentThread().getName()); mapper.insertUser(userById); session.commit(); }
-
mybatis緩存
-
1.一級緩存是sqlsession級別
同一個sqlsession執行同樣的sql是有緩存的,如果執行了commit、flush、close 之後 會失效
-
2.二級緩存是namespace級別的,(開發中從未使用),不做研究
-
-
sql解析
這個其實也是mysql的精髓之一,在xml裏面寫出不可維護的sql,外加一堆動態標籤,你無可替代
這個未研究,xml解析也爲研究
-
其他使用問題
- #{}和${}區別,前者在sql出來之後變成了?後者直接替代了
- 參數接收,mybatis對參數默認採用param1、param2這樣的命名,存在map裏面
- 什麼對多對一映射略 對多分頁參考
- mybatis的日誌實現,提供了幾種常見的,項目裏面有就會適配
- 略
6.結束
參考文章:文中demo的配置抄襲自網上,現在忘記那個了,主題流程來自mybatis官網
本文代碼github地址本文代碼地址
下一篇會結合spring開發使用,重點分析問題如下
1.spring如何解決sqlsession線程安全問題
2.spring裏面一級緩存爲何失效的問題
3.spring如何接管事務
4.分頁插件和sql打印
5.springboot自動配置和mybatisPlus的單表CRUD實現