zoukankan      html  css  js  c++  java
  • 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.完成超市账单管理系统的登陆功能

  • 相关阅读:
    [luogu P2184] 贪婪大陆 [树状数组][线段树]
    luogu P3373 【模板】线段树 2
    [luogu P3384] 【模板】树链剖分 [树链剖分]
    树链剖分膜版
    AtCoder Grand Contest 026F
    AtCoder Regular Contest 091F
    AtCoder Regular Contest 099F
    AtCoder Grand Contest 027D
    向量叉积分配律简单证明
    LOJ 538. 「LibreOJ NOIP Round #1」数列递推(找规律+结论)
  • 原文地址:https://www.cnblogs.com/liu13-B/p/11604559.html
Copyright © 2011-2022 走看看