2018-10-02
insert into administrator(id,name,age,job) VALUES
(#{id},#{administrator.name},${administrator.age},#{administrator.job})
只有insert和update标签中有useGenaratedKeys和keyProperty两个属性,这两个属性是为了获取
数据库中自增字段用的,将返回的值复制给keyProperty中设置的变量,然后可以在下面的sql语句这样
使用,你发现,我的administrator只有插入了三个属性,id属性是没有插入的,这个属性是为了接收
用的,也就是说在执行完这条语句后,我调用administrator.getId()会获取到数据库自增字段id的值。
参考链接:https://blog.csdn.net/hellostory/article/details/6790248
对于不支持自动生成主键的JDBC驱动或者数据库,需要在insert标签中加上标签
例如:
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
其中属性值order需要注意,这个属性值只有两个值,一个是BEFORE,另一个是AFTER
如果设置为 BEFORE,那么它会首先选择主键,设置 keyProperty 然后执行插入语句。
如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 元素
使用这个标签的时候要注意
我们知道在include标签中有个属性refid用来指向一个sql标签的
那么这个property标签则是对sql标签中的值进行复制用的,我们来看下例子:
${tt}
这条语句返回结果是错的,它显示找不到程序员这行,
我们在引用语句左右两边都加上引号,现在我们来看下输出的sql语句:
select * from administrator where job = " 程序员 "
发现了吧,我们传入值的左右两边都有空格,也就是说,在使用字段是字符串的情况下,不能这样写,否则会出现上面的情况。
#{变量名,属性:xxxx,属性:xxx}
关于使用和不使用@Param()注释的区别
1、不使用@Param注释,参数只能有一个,并且参数是对象,这样使用#{}和使用{}取不到值
3、使用@Param注释,#{}和${}都可以取到值,并且单个参数和多个参数的情况都适用
2018-10-09
以作用域+返回值+绝对路径的方法名
e.g:
public abstract java.until.List com.dao.PersonDao.select_PersonAndJob() 这个作为key
将MapperMethod作为value
MapperMethod中有两个比较重要的内部类
1、sqlCommand //封装了SQL标签的类型:insert update delete select
2、MethodSignature //封装了方法的参数信息 返回类型信息等
mybatis缓存
1、一级缓存是通过SqlSession来实现的
图片来自链接:https://blog.csdn.net/qq_25689397/article/details/52066179
这是只有mybatis框架的情况下,缓存的大体过程
如果只有mybatis的时候,当进入到MapperMethod类中调用selectList方法的时候,进入的是DefaultSqlSession类
然后在这个类中调用执行器executor的query方法,在执行器中有个成员变量用于存储缓存,就是PerpetualCache类
这个类中的成员变量cache存储缓存,这个成员变量类型是hashmap类型,也就是mybatis的缓存数据是存储在hashmap中的
注意:获取mapperMethod
将路径作为key,通过方法cachedMapperMethod()方法来操作,如果key存在则直接取出MapperMethod对象
如果key不存在则新建一个mapperMethod对象,并将对象存入ConcurrentHashMap集合中,然后返回该对象。
接着是调用mapperMethod的execute()方法,根据mapperMethod的command的属性类型,进行相对应的操作,
基本就是sqlSession的四个操作:insert update delete select
下面以select为例
调试进入这个方法发现是进入SqlSessionTemplate类,因为我是整合好的SSM框架,所以在这个时候是spring在
对这些sqlSession进行代理操作,在调用这个方法的时候,会进入到这个类的内部类SqlSessionInterceptor
在这个类invoke方法中,我们发现,他在finally{}代码块中对sqlSession进行了关闭操作,这也是为什么我们
每一次调用sql语句,不是从缓存中取出来,而是直接跟数据库打交道,也就是每个session运行完一个方法后,就
会被销毁,即创建快销毁的也快
2、二级缓存是通过mapper的namespace实现共享
也就是不同的session对于namespace的操作是共享的,每个namespace都有一块缓存,在执行查询的时候默认不刷新缓存,
而在执行增删改的时候是默认要刷新缓存的,mybatis提供给我们两个属性进行设置,一个是useCache一个是flushCache
并且默认值如下:
增删改是没有useCache属性的,被定死了
mybatis本身的二级缓存很容易出现问题,也就是到多表操作的时候,容易出现脏数据的问题,而且mybatis本身的二级缓存
无法实现分布式具体的问题,可以查看:https://www.cnblogs.com/liouwei4083/p/6025929.html
基于此,mybatis给我们提供了可以自定义的二级缓存,也就是添加了Cache接口,实现这个接口,可以自己来实现缓存,
通常可以借助实现这个接口来使用redis做mybatis的二级缓存,然后再在配置文件里的
这样便可以实现分布式。
SSM框架整合后,我们看下调试过程
我们看下代码的实现,首先进入MapperProxy类,也就是spring代理类
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
这个方法中,最重要的是最后两行的代码,cachedMapperMethod()方法是判断当前方法路径下是否有缓存,有缓存则将缓存返回,没有缓存,则重新创建一个MapperMethod对象,并且将对象存入ConcurrentHashMap中,这也是跟单独使用mybatis的区别之一,存储容器不同。然后通过MapperMethod的方法execute()执行sql语句。
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//根据sql的类型,选择不同的执行方式
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
//当前方法调试的入口
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
我们可以看到不管是增删改查哪一个都会调用sqlSession对应的方法,程序进一步进行,会到类SqlSessionTemplate类,调用selectList()方法,在调用这个方法的时候,会先进入到这个类的内部类SqlSessionInterceptor中,也就是Session的拦截器,调用了拦截器的Invoke方法,我们看下这个方法的实现:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
//注意每次SqlSession操作数据库后,都会自动断开,这也是为什么mybatis一级缓存会失效的原因
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
看到这块基本就能了解,为什么mybatis的一级缓存在SSM框架中会失效了,也就是spring管理使用sqlSession后,每次使用都会关闭,也就是sqlSession的创建和关闭变得频繁了
参考链接:
https://www.cnblogs.com/volatileAndCrazy/p/7826331.html
https://blog.csdn.net/z69183787/article/details/78402892
https://www.cnblogs.com/winclpt/articles/7511672.html
https://blog.csdn.net/qq_25689397/article/details/52066179
https://www.cnblogs.com/linkstar/p/7182695.html