标签:总结 缓存 name 超级 user SQL Mybatis where id
mybatis
什么是MyBatis?
MyBatis是一个可以自定义SQL、存储过程和高级映射的持久层框架。
MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解来配置和映射原生信息,将接口和Java的POJOs(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
为什么要使用MyBatis?
无论是Mybatis、Hibernate都是ORM的一种实现框架,都是对JDBC的一种封装。
持久层中的几种技术:
Hibernate(一个比较老旧的框架)
优点:用起来十分舒服,sql代码都不用写 缺点:处理复杂业务时,灵活度差, 复杂的HQL难写难理解,例如多表查询的HQL语句 JDBC
优点:易理解,几个固定的步骤 缺点:开发起来太麻烦,什么都需要自己写。
MyBatis工作流程
通过Reader对象读取MyBatis映射文件 通过SqlSessionFactoryBuilder对象创建SqlSessionFactory对象 获取当前线程的SQLSession 事务默认开启 通过SQLSession读取映射文件中的操作编号,从而读取SQL语句 提交事务 关闭资源 Mybatis中的事务是默认开启的,因此我们在完成操作以后,需要我们手动去提交事务!
#{}与${}的区别
#{}解析传递进来的参数数据。 ${}对传递进来的参数原样拼接在SQL中。 #{}是预编译处理,${}是字符串替换。 使用#{}可以有效的防止SQL注入,提高系统安全性。
//使用#{}
<insert id="save" parameterType="com.zhiyou100.mybatis.pojo.User" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
insert into user(name,password,phone,age,sex) values(#{name},#{password},#{phone},#{age},#{sex})
</insert>
//控制台显示日志信息
DEBUG [main] - ==> Preparing: insert into user(name,password,phone,age,sex) values(?,?,?,?,?)
DEBUG [main] - ==> Parameters: www(String), 123456(String), 111111(String), 18(Integer), true(Boolean)
DEBUG [main] - <== Updates: 1
User [id=8, name=www, password=123456, phone=111111, age=18, sex=true]
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//使用${}
<insert id="save" parameterType="com.zhiyou100.mybatis.pojo.User" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
insert into user(name,password,phone,age,sex) values('${name}','${password}','${phone}','${age}',${sex})
</insert>
//控制台显示日志信息
DEBUG [main] - ==> Preparing: insert into user(name,password,phone,age,sex) values('www','123456','111111','18',true)
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Updates: 1
User [id=9, name=www, password=123456, phone=111111, age=18, sex=true]
在MyBatis里面配置外部资源文件
-
在全局配置文件里面利用properties标签
<properties resource="类路径"></properties>
-
在src类路径下放入资源文件\
<properties url="网络路径或者本地磁盘路径"></properties>
-
配置数据源
-
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
第1种: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。
<select id="select" parameterType="java.lang.Integer" resultMap="userResultMap">
select user_id as id, user_name as name, user_password as password, user_phone as phone, user_age as age, user_sex as sex from user where user_id=#{id}
</select>第2种: 通过来映射字段名和实体类属性名的一一对应的关系。
<select id="select" parameterType="java.lang.Integer" resultMap="userResultMap">
select id as user_id,name as user_name,password as user_password,phone as user_phone,age as user_age,sex as user_sex from user where id=#{id}
</select>
<resultMap type="com.zhiyou100.mybatis.pojo.User" id="userResultMap">
<!–用id属性来映射主键字段–>
<id property="id" column="user_id"/>
<!–用result属性来映射非主键字段,property为实体类属性名,column为数据表中的属性–>
<result property="name" column="user_name"/>
<result property="password" column="user_password"/>
<result property="phone" column="user_phone"/>
<result property="age" column="user_age"/>
<result property="sex" column="user_sex"/>
</resultMap>
如何获取自动生成的(主)键值?
在<insert>标签中使用 useGeneratedKeys 和 keyProperty 两个属性来获取自动生成的主键值。
示例:
<insert id="insertname" usegeneratedkeys="true" keyproperty="id">
insert into names (name) values (#{name})
</insert>
动态sql
MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑。 MyBatis中用于实现动态SQL的元素主要有:
-
if 语句 (简单的条件判断)
<select id="dynamicIfTest" parameterType="Blog" resultType="Blog">
select * from t_blog where 1 = 1
<if test="title != null">
and title = #{title}
</if>
<if test="content != null">
and content = #{content}
</if>
<if test="owner != null">
and owner = #{owner}
</if>
</select>
解析:
如果你提供了title参数,那么就要满足title=#{title},同样如果你提供了Content和Owner的时候,它们也需要满足相应的条件,之后就是返回满足这些条件的所有Blog,这是非常有用的一个功能。
以往我们使用其他类型框架或者直接使用JDBC的时候, 如果我们要达到同样的选择效果的时候,我们就需要拼SQL语句,这是极其麻烦的,比起来,上述的动态SQL就要简单多了。 -
choose (when,otherwize) ,相当于java 语言中的 switch ,与 jstl 中的choose 很类似.
<select id="dynamicChooseTest" parameterType="Blog" resultType="Blog">
select * from t_blog where 1 = 1
<choose>
<when test="title != null">
and title = #{title}
</when>
<when test="content != null">
and content = #{content}
</when>
<otherwise>
and owner = "owner1"
</otherwise>
</choose>
</select>
when元素表示当when中的条件满足的时候就输出其中的内容,跟JAVA中的switch效果差不多的是按照条件的顺序,当when中有条件满足的时候,就会跳出choose,即所有的when和otherwise条件中,只有一个会输出,当所有的我很条件都不满足的时候就输出otherwise中的内容。所以上述语句的意思非常简单,当title!=null的时候就输出and titlte = #{title},不再往下判断条件,当title为空且content!=null的时候就输出and content = #{content},当所有条件都不满足的时候就输出otherwise中的内容。 -
trim (对包含的内容加上 prefix,或者 suffix 等,前缀,后缀)
<select id="dynamicTrimTest" parameterType="Blog" resultType="Blog">
select * from t_blog
<trim prefix="where" prefixOverrides="and |or">
<if test="title != null">
title = #{title}
</if>
<if test="content != null">
and content = #{content}
</if>
<if test="owner != null">
or owner = #{owner}
</if>
</trim>
</select>
trim元素的主要**功能**是可以**在自己包含的内容前加上某些前缀**,**也可以在其后加上某些后缀**,与之对应的属性是prefix和suffix;可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是prefixOverrides和suffixOverrides;正因为trim有这样的功能,所以我们也可以非常简单的利用trim来代替where元素的功能。trim标记是一个格式化的标记,可以完成set或者是where标记的功能,如下代码:
select * from user
<trim prefix="WHERE" prefixoverride="AND |OR">
<if test="name != null and name.length()>0">
AND name=#{name}
</if>
<if test="gender != null and gender.length()>0">
AND gender=#{gender}
</if>
</trim>
假如说name和gender的值都不为null的话打印的SQL为:select * from user where name = 'xx' and gender = 'xx'
在红色标记的地方是不存在第一个and的,上面两个属性的意思如下:
prefix:前缀
prefixoverride:去掉第一个and或者是orupdate user
<trim prefix="set" suffixoverride="," suffix=" where id = #{id} ">
<if test="name != null and name.length()>0">
name=#{name} ,
</if>
<if test="gender != null and gender.length()>0">
gender=#{gender} ,
</if>
</trim>
假如说name和gender的值都不为null的话打印的SQL为:update user set name='xx' , gender='xx' where id='x'
在红色标记的地方不存在逗号,而且自动加了一个set前缀和where后缀,上面三个属性的意义如下,其中prefix意义如上:
suffixoverride:去掉最后一个逗号(也可以是其他的标记,就像是上面前缀中的and一样)
suffix:后缀 -
where (主要是用来简化sql语句中where条件判断的,能智能的处理 and or ,不必担心多余导致语法错误)
<select id="dynamicWhereTest" parameterType="Blog" resultType="Blog">
select * from t_blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="content != null">
and content = #{content}
</if>
<if test="owner != null">
and owner = #{owner}
</if>
</where>
</select>
where元素的作用是会在写入where元素的地方输出一个where,另外一个好处是你不需要考虑where元素里面的条件输出是什么样子的,MyBatis会智能的帮你处理,如果所有的条件都不满足那么MyBatis就会查出所有的记录,如果输出后是and 开头的,MyBatis会把第一个and忽略,当然如果是or开头的,MyBatis也会把它忽略;此外,在where元素中你不需要考虑空格的问题,MyBatis会智能的帮你加上。像上述例子中,如果title=null, 而content != null,那么输出的整个语句会是select * from t_blog where content = #{content},而不是select * from t_blog where and content = #{content},因为MyBatis会智能的把首个and 或 or 给忽略。 -
set (主要用于更新时)
<update id="dynamicSetTest" parameterType="Blog">
update t_blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="content != null">
content = #{content},
</if>
<if test="owner != null">
owner = #{owner}
</if>
</set>
where id = #{id}
</update>
set元素主要是用在更新操作的时候,它的主要功能和where元素其实是差不多的,主要是在包含的语句前输出一个set,然后如果包含的语句是以逗号结束的话将会把该逗号忽略,如果set包含的内容为空的话则会出错。有了set元素我们就可以动态的更新那些修改了的字段。 -
foreach (在实现 mybatis in 语句查询时特别有用)
foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach元素的属性主要有item,index,collection, open,separator,close。
(1)item表示集合中每一个元素进行迭代时的别名。
(2)index指定一个名字,用于表示在迭代过程中,每次迭代到的位置。
(3)open表示该语句以什么开始。
(4)separator表示在每次进行迭代之间以什么符号作为分隔符。
(5)close表示以什么结束。
在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:
(1)如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
(2)如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
(3)如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key。
单参数List的类型
<select id="dynamicForeachTest" resultType="com.mybatis.entity.User">
select * from t_user where id in
<foreach collection="list" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
-----------------------------------------------------------------------------------------------------
上述collection的值为list,对应的Mapper是这样的:
/**mybatis Foreach测试 */
public List<User> dynamicForeachTest(List<Integer> ids);数组类型的参数
<select id="dynamicForeach2Test" resultType="com.mybatis.entity.User">
select * from t_user where id in
<foreach collection="array" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
------------------------------------------------------------------------------------------------------
对应mapper:
public List<User> dynamicForeach2Test(int[] ids);Map类型的参数
<select id="dynamicForeach3Test" resultType="com.mybatis.entity.User"> select * from t_user where username like '%${username}%' and id in <foreach collection="ids" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </select> ----------------------------------------------------------------------------------------------------- mapper 应该是这样的接口: /**mybatis Foreach测试 */ public List<User> dynamicForeach3Test(Map<String, Object> params);
-
<select id="dynamicForeachTest" resultType="com.mybatis.entity.User"> select * from t_user where id in <foreach collection="list" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </select>
mybatis缓存
2.4 注意事项 由于在更新时会刷新缓存, 因此需要注意使用场合:查询频率很高, 更新频率很低时使用, 即经常使用 select, 相对较少使用delete, insert, update。
缓存是以 namespace 为单位的,不同 namespace 下的操作互不影响。但刷新缓存是刷新整个 namespace 的缓存, 也就是你 update 了一个, 则整个缓存都刷新了。
最好在 「只有单表操作」 的表的 namespace 使用缓存, 而且对该表的操作都在这个 namespace 中。 否则可能会出现数据不一致的情况。
一级缓存
-
在同一个 SqlSession 中, Mybatis 会把执行的方法和参数通过算法生成缓存的键值, 将键值和结果存放在一个 Map 中, 如果后续的键值一样, 则直接从 Map 中获取数据;
-
不同的 SqlSession 之间的缓存是相互隔离的;
-
用一个 SqlSession, 可以通过配置使得在查询前清空缓存;
-
任何的 UPDATE, INSERT, DELETE 语句都会清空缓存
-
同一个
SqlSession
对象, 在参数和 SQL 完全一样的情况先, 只执行一次 SQL 语句(如果缓存没有过期),也就是只有在参数和 SQL 完全一样的情况下, 才会有这种情况。
-
@Test public void oneSqlSession() { SqlSession sqlSession = null; try { sqlSession = sqlSessionFactory.openSession(); StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); // 执行第一次查询 List<Student> students = studentMapper.selectAll(); for (int i = 0; i < students.size(); i++) { System.out.println(students.get(i)); } System.out.println("=============开始同一个 Sqlsession 的第二次查询============"); // 同一个 sqlSession 进行第二次查询 List<Student> stus = studentMapper.selectAll(); Assert.assertEquals(students, stus); for (int i = 0; i < stus.size(); i++) { System.out.println("stus:" + stus.get(i)); } } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } 在以上的代码中, 进行了两次查询, 使用相同的 SqlSession 在日志和输出中: 第一次查询发送了 SQL 语句, 后返回了结果; 第二次查询没有发送 SQL 语句, 直接从内存中获取了结果。 而且两次结果输入一致, 同时断言两个对象相同也通过。
-
不同的sqlsession
@Test public void differSqlSession() { SqlSession sqlSession = null; SqlSession sqlSession2 = null; try { sqlSession = sqlSessionFactory.openSession(); StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); // 执行第一次查询 List<Student> students = studentMapper.selectAll(); for (int i = 0; i < students.size(); i++) { System.out.println(students.get(i)); } System.out.println("=============开始不同 Sqlsession 的第二次查询============"); // 从新创建一个 sqlSession2 进行第二次查询 sqlSession2 = sqlSessionFactory.openSession(); StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class); List<Student> stus = studentMapper2.selectAll(); // 不相等 Assert.assertNotEquals(students, stus); for (int i = 0; i < stus.size(); i++) { System.out.println("stus:" + stus.get(i)); } } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } if (sqlSession2 != null) { sqlSession2.close(); } } } 两次查询都分别从数据库中取出了数据。 虽然结果相同, 但两个是不同的对象。
-
缓存刷新
刷新缓存是清空这个 SqlSession 的所有缓存, 不单单是某个键。 @Test public void sameSqlSessionNoCache() { SqlSession sqlSession = null; try { sqlSession = sqlSessionFactory.openSession(); StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); // 执行第一次查询 Student student = studentMapper.selectByPrimaryKey(1); System.out.println("=============开始同一个 Sqlsession 的第二次查询============"); // 同一个 sqlSession 进行第二次查询 Student stu = studentMapper.selectByPrimaryKey(1); Assert.assertEquals(student, stu); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } 如果是以上, 没什么不同, 结果还是第二个不发 SQL 语句。
在此, 做一些修改, 在 StudentMapper.xml 中, 添加
flushCache=“true”
修改后的配置文件如下:
<select id="selectByPrimaryKey" flushCache="true" parameterType="java.lang.Integer" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from student where student_id=#{id, jdbcType=INTEGER} </select>
二级缓存
-
在 mybatis 中, 二级缓存有全局开关和分开关, 全局开关, 在 mybatis-config.xml 中如下配置:
<settings> <!--全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 --> <setting name="cacheEnabled" value="true"/> </settings> 默认是为 true, 即默认开启总开关。
-
分开关
分开关就是说在 *Mapper.xml 中开启或关闭二级缓存, 默认是不开启的。
-
entity实现序列化接口
public class Student implements Serializable { private static final long serialVersionUID = -4852658907724408209L; ... }
使用二级缓存
@Test public void secendLevelCacheTest() { // 获取 SqlSession 对象 SqlSession sqlSession = sqlSessionFactory.openSession(); // 获取 Mapper 对象 StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); // 使用 Mapper 接口的对应方法,查询 id=2 的对象 Student student = studentMapper.selectByPrimaryKey(2); // 更新对象的名称 student.setName("奶茶"); // 再次使用相同的 SqlSession 查询id=2 的对象 Student student1 = studentMapper.selectByPrimaryKey(2); Assert.assertEquals("奶茶", student1.getName()); // 同一个 SqlSession , 此时是一级缓存在作用, 两个对象相同 Assert.assertEquals(student, student1); sqlSession.close(); SqlSession sqlSession1 = sqlSessionFactory.openSession(); StudentMapper studentMapper1 = sqlSession1.getMapper(StudentMapper.class); Student student2 = studentMapper1.selectByPrimaryKey(2); Student student3 = studentMapper1.selectByPrimaryKey(2); // 由于我们配置的 readOnly="true", 因此后续同一个 SqlSession 的对象都不一样 Assert.assertEquals("奶茶", student2.getName()); Assert.assertNotEquals(student3, student2); sqlSession1.close(); }
结果如下:
2018-09-29 23:14:26,889 [main] DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - Created connection 242282810. 2018-09-29 23:14:26,889 [main] DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@e70f13a] 2018-09-29 23:14:26,897 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - ==> Preparing: select student_id, name, phone, email, sex, locked, gmt_created, gmt_modified from student where student_id=? 2018-09-29 23:14:26,999 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - ==> Parameters: 2(Integer) 2018-09-29 23:14:27,085 [main] TRACE [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - <== Columns: student_id, name, phone, email, sex, locked, gmt_created, gmt_modified 2018-09-29 23:14:27,085 [main] TRACE [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - <== Row: 2, 小丽, 13821378271, xiaoli@mybatis.cn, 0, 0, 2018-09-04 18:27:42.0, 2018-09-04 18:27:42.0 2018-09-29 23:14:27,093 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - <== Total: 1 2018-09-29 23:14:27,093 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper] - Cache Hit Ratio [com.homejim.mybatis.mapper.StudentMapper]: 0.0 2018-09-29 23:14:27,108 [main] DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@e70f13a] 2018-09-29 23:14:27,116 [main] DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@e70f13a] 2018-09-29 23:14:27,116 [main] DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - Returned connection 242282810 to pool. 2018-09-29 23:14:27,124 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper] - Cache Hit Ratio [com.homejim.mybatis.mapper.StudentMapper]: 0.3333333333333333 2018-09-29 23:14:27,124 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper] - Cache Hit Ratio [com.homejim.mybatis.mapper.StudentMapper]: 0.5
以上结果, 分几个过程解释:
第一阶段:
在第一个 SqlSession 中, 查询出 student 对象, 此时发送了 SQL 语句; student更改了name 属性; SqlSession 再次查询出 student1 对象, 此时不发送 SQL 语句, 日志中打印了 「Cache Hit Ratio」, 代表二级缓存使用了, 但是没有命中。 因为一级缓存先作用了。 由于是一级缓存, 因此, 此时两个对象是相同的。 调用了 sqlSession.close(), 此时将数据序列化并保持到二级缓存中。 第二阶段:
新创建一个 sqlSession.close() 对象; 查询出 student2 对象,直接从二级缓存中拿了数据, 因此没有发送 SQL 语句, 此时查了 3 个对象,但只有一个命中, 因此 命中率 1/3=0.333333; 查询出 student3 对象,直接从二级缓存中拿了数据, 因此没有发送 SQL 语句, 此时查了 4 个对象,但只有一个命中, 因此 命中率 2/4=0.5; 由于 readOnly=“true”, 因此 student2 和 student3 都是反序列化得到的, 为不同的实例。
配置详解
查看 dtd 文件, 可以看到如下约束:
<!ELEMENT cache (property*)> <!ATTLIST cache type CDATA #IMPLIED eviction CDATA #IMPLIED flushInterval CDATA #IMPLIED size CDATA #IMPLIED readOnly CDATA #IMPLIED blocking CDATA #IMPLIED >
从中可以看出:
cache 中可以出现任意多个 property子元素; cache 有一些可选的属性 type, eviction, flushInterval, size, readOnly, blocking. 2.3.1 type type 用于指定缓存的实现类型, 默认是PERPETUAL, 对应的是 mybatis 本身的缓存实现类 org.apache.ibatis.cache.impl.PerpetualCache。
后续如果我们要实现自己的缓存或者使用第三方的缓存, 都需要更改此处。
2.3.2 eviction eviction 对应的是回收策略, 默认为 LRU。
LRU: 最近最少使用, 移除最长时间不被使用的对象。
FIFO: 先进先出, 按对象进入缓存的顺序来移除对象。
SOFT: 软引用, 移除基于垃圾回收器状态和软引用规则的对象。
WEAK: 弱引用, 移除基于垃圾回收器状态和弱引用规则的对象。
2.3.3 flushInterval flushInterval 对应刷新间隔, 单位毫秒, 默认值不设置, 即没有刷新间隔, 缓存仅仅在刷新语句时刷新。
如果设定了之后, 到了对应时间会过期, 再次查询需要从数据库中取数据。
2.3.4 size size 对应为引用的数量,即最多的缓存对象数据, 默认为 1024。
2.3.5 readOnly readOnly 为只读属性, 默认为 false
false: 可读写, 在创建对象时, 会通过反序列化得到缓存对象的拷贝。 因此在速度上会相对慢一点, 但重在安全。
true: 只读, 只读的缓存会给所有调用者返回缓存对象的相同实例。 因此性能很好, 但如果修改了对象, 有可能会导致程序出问题。
2.3.6 blocking blocking 为阻塞, 默认值为 false。 当指定为 true 时将采用 BlockingCache 进行封装。
使用 BlockingCache 会在查询缓存时锁住对应的 Key,如果缓存命中了则会释放对应的锁,否则会在查询数据库以后再释放锁,这样可以阻止并发情况下多个线程同时查询数据。
-
-
mybatis分页
标签:总结,缓存,name,超级,user,SQL,Mybatis,where,id 来源: https://www.cnblogs.com/yueyueniao-gif/p/13096289.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。