1.Mybatis整合Logback 2.SqlSession为什么可以提交事务 3.insert和delete底层实现的方式 4.完成超市账单管理系统的登陆功能

1.springmvc,mybatis,logback整合相关

1.利用spring-aop实现日志自动化

1.1添加相关依赖(spring-aop,aspectj)

<span style="white-space:pre"> </span><dependency>
<span style="white-space:pre"> </span><groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
1.2 spring-mvc.xml添加配置
<span style="white-space:pre"> </span><aop:aspectj-autoproxy proxy-target-class="true"/>
这条配置是将自动生成代理,proxy-target-class="true"配置是为实现类生成代理,若不加此项只能为接口生成代理


1.3 配置切面

@Component
@Aspect
public class LogAspect {

private Log log = LogFactory.getLog(LogAspect.class);

@Pointcut("execution(* com.jiechengkeji.manager..*(..))")
public void aspect() {
}

@Around("aspect()")
public Object around(ProceedingJoinPoint point) {
long start = System.currentTimeMillis();
String uuid = UUID.randomUUID().toString();//由于开始记录和结束记录会错开,用UUID进行配对

String before = String.format("%s.%s()--ARGS:%s (%s)",

point.getTarget().getClass().getSimpleName(),
MethodSignature.class.cast(point.getSignature()).getMethod().getName(),
Arrays.toString(point.getArgs()),
uuid);

log.info(before);

Object result = null;
try {
result = point.proceed();
} catch (Throwable e) {
log.error(e);
}

String after = String.format("%s.%s()--RETURNS:%s TIME:%s ms (%s)",
point.getTarget().getClass().getSimpleName(),
MethodSignature.class.cast(point.getSignature()).getMethod().getName(),
result,
System.currentTimeMillis() - start,
uuid);

log.info(after);

return result;
}

}

没啥说的 基本上看代码就能明白,不要忘记添加@Component注解。之前我以为和 @Controller,@Service一样是包含@Component注解的,没想到不是,排查了很长时间。

1.4 配置logback.xml


<!-- aop记日志的appender -->
<appender name="aop" class="ch.qos.logback.core.rolling.RollingFileAppender">
<FileNamePattern>${log.base}/${log.methodModuleName}%d{yyyy-MM-dd}.%i.log</FileNamePattern> <!-- 设置日志不超过${log.max.size}时的保存路径,注意如果
是web项目会保存到Tomcat的bin目录 下 -->
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${log.base}/${log.methodModuleName}%d{yyyy-MM-dd}.%i.log.zip
</FileNamePattern>
<!-- 文件输出日志 (文件大小策略进行文件输出,超过指定大小对文件备份) -->
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${log.max.size}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 日志输出的文件的格式 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%date{HH:mm:ss} [%-5level] %msg%n</Pattern>
</layout>
</appender>

<span style="white-space:pre"> </span><logger name="com.jiechengkeji.manager.aop.LogAspect" additivity="false">
<span style="white-space:pre"> </span><level value="INFO" />
<span style="white-space:pre"> </span><appender-ref ref="aop" /> 
<span style="white-space:pre"> </span></logger>


2. 整合mybatis
2.1 调整mybatis的config.xml


<settings>
<setting name="logPrefix" value="dao."/> <!-- dao. 是logger的name .不可省略-->
<setting name="cacheEnabled" value="false" />
<setting name="defaultExecutorType" value="REUSE" />
<setting name="logImpl" value="COMMONS_LOGGING" />
</settings>
2.2 调整logback.xml

<logger name="dao" level="DEBUG">
<!--daoFILE为实际定义的appender-->
<appender-ref ref="aop" />
</logger>

3.效果


1.@Pointcut("execution(* com.jiechengkeji.manager..*(..))")中定义的每一个切入点 执行前执行后都会打印日志
2.mybatis的日志信息也会打印到aop appender指定的文件中
示例:

</pre>13:12:35 [INFO ] CityService.getCityNameById()--ARGS:[-1]  (48ded28b-2773-4846-897e-ef14beee273b)13:12:35 [DEBUG] ooo Using Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@386f61ed]]]13:12:35 [DEBUG] ==>  Preparing: select name from dy_city where id = ? ; 13:12:35 [DEBUG] ==> Parameters: -1(Integer)13:12:35 [DEBUG] <==      Total: 013:12:35 [INFO ] CityService.getCityNameById()--RETURNS:null  TIME:8 ms  (48ded28b-2773-4846-897e-ef14beee273b)13:12:35 [INFO ] LoginController.doLogin()--RETURNS:com.jiechengkeji.manager.util.AsyncResult@23b16363  TIME:339 ms  (15ab451d-fa2e-42e1-adbe-54e106d77705)<p></p><div style="top:1283px"><pre name="code" class="html"> @Pointcut("execution(* com.jiechengkeji.manager..*(..))")

————————————————
版权声明:本文为CSDN博主「史蒂芬的速度」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/winnerwxc/article/details/51612493

2.SqlSession为什么可以提交事务

 

 

如题目所示,本小章节讨论为什么SqlSession的commit会造成事物的提交

