动态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 mapperPUBLIC "-//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_cityfrom 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_cityfrom 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_cityfrom memberswhere 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_cityfrom memberswhere member_nick like CONCAT('%',#{keyword},'%')</select><select id="queryMemberById" resultMap="MemberMapper" useCache="true">select member_id,member_nick,member_gender,member_age,member_cityfrom memberswhere 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 {@Testpublic 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);}}@Testpublic 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);}}@Testpublic 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);}}@Testpublic 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”.