动态sql
通过动态sql实现多条件查询,这里以查询为例,实现动态sql的书写。
创建members表
创建表并插入数据:
create table members (
member_id int (11),
member_nick varchar (60),
member_gender char (15),
member_age int (11),
member_city varchar (90)
);
insert into members (member_id, member_nick, member_gender, member_age, member_city) values('1','reading','W','99','wuhan');
insert into members (member_id, member_nick, member_gender, member_age, member_city) values('2','running','W','32','changsha');
insert into members (member_id, member_nick, member_gender, member_age, member_city) values('3','talking','W','26','changsha');
insert into members (member_id, member_nick, member_gender, member_age, member_city) values('4','song','W','22','beijing');
insert into members (member_id, member_nick, member_gender, member_age, member_city) values('5','running','F','28','beijing');
commit;
创建对应的members类
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Members implements Serializable {
private int memberId;
private String memberNick;
private String memberGender;
private int memberAge;
private String memberCity;
}
创建对应的dao类
public interface MembersDao {
public List<Members> queryMemberUsingWhere(HashMap<String, Object> parms);
public List<Members> queryMemberUsingTrim(HashMap<String, Object> parms);
public List<Members> queryMemberByCity(List<String> cities);
public List<Members> queryMemberByNick(String keyword);
public Members queryMemberById(int id);
public int updateMember(@Param("id") int id, @Param("age") int age);
}
创建xml文件
编写MembersDao的map文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kingyal.dao.MembersDao">
<!-- -->
<!-- <cache readOnly="true" size=""></cache>-->
<resultMap id="MemberMapper" type="Members">
<id column="member_id" property="memberId"></id>
<result column="member_nick" property="memberNick"></result>
<result column="member_gender" property="memberGender"></result>
<result column="member_age" property="memberAge"></result>
<result column="member_city" property="memberCity"></result>
</resultMap>
<select id="queryMemberUsingWhere" resultMap="MemberMapper">
select member_id,member_nick,member_gender,member_age,member_city
from members
<!--where 自动识别and -->
<where>
<!--gender 为参数对象的属性或者是map中的key值 -->
<if test="gender != null">
and member_gender = #{gender}
</if>
<!-- > 表示大于 -->
<if test="minAge != null">
and member_age >= #{minAge}
</if>
<!-- < 表示小于 -->
<if test="maxAge != null">
and member_age <= #{maxAge}
</if>
<if test="city != null">
and member_city = #{city}
</if>
</where>
</select>
<select id="queryMemberUsingTrim" resultMap="MemberMapper">
select member_id,member_nick,member_gender,member_age,member_city
from members
<!--trim: 起补充作用
prefix:sql增加前缀
prefixOverrides:如果第一个条件中有and或者or,将会去除掉
suffix: sql增加后缀
-->
<trim prefix="where" prefixOverrides="and | or" suffix="order by member_age">
<!--gender 为参数对象的属性或者是map中的key值 -->
<if test="gender != null">
and member_gender = #{gender}
</if>
<!-- > 表示大于 -->
<if test="minAge != null">
and member_age >= #{minAge}
</if>
<!-- < 表示小于 -->
<if test="maxAge != null">
and member_age <= #{maxAge}
</if>
<if test="city != null">
and member_city = #{city}
</if>
</trim>
</select>
<select id="queryMemberByCity" resultMap="MemberMapper">
select member_id,member_nick,member_gender,member_age,member_city
from members
where member_city in
<!--
collection:指定集合的类型,这里的list实际上是com.java.util.List,在mybatis中做了映射
item:值的别名,在配置中需要用到
seperator:每个值之间的分隔符
open:以(开头
close:以)开头
最终达到以下效果:
(changsah, beijing1)
-->
<foreach collection="list" item="cityName" separator="," open="(" close=")">
#{cityName}
</foreach>
</select>
<!--
${key}:表示获取参数,先获取参数的值拼接到sql中,再编译sql,会有sql注入问题,写法:where member_nick like '%${keyword}%'
#{key}:表示获取参数,先编译sql,再将获取到的参数值拼接到sql中,写法:where member_nick like CONCAT('%',#{keyword},'%')
-->
<select id="queryMemberByNick" resultMap="MemberMapper">
select member_id,member_nick,member_gender,member_age,member_city
from members
where member_nick like CONCAT('%',#{keyword},'%')
</select>
<select id="queryMemberById" resultMap="MemberMapper" useCache="true">
select member_id,member_nick,member_gender,member_age,member_city
from members
where member_id=#{id}
</select>
<update id="updateMember" flushCache="true">
update members set member_age=#{age} where member_id=#{id};
</update>
</mapper>
将类名与实体进行映射:
将membersDao的map文件添加到mybatis配置中:
编写demo
public class MembersDaoTest {
@Test
public void queryMemberUsingWhere() {
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("gender", "W");
params.put("minAge", 20);
params.put("maxAge", 40);
params.put("city", "changsha");
MembersDao membersDao = MybatisUtil.getMapper(MembersDao.class);
List<Members> membersList = membersDao.queryMemberUsingWhere(params);
for (Members m : membersList) {
System.out.println(m);
}
}
@Test
public void queryMemberUsingTrim() {
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("gender", "W");
params.put("minAge", 20);
params.put("maxAge", 40);
params.put("city", "changsha");
MembersDao membersDao = MybatisUtil.getMapper(MembersDao.class);
List<Members> membersList = membersDao.queryMemberUsingTrim(params);
for (Members m : membersList) {
System.out.println(m);
}
}
@Test
public void queryMemberByCity() {
List<String> cities = new ArrayList<String>();
cities.add("changsha");
cities.add("beijing");
MembersDao membersDao = MybatisUtil.getMapper(MembersDao.class);
List<Members> membersList = membersDao.queryMemberByCity(cities);
for (Members m : membersList) {
System.out.println(m);
}
}
@Test
public void queryMemberByNick() {
MembersDao membersDao = MybatisUtil.getMapper(MembersDao.class);
List<Members> membersList = membersDao.queryMemberByNick("r");
for (Members m : membersList) {
System.out.println(m);
}
}
}
mybatis日志记录
常见的日志记录组件有log4j,log4j2等,mybatis内部采用log4j组件。通过日志,可以查看sql执行情况。如下是配置方式。
引入pom文件
在resource目录下创建log4j.properties文件
如下是配置内容:
# 输出debug信息到控制台
log4j.rootLogger=DEBUG,stdout
# 日志级别
log4j.logger.org.mybatis.example.BlogMapper=TRACE
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 日志的打印格式 %t:线程名称 %5p:日志级别 %n:换行 %msg:打印内容 %n:换行
log4j.appender.stdout.layout.ConversionPattern=[%t] %5p - {%msg} %n
mybatis启动时打印日志
配置第三方数据库连接池
在进行数据库操作时,mybatis支持基于数据库连接池的方式。mybatis的配置中,有一个dataSource标签:
如果type标签的属性值为POOLED,表示使用mybatis内置的连接池管理;如果想使用第三方的数据库连接池,需要进行自定义配置。
druid连接池的配置方式
引入pom文件
mybatis缓存
mybatis使用缓存机制,优化查询性能。第一次查询到的数据会放置在缓存中,第二次查询时会优先到缓存中查询,如果缓存中没有,就会到数据库查询,在数据库中查到后,同时存储到缓存中。
一级缓存
为sqlsession对象建立的缓存,每个sqlsession有单独的缓存,无需手动开启,可直接使用,多个sqlsession无法共享。如果sqlsession关闭或者清空缓存,就会重新从数据库获取数据。如果第一次查询完成后,对查询出的对象做修改,第二次查询会直接使用缓存,查询出的结果为修改后的值。如果第一次查询后,使用这个个sqlsession执行了update操作,此操作在更新数据库的同时,也会更新缓存,第二次查询出的结果为修改后的值。demo如下:
public void queryMemberByNickToTestFirstCache() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
MembersDao membersDao = sqlSession.getMapper(MembersDao.class);
List<Members> membersList1 = membersDao.queryMemberByNick("reading");
System.out.println(membersList1);
System.out.println("~~~~~~~~~~~~~~~不会查询数据库,直接从缓存获取数据~~~~~~~~~~~~");
MembersDao membersDao2 = sqlSession.getMapper(MembersDao.class);
List<Members> membersList2 = membersDao2.queryMemberByNick("reading");
System.out.println(membersList2);
}
public void queryMemberByNickToTestFirstCacheCelaning() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
MembersDao membersDao = sqlSession.getMapper(MembersDao.class);
List<Members> membersList1 = membersDao.queryMemberByNick("reading");
System.out.println(membersList1);
// 清理缓存
sqlSession.clearCache();
System.out.println("~~~~~~~~~~~~~~~缓存被清空,再次查询会访问数据库~~~~~~~~~~~~");
MembersDao membersDao2 = sqlSession.getMapper(MembersDao.class);
List<Members> membersList2 = membersDao2.queryMemberByNick("reading");
System.out.println(membersList2);
}
public void queryMemberByIdToTestFirstCacheChangingAge() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
MembersDao membersDao = sqlSession.getMapper(MembersDao.class);
Members members1 = membersDao.queryMemberById(1);
System.out.println(members1);
members1.setMemberAge(60);
System.out.println("~~~~~~~~~~~~~~~没有清理缓存,第二次查询时,age变成了60~~~~~~~~~~~~");
MembersDao membersDao2 = sqlSession.getMapper(MembersDao.class);
Members members2 = membersDao2.queryMemberById(1);
System.out.println(members2);
}
public void updateMemberToTestFirstCacheChangingAge() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
MembersDao membersDao1 = sqlSession.getMapper(MembersDao.class);
Members members1 = membersDao1.queryMemberById(1);
System.out.println(members1);
// 做了更新操作
membersDao1.updateMember(1, 99);
sqlSession.commit();
System.out.println("~~~~~~~~~~~~~~~在第一次查询后,使用代码更新了数据库,缓存也会被同步更新,第二次查询为99~~~~~~~~~~~~");
MembersDao membersDao2 = sqlSession.getMapper(MembersDao.class);
Members members2 = membersDao2.queryMemberById(1);
System.out.println(members2);
}
二级缓存
二级缓存也称为sqlSessionFactory级缓存,通过同一个factory对象获取的SqlSession可以共享二级缓存,在应用服务器中,SqlSessionFactory是单例的,因此,我们二级缓存可以实现全局共享。特性如下:
1. 二级缓存默认没有开启,需要在mybatis的配置文件中的settgng标签开启。
2. 并在需要使用二级缓存的mapper文件中配置cache标签,表示使用二级缓存。
eviction策略:
flushInterval:刷新间隔,设置的值是一个以毫秒为单位的合理时间量,默认不设置,此时缓存仅仅会在调用语句时刷新。
size:引用数目,表示缓存的大小,设置时要注意缓存对象的大小和此时的可用内存,默认是1024。
readOnly:属性可以设置为true或者false,true表示只读,只读的缓存会给所有调用者返回缓存对象的相同实例。因为他们不能被修改。false表示可读写,可读写的缓存会通过序列化返回缓存对象的拷贝,速度上会慢一些,但是更安全,默认是false。
3. 二级缓存只能缓存实现序列化接口的对象。
实例:
public void queryMemberByIdToTestSecondCacheChangingAge() {
SqlSessionFactory factory= MybatisUtil.getSqlSessionFactory();
SqlSession sqlSession = factory.openSession();
MembersDao membersDao = sqlSession.getMapper(MembersDao.class);
Members members1 = membersDao.queryMemberById(1);
System.out.println(members1);
sqlSession.commit();
System.out.println("~~~~~~~~~~~~~commit之后,就会将当前sqlsession的查询结果缓存到二级缓存~~~~~~~~~~~~~~");
SqlSession sqlSession2 = factory.openSession();
MembersDao membersDao2 = sqlSession2.getMapper(MembersDao.class);
Members members2 = membersDao2.queryMemberById(1);
System.out.println(members2);
}
也可以在查询时使用二级缓存:
1. 使用“flushCache”表示做更新操作时刷新缓存:
2. 使用“useCache”表示做查询操作时使用缓存
延迟加载
如果在mybatis开启了延迟加载,在执行子查询时(至少查询两次及以上),默认只执行第一次查询,当用到子查询的查询结果时,才会触发子查询的执行;如果无需用到子查询结果,则子查询不会执行。延迟加载的作用是减少对数据库的访问。配置如下,在子查询语句中配置fetchType为“true”.