首先先看SqlSession的commit()他是一个接口的方法,所以去他的实现类找(Ctrl+H)DefaultSqlSession类中查找他的commit无参数的方法,因为我们调用的也是他的无参方法,往下看

 

复制代码
public void commit() {
        this.commit(false);
    }

    public void commit(boolean force) {
        try {
            this.executor.commit(this.isCommitOrRollbackRequired(force));
            this.dirty = false;
        } catch (Exception var6) {
            throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + var6, var6);
        } finally {
            ErrorContext.instance().reset();
        }

    }
复制代码

 

看出来了吗?他调用了他下面的一个有参数的方法,并且传进去了参数,我们稍做记录,force是false

dirty变成false在执行器的提交下面,说明数据不是脏的了

executor.commit(XXX)执行器的提交,我们看里面的那个方法,commit在方法参数里面调用了方法并拿到返回值,看一下那个里面的方法,注意传进去的值为false

 

private boolean isCommitOrRollbackRequired(boolean force) {
        return !this.autoCommit && this.dirty || force;
    }

 

这个方法说实话,一眼看上去有点懵,但是你知道      !        &&        ||     的使用优先级,你就可以计算出来了      &&>||>!                    结果return回去的是true

上面的this.dirty是在上回说到的增删改都会底层调用update方法,里面改为true的

上面的autoCommit则创建sqlSession的时候就早早的改为false,OpenSession方法底层

接下来看上面的上面的executor.commit(XXX)方法,,他是Executor执行器接口的,找他的实现类BaseExecutor类的commit的带boolean参数的方法

 

复制代码
 public void commit(boolean required) throws SQLException {
        if(this.closed) {
            throw new ExecutorException("Cannot commit, transaction is already closed");
        } else {
            this.clearLocalCache();
            this.flushStatements();
            if(required) {
                this.transaction.commit();
            }

        }
   }
复制代码

 

里面有一些不需要关注,不过简单提一嘴,clearLocalCache()清理缓存,flushStatements刷新参数

最终级的关注点就是因为if中的required是传进来的参数,上面已经解释过是true,所以他执行下一行代码就是transaction.commit(),哦,transaction的英文名就叫做事物

终结一句话:session.commit()最终调度到了事物的提交 ,this.transaction.commit()

 

3.mybatis中insert和delete底层实现的方式

 

先点进去看一下insert方法

用ctrl加鼠标左键点进去看

  发现是一个接口SqlSession的方法,没有实现 ,但是通过里氏替换原则的想法,他是接口接收了实现类,所以找他的实现类DefaultSqlSession(idea快捷键ctrl+H)

  进去后用ctrl+F查找insert,可以看到多个insert方法构成的重载,但是他们的方法实现调用了这个

 

复制代码
public int insert(String statement) {
        return this.insert(statement, (Object)null);
    }

    public int insert(String statement, Object parameter) {
        return this.update(statement, parameter);
    }
复制代码

 

  上面那个方法调用了下面的方法,下面insert方法确实调用了update方法

再看一下delete的方法,

  他和insert类似,都是接口SqlSession的方法,所以还是找他的实现类DefalutSqlSession

  进去之后继续寻找delete方法

 

复制代码
public int delete(String statement) {
        return this.update(statement, (Object)null);
}

public int delete(String statement, Object parameter) {
        return this.update(statement, parameter);
}
复制代码

 

  可以看出他也是调用了update方法,所以就引发了好奇,update带俩个参数的那个方法中有什么?为什么都调用它呢?

看一下update方法

 

复制代码
public int update(String statement, Object parameter) {
        int var4;
        try {
            this.dirty = true;
            MappedStatement e = this.configuration.getMappedStatement(statement);
            var4 = this.executor.update(e, this.wrapCollection(parameter));
        } catch (Exception var8) {
            throw ExceptionFactory.wrapException("Error updating database.  Cause: " + var8, var8);
        } finally {
            ErrorContext.instance().reset();
        }

        return var4;
    }
复制代码

 

  这里简单的提一下,

    1)dirty=true;代表了他已经成为脏数据,意思就是内存上的数据和数据库中不一致,为什么这儿会改,或许就要谈论到为什么增删改需要手动提交事务(session.commit())和

      session.close为什么会造成事物的回滚,可能下几章博客会有简单刨析一下

    2)MappedStatement这个我找了一些资料,但是从百度搜索的没有告诉明白这个是什么东西,简单的说一下,如果错误,请大家帮我指出来,它应该是获取到mybatis配置中的具体SQL

    3)var4就是用执行器执行sql,接收返回值,由update返回值是int,并且是var4,可以推断出他应该是返回受影响的行数

    4)ErrorContext从字面意思理解为错误的上下文,instance是实例的意思,他源码是通过静态方法,拿到ErrorContext对象实例或创建实例并返回回来,单例的写法,reset是复位,重置,

    初始化的意思

4.完成超市账单管理系统的登陆功能

posted @ 2019-09-28 19:45  小猪爱蹦迪--自学Java  阅读(228)  评论(0编辑  收藏  举报