MyBatis 笔记
配置
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
dbcp2:
driver-class-name: com.mysql.cj.jdbc.Driver
password: ***
url: jdbc:mysql://127.0.0.1:3306/pzx?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
username: app
max-total: 1024
Config文件
@Component
@ConfigurationProperties("spring.datasource.dbcp2")
class MySqlConfig {
var driverClassName = "com.mysql.jdbc.Driver"
var username = ""
var password = ""
var url = ""
var maxTotal = 0;
var maxIdel = 0;
var maxWaitMillis = 0L
@Bean
fun dataSource(): BasicDataSource {
println("BasicDataSource inited: ${url}")
val dataSource = BasicDataSource()
dataSource.driverClassName = driverClassName
dataSource.url = url
dataSource.username = username
dataSource.password = password
dataSource.maxTotal = maxTotal
dataSource.maxIdle = maxIdel
dataSource.maxWaitMillis = maxWaitMillis
dataSource.setValidationQuery("SELECT 1")
dataSource.testOnBorrow = true
return dataSource
}
}
@Component
@AutoConfigureAfter(MySqlConfig::class)
class MyBatisSessionConfig {
@Bean
fun mapperScannerConfigurer(): MapperScannerConfigurer {
val mapperScannerConfigurer = MapperScannerConfigurer()
//获取之前注入的beanName为sqlSessionFactory的对象
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory")
//指定xml配置文件的路径
mapperScannerConfigurer.setBasePackage("pzx.db.mybatis.mapper")
return mapperScannerConfigurer
}
}
@Configuration
//加上这个注解,使得支持事务
@EnableTransactionManagement
class MyBatisConfig : TransactionManagementConfigurer {
@Autowired
private var dataSource: DataSource? = null
override fun annotationDrivenTransactionManager(): PlatformTransactionManager {
return DataSourceTransactionManager(dataSource!!)
}
@Bean(name = arrayOf("sqlSessionFactory"))
fun sqlSessionFactoryBean(): SqlSessionFactory? {
val bean = SqlSessionFactoryBean()
bean.setDataSource(dataSource)
try {
return bean.`object`
} catch (e: Exception) {
e.printStackTrace()
throw RuntimeException(e)
}
}
@Bean
fun sqlSessionTemplate(sqlSessionFactory: SqlSessionFactory): SqlSessionTemplate {
return SqlSessionTemplate(sqlSessionFactory)
}
}
Mapper文件
@Mapper
interface CityMapper {
@Select("select * from s_city where code = #{code}")
@Results(value = arrayOf(Result(column = "password", property = "password")))
fun findByCode(@Param("code") code: String): SysCity?
}
调用
@Autowired
lateinit var ds : CityMapper
@GetMapping("/testMySql")
fun testMySql(request: HttpServletRequest): String {
var e = ds.findByCode("110")
return request.UserId;
}
源码跟踪
关于缓存参考:
https://www.jianshu.com/p/c553169c5921
而我想要的缓存是:
- 拦截 query , update
- query 时,自定义 cacheKey , 及缓存策略。
- update 时,清空缓存。
参考: https://blog.csdn.net/mingjia1987/article/details/79424272
依次执行:
-
MapperMethod.execute -> sqlSession.selectOne
-
SqlSessionTemplate.selectOne
-
DefaultSqlSession.selectOne
-
DefaultSqlSession.selectList ->
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);到这里, Configuration 出现了。
-
CachingExecutor.query ->
createCacheKey
query出现在Sql及CacheKey: 383339099:1114119213:pzx.db.mybatis.mapper.CityMapper.findByCode:0:2147483647:select * from s_city where code = ?:110:SqlSessionFactoryBean
分为以下部分: hashcode:checksum:各个部分。 前面的 hashcode:checksum 可以表示唯一了, 添加后面的部分, 是为了描述元数据。个人感觉后面部分可以简化为: 排序关联表(主键的唯一值) 的方式。唯一值仅在关联表是一个,且根据主键查询的情况。 -
CachingExecutor.query ->
MappedStatement.getCache
SimpleExecutor.query可惜 MappedStatement.getCache 返回了空。没走缓存。
-
BaseExecutor.query ->
PerpetualCache: localCache .getObject
else queryFromDatabase最后执行:
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}默认配置 configuration.localCacheScope = LocalCacheScope.SESSION ,简单来说,是一次连接一个会话,每个会话有独立的缓存数据 , 这里也使用了缓存。也返回了空,因为第二次刷新页面,是一个新的会话。
localCache .getObject 是网上说的一级缓存, 也就是说,上面的 MappedStatement.getCache 是二级缓存。 逻辑:
先从二级缓存查,查不到再从一级缓存查。
这是有道理的: 因为一级缓存无法感知外部数据变化,可能有脏数据。那么如果外部数据由于 update 等操作,把缓存删掉,再从一级缓存查,就查出脏数据了。一级缓存的破坏是怎样进行的? 应该在外部 update 等 更新操作事件后, 把所有关联的一级缓存清空。
应该在 update , delete, insert 操作之后, 自动清空所有的相关表的一级缓存, 待验证。 -
BaseExecutor.queryFromDatabase ->
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
-
SimpleExecutor.doQuery
-
StatementHandler.query
-
RoutingStatementHandler.query
-
PreparedStatementHandler.query ->
PreparedStatement.execute
DefaultResultSetHandler.handleResultSetsPreparedStatement.execute 是真正的执行。
DefaultResultSetHandler.handleResultSets 应该是缓存数据的。 -
DefaultResultSetHandler.handleResultSets ->
handleResultSet ->
storeObject -> callResultHandler -> 数据保存在 : DefaultResultContext.resultObject -> DefaultResultHandler.handleResult 数据也存在了 DefaultResultHandler.list 中。
生成器
在项目根目录,建一个 lib 文件夹, 里面放:
- mysql-connector-java-6.0.6.jar
- mybatis-generator-core-1.3.6.jar
.\src\main\resources\generator\generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<classPathEntry location=".\lib\mysql-connector-java-6.0.6.jar" />
<context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
<!--<plugin type="tk.mybatis.mapper.generator.MapperPlugin">-->
<!--<property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>-->
<!--<!– caseSensitive默认false,当数据库表名区分大小写时,可以将该属性设置为true –>-->
<!--<property name="caseSensitive" value="true"/>-->
<!--</plugin>-->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://192.168.3.1:3306/xy_broker"
userId="root"
password="">
</jdbcConnection>
<javaModelGenerator targetPackage="com.xyauto.interact.broker.server.entity" targetProject=".\src\main\java"/>
<sqlMapGenerator targetPackage="mapper" targetProject=".\src\main\resources"/>
<!--<javaClientGenerator targetPackage="com.xyauto.interact.broker.server.dao" targetProject="D:\xycode-git\broker\broker-server\src\main\java" type="XMLMAPPER" />-->
<table tableName="statistics_clue_broker_day" >
</table>
</context>
</generatorConfiguration>
Java -jar .\lib\mybatis-generator-core-1.3.6.jar -configfile .\src\main\resources\generator\generatorConfig.xml -overwrite
日志
@Bean
fun abc() : SqlSessionFactory {
SqlSessionFactory fac = factory.getObject();
fac.getConfiguration().setLogImpl(MyBatisLog.class);
return fac;
}
package com.xingyuanauto.api.pic.mybatis;
import org.apache.ibatis.logging.Log;
/**
* Created by yuxh on 2018/8/27
*/
public class MyBatisLog implements Log {
private String action = "";
public MyBatisLog(String actionClass) {
String[] ary = actionClass.split("\\.");
this.action = ary[ary.length - 1];
}
@Override
public boolean isDebugEnabled() {
return true;
}
@Override
public boolean isTraceEnabled() {
return true;
}
@Override
public void error(String s, Throwable throwable) {
System.out.println(s);
}
@Override
public void error(String s) {
System.out.println(s);
}
@Override
public void debug(String s) {
System.out.println(s);
}
@Override
public void trace(String s) {
System.out.println(s);
}
@Override
public void warn(String s) {
System.out.println(s);
}
}
配置
<logger name="java.sql.Connection" level="DEBUG">
<appender-ref ref="STDOUT"/>
</logger>
<logger name="java.sql.Statement" level="DEBUG">
<appender-ref ref="STDOUT"/>
</logger>
<logger name="java.sql.PreparedStatement" level="DEBUG">
<appender-ref ref="STDOUT"/>
</logger>
<logger name="org.apache.ibatis" level="DEBUG">
<appender-ref ref="STDOUT"/>
</logger>
<logger name="java.sql" level="debug">
<appender-ref ref="STDOUT"/>
</logger>
设置参数:
mapUnderscoreToCamelCase: true -> 数据库自动映射到小驼峰字段.
作者:NewSea 出处:http://newsea.cnblogs.com/
QQ,MSN:iamnewsea@hotmail.com 如无特别标记说明,均为NewSea原创,版权私有,翻载必纠。欢迎交流,转载,但要在页面明显位置给出原文连接。谢谢。 |