MyBatis第二天内容
1.mybatis的执行原理
通过:
1.全局配置文件SqlMapConfig.xml 映射文件mapper.xml 获取得到SqlSessinFactory工厂
2.由工厂得到SqlSession
3.通过SqlSession获取mapper对象然而mapper对象能执行操作是因为,由SqlSession得到Executor执行对象
4.通过解析Mapper.xml的映射文件我们将每一条语句封装为一个StatementHandler对象
5.在mapper.xml配置文件中我们可以配置输入参数类型(这个参数是由外界传来的)通过解析xml将其封装在ParamentHandler对象中
6.同理输出参数也是由解析xml得到最后封装的类型以及关联关系最后封装在ResultSetHandler中
Mybatis四大核心对象介绍:
Executor(执行器)
什么是执行器?
Mybatis中所有的Mapper语句的执行都是通过Executor进行的,Executor是Mybatis的一个核心接口。从其定义的接口方法我们可以看出,对应的增删改语句是通过Executor接口的update方法进行的,查询是通过query方法进行的。虽然Executor接口的实现类有BaseExecutor和CachingExecutor,而BaseExecutor的子类又有SimpleExecutor、ReuseExecutor和BatchExecutor,但BaseExecutor是一个抽象类,其只实现了一些公共的封装,而把真正的核心实现都通过方法抽象出来给子类实现,如doUpdate()、doQuery();CachingExecutor只是在Executor的基础上加入了缓存的功能,底层还是通过Executor调用的,所以真正有作用的Executor只有SimpleExecutor、ReuseExecutor和BatchExecutor。它们都是自己实现的Executor核心功能,没有借助任何其它的Executor实现,它们是实现不同也就注定了它们的功能也是不一样的。Executor是跟SqlSession绑定在一起的,每一个SqlSession都拥有一个新的Executor对象,由Configuration创建。
StatementHandler(相当于sql语句执行的对象由Executor来调用)
StatementHandler 对象是在 SqlSession 对象接收到命令操作时,由 Configuration 对象中的newStatementHandler 负责调用的,也就是说 Configuration 中的 newStatementHandler 是由执行器中的查询、更新(插入、更新、删除)方法来提供的,StatementHandler 其实就是由 Executor 负责管理和创建的。
ParamenterHandler :(输入映射)
拦截参数的处理。
ResultSetHandler :(输出映射)
拦截结果集的处理。
2.mybatis的插件原理
引入插件的两个方式:
1.在mybatis 全局配置中去配置插件配置
1 <!-- 2 plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下: properties?, settings?, typeAliases?, typeHandlers?, objectFactory?,objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers? 3 --> 4 <plugins> 5 <!-- com.github.pagehelper为PageHelper类所在包名 --> 6 <plugin interceptor="com.github.pagehelper.PageInterceptor"> 7 <!-- 使用下面的方式配置参数,后面会有所有的参数介绍 --> 8 <property name="param1" value="value1"/> 9 </plugin> 10 </plugins>
2.在spring配置文件中去整合插件配置
1 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 2 <!-- 注意其他配置 --> 3 <property name="plugins"> 4 <array> 5 <bean class="com.github.pagehelper.PageInterceptor"> 6 <property name="properties"> 7 <!--使用下面的方式配置参数,一行配置一个 --> 8 <value> 9 xxxxInterceptor 10 </value> 11 </property> 12 </bean> 13 14 </array> 15 </property> 16 </bean>
插件原理:
四大对象创建后不是直接返回而是
1.调用interceptorChain.pluginAll(parameterHandler);
2.从1中获取到所有的拦截器interceptor(插件所需要实现的接口)
3.调用interceptor.plugin(target)返回所有target包装后的对象
public Object pluginAll(Object target){
for(Interceptor interceptor:interceptors){
target=interceptor.plugin(target);
}
return target;
}
注意:
也就是说创建核心四大对象时我们回去调用pluginAll方法来得到所有的拦截器,插件就是利用了拦截器原理。
想要做一个插件我们需要继承Intercepter接口并且重写里面的方法,如下这是我们创建的一个简单插件,注意注释信息
1 /* 2 * 完成插件的签名: 3 * 告诉mybatis插件拦击哪个对象的哪个的方法的哪个参数的签名 4 */ 5 6 @Intercepts({ 7 @Signature(type=StatementHandler.class,method="parameterize",args= java.sql.Statement.class) 8 9 }) 10 public class MyFirstPlugin implements Interceptor { 11 12 /*intercept() 13 * 用来;拦截目标对象的目标方法的执行 14 * 15 */ 16 @Override 17 public Object intercept(Invocation invocation) throws Throwable { 18 /* 19 * 打印拦截的方法: 20 * 四大对象创建时都会执行插件的拦截方法 21 * Executor 22 * ParameterHandler 23 * ResultMapHandler 24 */ 25 System.out.println("MyFirstPlugin=====这个方法被拦截"+invocation.getMethod()); 26 //执行目标方法 (这个方法一定要像小心编写,因为这个会触碰mybatis的底层代码) 27 28 Object proceed = invocation.proceed(); 29 //返回执行后的返回值 30 return proceed; 31 } 32 33 /* 34 * plugin() 35 *包装目标对象,包装目标:给目标对象创建一个代理对象 36 */ 37 @Override 38 public Object plugin(Object target) { 39 System.out.println("MyFirstPlugin====这些对象已经被创建"+target); 40 //给目标对象生成一个代理对象 41 Object wrap=Plugin.wrap(target, this); 42 //返回为当前target返回的动态代理 43 return wrap; 44 } 45 /*setProperties() 46 * 将插件注册时的property传递进来 47 * 48 */ 49 @Override 50 public void setProperties(Properties properties) { 51 52 System.out.println(properties); 53 } 54 55 }
@Intercepts({
@Signature(type=StatementHandler.class,method="parameterize",args= java.sql.Statement.class)
})
告诉mybatis插件拦击哪个对象的哪个的方法的哪个参数的签名
intercept:当四大对象被创建时会拦截对象
而
Object proceed = invocation.proceed();
这个方法是用来指定让那些对象通过的
plugin:这个方法是用来生成代理类,上面的前面指定着哪个对象要被拦截,并且是指定到方法,生成代理类对象,返回相当于是将功能增强了(著名的插件pagehelper就是这个原理)
setProperties:是在配置插件的时候配置指定的参数用来获取参数的
写完拦截器后我们需要在mybatis全局配置文件中配置,或者是在spring中配置配置好后就可以使用
这里我们在mybatis全局配置文件中配置
1 <plugins> 2 <plugin interceptor="com.atguigu.plugin.myplugin.MyFirstPlugin"> 3 <property name="username" value="root"/> 4 <property name="password" value="1234"/> 5 </plugin> 6 </plugins>
测试:
1 @Before 2 public void setUp() throws Exception { 3 //1.给出全局配置文件的文件路径 4 String resource="SqlMapConfig.xml"; 5 //2.读取文件返回一个输入流 6 InputStream inputStream= Resources.getResourceAsStream(resource); 7 sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream); 8 9 } 10 11 @Test 12 public void testFindUserById() { 13 //1.创建sqlSession会话 14 SqlSession sqlSession = sqlSessionFactory.openSession(); 15 //2.使用Mapper代理的方式创建接口实例(这个实例不是我们创建的而是由MyBatis创建的我们遵守规范即可) 16 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 17 //3.调用实例方法得到封装好的对象 18 User user =userMapper.findUserById(1); 19 System.out.println(user); 20 sqlSession.close(); 21 } 22 }
现象:
创建四大对象时必须经过的方法
拦截的对象
3.mybatis的批量操作
如果我们不去配置批量的SqlSession的话,当我们执行下面添加代码时性能就会低很多
我们用时间戳去记录花费了63秒,这个是没有配置批量操作的SqlSession对象
1 for (int i = 0; i < 1000; i++) {//花费了63秒这与cpu性能有关 2 String u=UUID.randomUUID().toString(); 3 String uuid=u.substring(0, 5); 4 employeeMapper.insert(new Employee(null,uuid,"1",uuid+"@qq.com",1)); 5 6 }
我们去spring整合mybatis时我们是直接将接口、映射文件还用全局配置文件全都配置好了所以我们拿出来的mapper对象是执行不了批量操作的
配置批量的SqlSession对象有两种方式,一种是mybatis自己的方式 一种是整合在spring中的方式
在mybatis中指定批量执行器
1 private SqlSessionFactory sqlSessionFactory; 2 @Before 3 public void setUp() throws Exception { 4 //1.给出全局配置文件的文件路径 5 String resource="SqlMapConfig.xml"; 6 //2.读取文件返回一个输入流 7 InputStream inputStream= Resources.getResourceAsStream(resource); 8 sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream); 9 10 } 11 12 13 @Test 14 public void testFindUserById() { 15 //1.创建sqlSession会话 16 SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); ............
注意标红的代码:
ExecutorType这是一个枚举类,里面有许多类型的执行器,我选择
ExecutorType.BATCH也就是我们选择了一个批量操作的执行器,这样我们SqlSession对象就是一个可以批量操作的对象
对比代码:我们这个时候使用批量的SqlSession就是一个性能高的操作花费了55秒
1 EmployeeMapper emapper= sqlSession.getMapper(EmployeeMapper.class); 2 for (int i = 0; i < 10; i++) {//花费了55秒 3 String u=UUID.randomUUID().toString(); 4 String uuid=u.substring(0, 5); 5 emapper.insert(new Employee(null,uuid,"1",uuid+"@qq.com",1)); 6 } 7 long end =System.currentTimeMillis(); 8 System.out.println("本次操作花了:"+(end-start)+"毫秒");
在spring整合中配置一个能执行批量操作的执行器
1 <!-- 创建一个可以批量操作的sqSession --> 2 <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> 3 <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/> 4 <constructor-arg name="executorType" value="BATCH"></constructor-arg> 5 </bean> 6
注意在:sqlSession类中有一个executorType我们直接给他指定一个BATCH的值这样就是一个批量的执行器
我们可以直接注入就行
代码实例
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations= {"classpath:applicationContext.xml"}) public class Spring4Junit { private ApplicationContext applicationContext; @Autowired private EmployeeMapper employeeMapper; @Autowired private SqlSession sqlSession; @Test//使用spring的单元测试 public void test02() { EmployeeMapper emapper= sqlSession.getMapper(EmployeeMapper.class); for (int i = 0; i < 10; i++) {//花费了55秒 String u=UUID.randomUUID().toString(); String uuid=u.substring(0, 5); emapper.insert(new Employee(null,uuid,"1",uuid+"@qq.com",1)); } long end =System.currentTimeMillis(); System.out.println("本次操作花了:"+(end-start)+"毫秒"); } }
@Autowired根据bean的类型注入和@Resource(jdk提供的注解)作用一样但是@Resource是默认按照属性Bean实例名称进行装配
思考?为什么我们没有在spring中配置 employeeMapper对象但是还是能注入,因为这个对象是代理生成的我们将接口映射文件传入他就会代理生成指定的实现类