解决步骤1:pom文件引入依赖并即可
步骤2:添加完依赖后伍哗此时xml头腔档行文件飘红左边有红色小灯泡,点击Fetch external resource即蠢郑可解决
从命名上可以看出,这个是一个 Builder 模式的,用于创建 SqlSessionFactory 的类。SqlSessionFactoryBuilder 根据配置来构造 SqlSessionFactory。其中配置方式有两种:
mybatis-config.xml 就是我们的配置文件:
Java Config 相比较 XML 文件的方式而言,会有一些限制。比如修改了配置文件需要重新编译,注解方式没有 XML 配置项多等。所以,业界大多数情况下是选择 XML 文件的方式。但到底选择哪种方式,这个要取决与自己团队的需要。比如,项目的 SQL 语句不复杂,也不需要一些高级的 SQL 特性,那么 Java Config 则会更加简洁一点;反之,则可以选择 XML 文件的方式。
创建配置文件解析器XMLConfigBuilder
解析mybatis-config.xml里的配置为Configuration对象,Mybatis的全局配置对象。
XMLConfigBuilder#parseConfiguration解析mapper下的xml
XMLMapperBuilder#bindMapperForNamespace,根据xml里的 namespace 反射出 mapper接口 的 class,如果有mapper接口,则把该mapper接口的class添加到Configuration的mapperRegistry里。
如果该接口已经注册,则抛出已经绑定的异常。
为该接口注模春册MapperProxyFactory,但这里只是注册其创建MapperProxy的工厂,并不是创建MapperProxy。
如果Mapper对应的xml资源未加载,触发xml的绑定 *** 作,将xml中的sql语句与Mapper建立关系。
addMapper方法,只是为**Mapper创建对应对应的MapperProxyFactory。
根据Mapper接口与SqlSession创建MapperProxy对象。
根据接口类获取MapperProxyFactory。烂悄
调用MapperProxyFactory的newInstance创建MapperProxy对象。
SqlSessionFactory 顾名思义,是用于生产 SqlSession 的工厂。 通过如下的方式来获取 SqlSession 实例:
SqlSession 包含了执行 SQL 的所有的方法。以下是示例:
当然,下面的方式可以做到类型安全:
MapperProxy是MapperProxyFactory使用SqlSession创建出来的。所以MapperProxy中包含SqlSession。
可以看到MapperProxy调用invoke方法,进而调用MapperMethod的execute(),这些MapperMethod就是和你要执行的命令相关,比如执行select语句,则会通过SqlSession的select()方法,最饥码渣终调用到Executor的query方法。Executor会再协调另外三个核心组件。
MapperProxy:
MapperMethod:
插件的构建:
谈原理首先要知道StatementHandler,ParameterHandler,Result Handler都是代理,他们是Configuration创建,在创建过程中会调用interceptorChain.pluginAll()方法,为四大组件组装插件(再底层是通过Plugin.wrap(target,XX, new Plugin( interceptor))来来创建的)。
插件链是何时构建的:
在执行SqlSession的query或者update方法时,SqlSession会通过Configuration创建Executor代理,在创建过程中就调用interceptor的pluginAll方法组装插件。然后executor在调用doQuery()方法的时候,也会调用Configuration的newStatementHandler方法创建StatemenHandler(和上面描述的一样,这个handler就是个代理,也是通过interceptorChain的pluginAll方法构建插件)
插件如何执行:
以statementhandler的prepare方法的插件为例,正如前面所说,statementhandler是一个proxy,执行他的prepare方法,将调用invokeHandler的invoke方法,而invokeHandler就是Plugin.wrap(target, xxx, new Plugin(interceptor))中的第三个参数,所以很自然invokeHanlder的invoke的方法最终就会调用interceptor对象的intercept方法。
Mybatis的插件配置在configuration内部,初始化时,会读取这些插件,保存于Configuration对象的InterceptorChain中。
org.apache.ibatis.plugin.InterceptorChain.java源码。
上面的for循环代表了只要是插件,都会以责任链的方式逐一执行,所谓插件,其实就类似于拦截器。
插件的编写
插件必须实现org.apache.ibatis.plugin.Interceptor接口。
-intercept()方法:执行拦截内容的地方,拦截目标对象的目标方法的执行
-plugin()方法:决定是否触发intercept()方法。 作用:包装目标对象,包装就是为目标对象创建一个代理对象
-setProperties()方法:给自定义的拦截器传递xml配置的属性参数。将插件注册时的property属性设置进来
下面自定义一个拦截器:
为什么要写Annotation注解?注解都是什么含义?
Mybatis规定插件必须编写Annotation注解,是必须,而不是可选。@Intercepts注解:装载一个@Signature列表,一个@Signature其实就是一个需要拦截的方法封装。那么,一个拦截器要拦截多个方法,自然就是一个@Signature列表。
type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }
解释:要拦截Executor接口内的query()方法,参数类型为args列表。
Plugin.wrap(target, this)是干什么的?
使用JDK的动态代理,给target对象创建一个delegate代理对象,以此来实现方法拦截和增强功能,它会回调intercept()方法。
Mybatis可以拦截哪些接口对象?
Mybatis只能拦截ParameterHandler、ResultSetHandler、StatementHandler、Executor共4个接口对象内的方法。
重新审视interceptorChain.pluginAll()方法:该方法在创建上述4个接口对象时调用,其含义为给这些接口对象注册拦截器功能,注意是注册,而不是执行拦截。
拦截器执行时机:plugin()方法注册拦截器后,那么,在执行上述4个接口对象内的具体方法时,就会自动触发拦截器的执行,也就是插件的执行。
Invocation
可以通过invocation来获取拦截的目标方法,以及执行目标方法。
分页插件原理
由于Mybatis采用的是逻辑分页,而非物理分页,那么,市场上就出现了可以实现物理分页的Mybatis的分页插件。 要实现物理分页,就需要对String sql进行拦截并增强,Mybatis通过BoundSql对象存储String sql,而BoundSql则由StatementHandler对象获取。
因此,就需要编写一个针对StatementHandler的query方法拦截器,然后获取到sql,对sql进行重写增强。
1、xml文件头部需要添加spring的相关支持:渣搜 2、配置事务管理器
3、配置需要加入事务的方法规则,或者说是一个切面
<!-- 定义事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--使用注释事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />
事物配置中有哪些属性可以配置
(1)事务的传播性:@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
(2)事务的超时性:@Transactional(timeout=30) //默认是30秒
注如轮历意这里说的是事务的超时性而不是Connection的超时性,这两个是有区别的
(3)事务的隔离级别:@Transactional(isolation = Isolation.READ_UNCOMMITTED)
读取未提交数据(会出现脏读, 不可重复读) 基本不使用
(4)回滚:
指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。
(5)只读:@Transactional(readOnly=true)
该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。
例如:
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class,timeout=1,isolation=Isolation.DEFAULT)
public void saveUser(Map<String, String>map) throws Exception {
System.out.println("方法开始")
for (int i = 0i <500000i++) {
System.out.println("*")
}
System.out.println("进入保存")
userDao.saveUser(map)
System.out.println("退出保存")
}
解释说明
事务的传播级别定义的是事务的控制范围,主要是父子事务之间的相互影响关系;事务的隔离级别定义的是事务读写的控制范围,主要是两个事务之间的相互影响关系。
传播级别:
1)、REQUIRED
如果当前方法已经在事务中,那么就以父事务执行,不需要新建事务;如果当前方法不在事务中,那么就为当前方法新建事务。桐纯回滚情况:父子方法中任何地方出现问题,都会全部回滚。
2)、SURPPORTED
如果当前方法已经在事务中,那么就以当前事务执行;如果当前方法不再事务中,那么就以非事务方式运行。如果运行在事务中,那么只要出现异常都会回滚。
3)、NOT_SURPPORTED
如果当前方法已经在事务中,那么就挂起当前事务,以非事务方式运行,方法执行完毕后,恢复事务;如果当前方法不再事务中,那么就以非事务方式执行。
4)、MANDATORY
强制以事务方式执行,如果当前方法不在事务中,那么会抛出异常。
5)、NEVER
与MANDATORY相反,强制以非事务方式执行,如果当前方法在事务中,那么会抛出异常。
6)、REQUIRED_NEW
与REQUIRED不同的是,无论该方法当前是不是在事务中,都会为自己新建一个事务。如果当前已经在事务中,那么会挂起父事务,为自己新建一个事务。父事务不影响它子事务的提交和回滚。
7)、NESTED
嵌套事务。理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。而Nested事务的好处是他有一个savepoint。
例如:
ServiceA {
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void methodA() {
try {
//savepoint
ServiceB.methodB()//PROPAGATION_NESTED 级别
} catch (SomeException) {
// 执行其他业务, 如 ServiceC.methodC()
}
}
}
也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如ServiceC.methodC,继续执行,来尝试完成自己的事务。
原文链接:https://blog.csdn.net/wgh1015398431/java/article/details/52861048
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)