1、mybatis分页方式
-- 原始方法(mysql数据库)
select * from table limit 5; --返回前5行
select * from table limit 0,5; --同上,返回前5行
select * from table limit 5,10; --返回6-15行
-- 原始方法(oracle数据库可以使用 rownum)
SELECT * FROM
(SELECT ROWNUM R,t1.* From Sys_option where rownum < 30 ) t2
Where t2.R >= 10
从第十行开始算,返回20条记录
-- 拦截StatementHandler,其实质还是在最后生成limit语句
详见:www.cnblogs.com/jcli/archive/2011/08/09/2132222.html
-- 使用PageHelper插件,这是目前比较常见的方法:
原文:www.cnblogs.com/digdeep/p/4608933.html
2、# #{}和${}区别
#{} 是预编译处理,像传进来的数据会加个" "(#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号)
${} 就是字符串替换。直接替换掉占位符。$方式一般用于传入数据库对象,例如传入表名.
使用 ${} 的话会导致 sql 注入。什么是 SQL 注入呢?比如 select * from user where id = ${value}
value 应该是一个数值吧。然后如果对方传过来的是 001 and name = tom。这样不就相当于多加了一个条件嘛?
把SQL语句直接写进来了。如果是攻击性的语句呢?001;drop table user,直接把表给删了
所以为了防止 SQL 注入,能用 #{} 的不要去用 ${}
如果非要用 ${} 的话,那要注意防止 SQL 注入问题,可以手动判定传入的变量,进行过滤,一般 SQL 注入会输入很长的一条 SQL 语句
3、MyBatis接口绑定的几种方式
接口绑定有两种方式
1、使用注解,在接口的方法上面添加@Select@Update等注解,里面写上对应的SQL语句进行SQL语句的绑定。
2、通过映射文件xml方式进行绑定,指定xml映射文件中的namespace对应的接口的全路径名
4、MyBatis模糊查询
-- 方式1 ($ 这种方式,简单,但是无法防止SQL注入)
LIKE '%${name}%'
-- 方式2 (#)
like "%"#{name}"%"
-- 方式3(concat函数)
AND name LIKE CONCAT(CONCAT('%',#{name},'%'))
-- 方式4 (bind标签)
<select id="searchStudents" resultType="com.example.entity.StudentEntity"
parameterType="com.example.entity.StudentEntity">
<bind name="pattern1" value="'%' + _parameter.name + '%'" />
<bind name="pattern2" value="'%' + _parameter.address + '%'" />
SELECT * FROM test_student
<where>
<if test="age != null and age != '' and compare != null and compare != ''">
age
${compare}
#{age}
</if>
<if test="name != null and name != ''">
AND name LIKE #{pattern1}
</if>
<if test="address != null and address != ''">
AND address LIKE #{pattern2}
</if>
</where>
ORDER BY id
</select>
-- 方式5 (硬编码)
param.setUsername("%CD%"); 在 java 代码中传参的时候直接写上
<if test="username!=null"> AND username LIKE #{username}</if>
5、# mapper.XML对应一个DAO接口,DAO是否可以重载?
不能重载,方法名对应的 mapper.xml 文件里的一个 id,这个与方法名对应,系统会根据 namespace+id 找到对应的方法对应。
Dao 接口即 Mapper 接口。接口的全限名,就是映射文件中的 namespace 的值;接口的方法名,
就是映射文件中 Mapper 的 Statement 的 id 值;
接口方法内的参数,就是传递给 sql 的参数。Mapper 接口是没有实现类的,当调用接口方法时,
接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个 MapperStatement。
在 Mybatis 中,每一个、、、标签,都会被解析为一个MapperStatement 对象。
Mapper 接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。Mapper 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK动态代理为 Mapper 接口生成代理对象 proxy,代理对象会拦截接口方法,转而执行 MapperStatement 所代表的 sql,然后将 sql 执行结果返回。
6、 A标签引用B标签,B标签可以定义在A标签后面吗?
虽然 Mybatis 解析 Xml 映射文件是按照顺序解析的,但是,被引用的 B 标签依然可以定义在任何地方,Mybatis 都可以正确识别。
Mybatis 解析 A 标签时,发现引用了 B 标签,未解析到 B 标签,此时会把 A 标签标记为未解析状态;继续解析下面内容,把剩下解析完之后,再解析标记为未解析的标签;
此时已解析到 B 标签,此时再解析A标签时,B标签已经存在,A 标签也就顺利解析完成。
7、不同映射文件中的id是否可以重复?
可以重复,但是需要映射文件的namespace
不同。
8、MyBatis是否可以映射到枚举类
Mybatis 可以映射枚举类。
不单可以映射枚举类,Mybatis 可以映射任何对象到表的一列上。映射方式为自定义一个 TypeHandler ,实现 TypeHandler 的 setParameter()
和 getResult()
接口方法。
TypeHandler 有两个作用,一是完成从 javaType 至 jdbcType 的转换,二是完成 jdbcType 至 javaType 的转换,体现为 setParameter()
和 getResult()
两个方法,分别代表设置 sql 问号占位符参数和获取列查询结果。
9、# MyBatis如何获取自动生成的主键id
-- MySQL:Mapper 文件 insert 语句设置
<insert id="insertname" useGeneratedKeys="true" keyProperty="id">
insert into names (name) values (#{name})
</insert>
-- Oracle:Mapper 文件 insert 语句增加
<insert id="insertStudent" parameterType="org.school.entity.Student">
insert into student(name,age,sex) value(#{name},#{age},#{birthday},#{sex})
<selectKey resultType="Long" keyProperty="id">
select Max(id) from student
</selectKey>
</insert>
针对 Oracle 数据库,还可以使用@SelectKey 注解来为任意SQL语句来指定主键值,作为主键列的值。
resultType="Long" 指定了返回值的类型;
keyProperty="id" 指定了要将自动生成的主键值赋给 id 属性;
select Max(id) from student 即选择当前表中最大的id作为主键id,实现了自增效果
-- 获取序列号(oracle)
-- 创建sequence
CREATE SEQUENCE DATAGOVSERVER.T_IAM_REFRENCE_DATA_ID_SEQ INCREMENT 1 MINVALUE 1 START 2 NO CACHE NO CYCLE;
-- dao接口
@Select("select ${sequenceName}.NEXTVAL FROM DUAL")
Long getSequenceId(@Param("sequenceName")String sequenceName);
10、MyBatis传递多个参数
-- 使用map接口传递参数
-- 使用注解传递多个参数注解@Param(org.apache.ibatis.annotations.Param)
-- 通过Java Bean传递多个参数
11、MyBatis缓存机制
- 一级缓存
一级缓存为 SqlSession 缓存,缓存的数据只在 SqlSession 内有效。在操作数据库的时候需要先创建 SqlSession 会话对象,在对象中有一个 HashMap 用于存储缓存数据,此 HashMap 是当前会话对象私有的,别的 SqlSession 会话对象无法访问。
具体流程:
第一次执行 select 完毕会将查到的数据写入 SqlSession 内的 HashMap 中缓存起来
第二次执行 select 会从缓存中查数据,如果 select 同传参数一样,那么就能从缓存中返回数据,不用去数据库了,从而提高了效率
注意:
1、如果 SqlSession 执行了 DML 操作(insert、update、delete),并 commit 了,那么 mybatis 就会清空当前 SqlSession 缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现差异
2、当一个 SqlSession 结束后那么他里面的一级缓存也就不存在了, mybatis 默认是开启一级缓存,不需要配置
3、 mybatis 的缓存是基于 [namespace:sql语句:参数] 来进行缓存的,意思就是, SqlSession 的 HashMap 存储缓存数据时,是使用 [namespace:sql:参数] 作为 key ,查询返回的语句作为 value 保存的
- 二级缓存
二级缓存是 mapper
级别的缓存,也就是同一个 namespace 的 mapper.xml ,当多个 SqlSession 使用同一个 Mapper 操作数据库的时候,得到的数据会缓存在同一个二级缓存区域
二级缓存默认是没有开启的。需要在 setting 全局参数中配置开启二级缓存
开启二级缓存步骤:
1、conf.xml
配置全局变量开启二级缓存
<settings>
<setting name="cacheEnabled" value="true"/>默认是false:关闭二级缓存
<settings>
2、在 userMapper.xml
中配置
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>当前mapper下所有语句开启二级缓存
这里配置了一个 FIFO
缓存,并每隔60秒刷新,最大存储512个对象,而返回的对象是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。可用的收回策略有,默认的是LRU
:
-
LRU
- 最近最少使用的;移除最长时间不被使用的对象。 -
FIFO
- 先进先出;按对象进入缓存的顺序来移除它们。 -
SOFT
- 软引用;移除基于垃圾回收器状态和软引用规则的对象 -
WEAK
- 弱引用;更积极地移除基干垃圾收集器状态和弱引用规则的对象
若想禁用当前select
语句的二级缓存,添加 useCache="false"
修改如下:
<select id="getCountByName" parameterType="java.util.Map" resultType="INTEGER" statementType="CALLABLE" useCache="false">
具体流程:
1.当一个 sqlseesion
执行了一次 select
后,在关闭此 session
的时候,会将查询结果缓存到二级缓存
2.当另一个 sqlsession
执行 select
时,首先会在他自己的一级缓存中找,如果没找到,就回去二级缓存中找,找到了就返回,就不用去数据库了,从而减少了数据库压力提高了性能
注意:
1、如果 SqlSession
执行了 DML 操作(insert、update、delete)
,并 commit
了,那么 mybatis
就会清空当前 mapper
缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现差异
2、 mybatis
的一级缓存是基于 [namespace:sql语句:参数]
来进行缓存的,意思就是,SqlSession
的 HashMap
存储缓存数据时,是使用 [namespace:sql:参数]
作为 key
,查询返回的语句作为 value
保存的。
12、MyBatis时间timestamp做条件进行查询
首先要将条件 转换为 时间戳:
long startTime = TimeUtil.parseTimestamp(start);
long endTime = TimeUtil.parseTimestamp(end);
/*对应工具类*/
public static long parseTimestamp(String datetime){
try{
SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = dateformat.parse(datetime);
return date.getTime()/1000;
}catch(Exception e){
e.printStackTrace();
}
return 0;
}
然后Mapper.xml中 使用BETWEEN and 和 to_timestamp:
<if test="startDate !=null and startDate !='' and endDate !=null and endDate !=''">
AND tdnm.create_time BETWEEN to_timestamp(#{startDate}) AND to_timestamp(#{endDate})
</if>
评论