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实现