ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

mybatis使用笔记、生命周期、拦截器等、类型转换

mybatis使用笔记、生命周期、拦截器等、类型转换 文章目录打印sql日志mybatis-config.xml方式application.yml里面配置配置类配置方式生命周期生命周期-StatementHandler生命周期-ParameterHandler生命周期-Executor生命周期-ResultSetHandlermybatis拦截器其他扫描方式select日志可以打出来update和delete语句未打印出来?mybatis拦截器及sql拦截器拦截并打印指定方法的语句拦截器日志排查过程mybatis打印的时间一定准吗?BaseTypeHandler类型转换批量模式(批量插入)批量模式-原生批量模式-mybatis-plus批量模式-处理大文本时性能较低其他文档mybatis用了那么久实际一直不明白做个笔记吧。千万不要以为mybatis简单里面有很多东西它的设计思路及以及很多东西都很有借鉴性否则也不会成为最流行的持久层框架。如果能把mybatis裂解的很深那么很有好处。打印sql日志实测mybatis-config.xml方式好用(记得注掉yml里的相关配置)mybatis-config.xml方式这种方式实测稳稳当当可以打印sql日志在有mybatis-config.xml文件的时候推荐用这个。settingssettingnamelogImplvalueSTDOUT_LOGGING/setting/settings那么如果没有mybatis-config配置文件呢?用properties或者yml配置也是可以的。application.yml里面配置只配置这一句好像还不行需要配置logmapper所在位置的包为debug?实测用yml配置的方式没有成功改用mybatis-config.xml配置成功了。写法一(推荐)mybatis:configuration:log-impl:org.apache.ibatis.logging.slf4j.Slf4jImpl 写法二(实测打印不出参数建议用Slf4jImpl比较好)mybatis:configuration:log-impl:org.apache.ibatis.logging.stdout.StdOutImpllogging:level:root:infocom.ttt.demo:debug# 这个对应mapper类的包路径级别必须设置为debug注主要就2点。1、配置日志级别为debug。(注必须是debug才会打印sql)2、查看执行过程。(包含参数等)上面这种配置方法好像有问题。本地debug的时候加入如下两个参数实测可以-Dlogging.level.java.sql.StatementDEBUG-Dlogging.level.java.sql.PreparedStatementDEBUG如下参数也可以考虑加上-Dlogging.level.org.apache.ibatisDEBUG-Dlogging.level.java.sql.ConnectionDEBUG这几个参数也可以一起加log4j.logger.org.mybatisDEBUGlog4j.logger.org.apache.ibatisDEBUGlog4j.logger.java.sql.StatementDEBUGlog4j.logger.java.sql.ResultSetDEBUGlog4j.logger.java.sql.PreparedStatementDEBUGlog4j.logger.com.baiwang.custom.common.daoDEBUG注如果是log4j.properties加在这里面就行不用在application.yml里面加了。如果没有log4j.properties文件或者没有其他地方来配置才考虑在application.yml里面加。实测加在log4j.properties里面就够用。配置类配置方式生命周期生命周期-StatementHandler1、在MyBatis准备执行SQL之前首先会创建Statement对象这时会触发对StatementHandler的拦截。2、使用StatementHandler拦截器可以在SQL语句被发送到数据库执行前进行自定义操作比如修改原始SQL语句、设置特殊的Statement属性等。生命周期-ParameterHandler1、在Statement准备执行之前ParameterHandler将会被调用以设置SQL语句中的参数。2、通过拦截ParameterHandler可以在SQL参数绑定前后进行操作。适用于复杂的参数处理逻辑比如加密/解密数据或者对特殊的参数格式进行处理。生命周期-Executor1、执行器Executor是整个执行过程的中心它会调用上述的StatementHandler和ParameterHandler来准备命令并执行。2、拦截Executor可以在SQL执行前后添加逻辑比如缓存的逻辑在查询语句执行前后检查和添加缓存。生命周期-ResultSetHandler1、SQL语句执行后如果有结果集返回MyBatis将使用ResultSetHandler来处理这些结果集将JDBC返回的ResultSet转化为MyBatis中指定的结果对象。2、拦截ResultSetHandler支持在结果集映射过程中插入自定义逻辑比如结果集的加工处理、性能统计等。mybatis拦截器接口代码publicinterfaceInterceptor{Objectintercept(Invocationinvocation)throwsThrowable;defaultObjectplugin(Objecttarget){returnPlugin.wrap(target,this);}defaultvoidsetProperties(Propertiesproperties){}}实现类有一些如ExtDataInterceptor、PageInterceptor等。注从这里也可以看出不同拦截器因为需求不通接口设计也不同例如springmvc的拦截器的preHandler()就带有return false的功能。 mybatis因为没有这个需求所以没有带。也就是说拦截器是不同的。其他扫描方式不只一种哪种都是可以的。select日志可以打出来update和delete语句未打印出来?正常应该是都可以打印出来的应该不是设置控制的。那么问题来了如果sql执行出问题了会打印吗?感觉也会打印那么为什么没打印出来呢。。。所有方法都试了还是不行最后发现是mybatisConfig里面没有配置sql拦截器SqlPrintInterceptor。sqlPrintInterceptor()这个interceptor是mybatis的interceptor。所以应该多个思路是否重置了mybatis的拦截器。mybatis拦截器及sql拦截器拦截器代码Interceptor[]pluginsnewInterceptor[]{sqlPrintInterceptor(),otherInterceptor()};sessionFactoryBean.setPlugins(plugins);简单的说就是在sessionFactoryBean中设置plugins当然sessionFactoryBean可不只这点参数数据源等都是从这里设置。SqlPrintInterceptor代码在此不详述简单说就是实现Interceptor然后重写accept方法。拦截并打印指定方法的语句原理很简单intercept里面Invocation可以获取到MappedStatement再获取到statementId这个statementId就是mapper的方法名如com.test.UserMapper.saveUser;那么我们配置个拦截列表如果列表包含sql的statementId就打印sql否则不打印。OverridepublicObjectintercept(Invocationinvocation)throwsThrowable{MappedStatementmappedStatement(MappedStatement)invocation.getArgs()[0];ObjectparameterObjectnull;if(invocation.getArgs().length1){parameterObjectinvocation.getArgs()[1];}longstartSystem.currentTimeMillis();Objectresultinvocation.proceed();StringstatementIdmappedStatement.getId();logger.info(statementIdstatementId);// apache日志写法// 这个不拦截所有只有配置的方法才拦截if(interceptorMethodSet.contains(statementId)){BoundSqlboundSqlmappedStatement.getBoundSql(parameterObject);ConfigurationconfigurationmappedStatement.getConfiguration();StringsqlgetSql(boundSql,parameterObject,configuration);longendSystem.currentTimeMillis();longtimingend-start;if(logger.isInfoEnabled()||logger.isDebugEnabled()){// 这里打印sql}}else{// 不进行拦截}returnresult;}拦截器日志排查过程半天不打印sql搞了半天居然是Value注入set的问题改为注入string问题解决因为都可以用contains方法。这里不生效是哪里的问题呢?? // TODO1、按道理set肯定是可以注入的那么可能和值中间有.(点号)有关。2、和mybatis的statementId有关难道值中途改变了?感觉更加不像。3、和log的类型有关?apache日志还是log4j感觉这个更荒谬更不太可能。最后找到问题了居然是log打印的写法不对也是无语了。apache log的写法 不支持{}大括号的写法 log.info(sql);// apache log的写法 不支持{}大括号的写法slf4j的log写法 支持大括号的写法 log.info(sql{},sql);//mybatis打印的时间一定准吗?这个问题不能直接答准或不准。要有分析。mybatis执行语句的链路。1、从连接池获取连接。2、使用连接。3、归还连接。从日志来看是1、打印prepare语句。 # 1和2一般会一起打印出来。如果1、2没打印出来说明是僵死连接session就没创建成功。2、打印参数。3、打印执行结果。如果3和1、2时间间隔很长(1) 是慢sql拿出sql在数据库执行下如果也慢那就是慢sql。(2) 如果数据库执行不慢那就是其他问题例如连接池占满了或数据库响应慢。实际遇到过的场景1、自己加了sql拦截器加的不好全局sql都贼慢 # 去掉拦截器问题解决2、应用配置的连接池数太小排队等连接池耗费时间3、数据库负载到未响应的节点一直不响应BaseTypeHandler类型转换这个需要了解下它一直在默默运转虽然我们感知不到。机制不管你用它来做什么BaseTypeHandler的核心永远只有两个动作重写它的4个核心方法其实就是在做这两件事从Java到数据库写重写setNonNullParameter方法。比如把明文变密文、把List变JSON字符串然后通过ps.setString(...)等方法交给数据库。从数据库到Java读重写getNullableResult方法。比如把密文变回明文、把JSON字符串变回List然后return给你的Java对象。批量模式(批量插入)批量模式主要有两种1、mybatis原生 # 即 openSession的时候指定batch模式2、mybatis-plus的batch方法 # 内置了batch模式注这个容易有个误区认为批量模式就是for循环单条执行只不过写到一个方法里面了。实际并不是这么简单batch模式是先把数据缓存再一次性写入所以性能会大幅度提高。批量模式-原生try(SqlSessionsqlSessionsqlSessionFactory.openSession(ExecutorType.BATCH)){DimApiCallLogMapperbatchMappersqlSession.getMapper(DimApiCallLogMapper.class);for(DimApiCallLogl:sub){batchMapper.insert(l);}sqlSession.flushStatements();sqlSession.commit();}catch(Exceptione){log.error(批量新增维度API调用日志失败本批{}, serialNo{}, callStatus{},sub.size(),sub.get(0).getSerialNo(),sub.get(0).getCallStatus(),e);}批量模式-mybatis-plusbatch方法内置了批量机制所以性能本身就是比较高的。批量模式-处理大文本时性能较低实际遇到过3个longtext字段1000条批量一次要12s。longtext本来就慢单条也慢批量更慢时间会消耗在组装数据cpu和内存拆解等写库是甚至也要写多页。例如3个字段如果都超过了16k那么一次插入要4次io(1次主数据3次扩展页)性能慢很多。其他文档一文搞懂Mybatis执行原理 # 这篇文章不错mybatis相关文档https://mybatis.org/mybatis-3/https://mybatis.org/mybatis-3/logging.html # 日志锚点https://mybatis.plus/ # mybatis-plus文档
返回列表