首先我们来看一下动态sql,动态sql就是传递的参数不确定的时候,使用if,where,select,choose,set等标签,先来看一下
lib,rescources,以及utils里面文件不变,直接来看使用
直接看if跟where,if使用比较简单,就是if会有一个条件判断,如果条件满足,就会把if里面的sql语句块加入slq语句,where就是帮助我们加载一个where条件判断,并且会把拼接语句中的第一个and删除掉,接下来看一下例子
看一下UserMapper
public interface UserMapper {
List getUsers(@Param("username") String username, @Param("password") String password);
}
看一下UserMapper.xml
select * from `user`
and username=#{username}
and password=#{password}
看一下测试
@Testpublic voidtestWhere(){
SqlSession sqlSession=MyBatisUtils.openSession();
UserMapper userMapper= sqlSession.getMapper(UserMapper.class);//调用这个封装的where标签,password传入为空,可以看出,执行的sql语句就会不带后面的password
List userList = userMapper.getUsers("yang", null); //==> Preparing: select * from `user` WHERE username=?//如果全部为空,那么就会忽略所有的if字段
List userList2 = userMapper.getUsers(null, null); //==> ==> Preparing: select * from `user`//第一个if条件我们的是带有if的,而sql语句中不存在,因此可以确认,where确实是把第一个语句中的开头的and删除了。
List users = userMapper.getUsers("shi", "5678"); //==> Preparing: select * from `user` WHERE username=? and password=?
for(User user : users) {
System.out.println(user);
}
sqlSession.close();
}
where可以删除第一个语句的前置and,但是无法删除结尾的and,接下来看一下trim标签,该标签可以删除前置或后置的指定的字符串
UserMapper
//trim
List getUserList(@Param("username") String username, @Param("password") String password);
UserMapper.xml
select * from `user`
and username=#{username}
and password=#{password} and
测试类
@Testpublic voidtestTrim(){
SqlSession sqlSession=MyBatisUtils.openSession();
UserMapper userMapper= sqlSession.getMapper(UserMapper.class);//调用trim标签的映射,在if判断中第一个前置有and,最后一个语句后置and,利用trim的删除,删除掉了
List users = userMapper.getUserList("shi", "5678"); //==> Preparing: select * from `user` where username=? and password=?
for(User user : users) {
System.out.println(user);//User{id=2, username='shi', password='5678'}
}//两个传入都是空值,可以看出不符合if,不执行
List users2 = userMapper.getUserList("", ""); //==> Preparing: select * from `user`
sqlSession.close();
}
接下来看一下choose标签,这个标签的作用就是只要满足一个条件不执行其他条件了,相当于select标签
看一下mapper文件
//choose
List getUserChoose(@Param("username") String username, @Param("password") String password);
看一下UserMapper.xml文件
select * from `user`
username=#{username}
password=#{password}
1 = 1
测试一下
@Testpublic voidtestChoose(){
SqlSession sqlSession=MyBatisUtils.openSession();
UserMapper userMapper= sqlSession.getMapper(UserMapper.class);//我们可以看出这个两个参数均满足之前所说的条件,但是最终只是执行了最上面的满足条件的语句
List users = userMapper.getUserChoose("shi", "5678"); //==> Preparing: select * from `user` WHERE username=?
for(User user : users) {
System.out.println(user);
}//两个都不满足,就会执行otherwise
List users2 = userMapper.getUserList("", ""); //==> Preparing: select * from `user`
}
在实际应用时,我们会遇到传入一个主键数组列表,返回对应对象,sql语句中使用的是in(?),不能直接传入数组,因此这时候可以使用forEach标签
看一下接口文件
//forEach, 这个可以传入数组,列表,pojo对象也可以,只要是列表类的就行
List getUserByIds(@Param("idList") Integer[] ids);
映射文件
select * from `user` where id in#{ids}
看一下测试
@Testpublic voidtestForEach() {
SqlSession sqlSession=MyBatisUtils.openSession();
UserMapper userMapper= sqlSession.getMapper(UserMapper.class);//使用数组或者列表都可以,主要看java中的映射是如何定义的,可以看出,foreach会帮助我们循环传入的数据并格式化为sql需求的样子
List users = userMapper.getUserByIds(new Integer[]{11, 12, 13, 14, 15}); //==> Preparing: select * from `user` where id in ( ? , ? , ? , ? , ? ) ==> Parameters: 11(Integer), 12(Integer), 13(Integer), 14(Integer), 15(Integer)
for(User user : users) {
System.out.println(user);
}//User{id=11, username='mark', password='1111'}//User{id=12, username='mark', password='1111'}//User{id=13, username='mark', password='1111'}//User{id=14, username='mark', password='1111'}//User{id=15, username='mark', password='1111'}
}
最后我们看一下bind标签,set标签,bind标签可以取出传入的值,并进行重新处理,赋值给另外一个值,set标签会把最后一个,号去掉
看一下mapper文件
//set 与 bind
void updateUser(User user);
看一下我们的映射文件
update `user`
username=#{username},
password=#{password},
where id=#{id}
看一下测试类
@Testpublic voidupdate() {
SqlSession sqlSession=MyBatisUtils.openSession();
UserMapper userMapper= sqlSession.getMapper(UserMapper.class);
User user= newUser();
user.setUsername("yang");
user.setPassword("1234");
user.setId(2);//通过sql语句可以看出,虽然名称设置的是yang,但是我们在bind标签中拦截了username并且在最后加了一个bind,最终他们会添加上。
userMapper.updateUser(user); //==> Preparing: update `user` SET username=?, password=? where id=? ==> Parameters: yangbind(String), 1234(String), 2(Integer)
sqlSession.commit();
sqlSession.close();
}
我们来看一下include,sql标签,对于重复性语句,我们可以提出封装成一个sql语句块,然后使用include进行引用,并且可以传值
看一下mapper,定义了两个接口
//sql 与include语句
User getUserById(@Param("id") Integer id);
User getUser(@Param("id") Integer id);
看一下UserMapper.xml
where id=#{id}
where id=#{id}
select `username` from `user`
select * from `user`
看一下测试类
@Testpublic voidgetUser() {
SqlSession sqlSession=MyBatisUtils.openSession();
UserMapper userMapper= sqlSession.getMapper(UserMapper.class);//可以看出使用这个之后就会把之前的定义的sql进行拼接,并且通过property进行传值,sql语句块也接受到了,只查询username
User user2 = userMapper.getUserById(2); //==> Preparing: select `username` from `user` where id=?
System.out.println(user2); //User{id=null, username='yangbind', password='null'}
}
基本动态语句够用了,接下来看一下mybatis的缓存,mybatis缓存分为
一级缓存,只存在同一个sqlSession,对于用一个sqlsession,如果参数和sql完全一样的情况下,并且中间没有增删改,没有关闭sqlSession,没有删除缓存,并且没有超时,那么同一个sqlsession调用一个mappper对象,只会执行一次sql,剩余会走缓存取,并不会再次查询数据库,一级缓存默认就是开启的。
二级缓存,是mapper级别的缓存,顾名思义缓存只存在与yigemapper中,并且二级缓存就在命名空间中命名,二级缓存默认是不开启的,二级缓存需要配置,并且二级缓存中实现返回的PoJo必须是可序列化,也就是必须实现Serializable接口。二级缓存我们一般使用第三方,因为mybatis并不是专业做缓存的。
首先我们先来配置一下,把一级缓存,二级缓存的配置一下
先看一下配置文件,直接在settings李 main开启二级缓存,一级缓存是开启的
...
看一下pojo类,如果需要开启二级缓存,这个类需要实现接口implements
public class User implementsSerializable {privateInteger id;privateString username;privateString password;
.....
}
接下来看一下我我们的marpper
packagecom.yang.mapper;importcom.yang.domain.User;public interfaceUserMapper2 {
User getUserById(Integer id);voidinsertUser(User user);
}
/p>
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
select * from `user` where id=#{id}
insert into `user`(username, password) values (#{username}, #{password})
首先我们做一下一级缓存的测试
/*** 测试一级缓存
* 从这个测试缓存可以看出两次查询相同的mapper对象,
* 只查询了一次数据库,并且两个对象是相同的,这个是以及缓存*/@Testpublic voidtestCache(){
SqlSession sqlSession=MyBatisUtils.openSession();
UserMapper2 userMapper= sqlSession.getMapper(UserMapper2.class);
User user1= userMapper.getUserById(2);
System.out.println(user1);//User{id=2, username='yangbind', password='1234'}
User user2 = userMapper.getUserById(2);
System.out.println(user2);//User{id=2, username='yangbind', password='1234'}
System.out.println(user1 == user2); //true
/*Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@66ea810]
==> Preparing: select * from `user` where id=?
==> Parameters: 2(Integer)
<== Columns: id, username, password
<== Row: 2, yangbind, 1234
<== Total: 1
User{id=2, username='yangbind', password='1234'}
Cache Hit Ratio [com.yang.mapper.UserMapper2]: 0.0
User{id=2, username='yangbind', password='1234'}
true*/}
接下来看一下中间插入数据,一级缓存会失效,执行了两次查询数据库操作
/*** 通过打印结果可以看出,如果中间执行了一次数据库改变操作,那么就是是一级缓存失效
* 并且同时我们也可以发现,二级缓存也没有作用,这是因为,只有关闭sqlSEssion之后,一级缓存才会将内容写入二级缓存*/@Testpublic voidtestCache2(){
SqlSession sqlSession=MyBatisUtils.openSession();
UserMapper2 userMapper= sqlSession.getMapper(UserMapper2.class);
User user1= userMapper.getUserById(2);
System.out.println(user1);//User{id=2, username='yangbind', password='1234'}
User user = newUser();
user.setUsername("yang");
user.setPassword("13456");
userMapper.insertUser(user);
User user2= userMapper.getUserById(2);
System.out.println(user2);//User{id=2, username='yangbind', password='1234'}
System.out.println(user1 == user2); //false
/*==> Preparing: select * from `user` where id=?
==> Parameters: 2(Integer)
<== Columns: id, username, password
<== Row: 2, yangbind, 1234
<== Total: 1
User{id=2, username='yangbind', password='1234'}
==> Preparing: insert into `user`(username, password) values (?, ?)
==> Parameters: yang(String), 13456(String)
<== Updates: 1
Cache Hit Ratio [com.yang.mapper.UserMapper2]: 0.0
==> Preparing: select * from `user` where id=?
==> Parameters: 2(Integer)
<== Columns: id, username, password
<== Row: 2, yangbind, 1234
<== Total: 1
User{id=2, username='yangbind', password='1234'}
false*/}
通过上述例子,大体可以分析出缓存的查询顺序,先查询一级缓存,在查询二级缓存。
并且一开始二级缓存是没有东西的,只有关闭sqlSession之后,才会将一级缓存对象存入二级缓存,接下来看一下二级缓存例子
/*** 这个是关闭了一级缓存,这个我们没有关闭sqlSession,使用同一个sqlSession,
* 执行结果显示是查询了两次数据库*/@Testpublic voidtestSecond(){
SqlSession sqlSession=MyBatisUtils.openSession();
UserMapper2 userMapper= sqlSession.getMapper(UserMapper2.class);
User user1= userMapper.getUserById(2);
System.out.println(user1);//User{id=2, username='yangbind', password='1234'}
User user2 = userMapper.getUserById(2);
System.out.println(user2);//User{id=2, username='yangbind', password='1234'}
System.out.println(user1 == user2); //false
sqlSession.close();/*Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@508dec2b]
==> Preparing: select * from `user` where id=?
==> Parameters: 2(Integer)
<== Columns: id, username, password
<== Row: 2, yangbind, 1234
<== Total: 1
User{id=2, username='yangbind', password='1234'}
Cache Hit Ratio [com.yang.mapper.UserMapper2]: 0.0
==> Preparing: select * from `user` where id=?
==> Parameters: 2(Integer)
<== Columns: id, username, password
<== Row: 2, yangbind, 1234
<== Total: 1
User{id=2, username='yangbind', password='1234'}
false*/}/*** 这个是关闭了一级缓存,这个我们关闭sqlSession,使用同一个sqlSession,
* 执行结果显示是查询了一次数据库,并且两个对象都是一样的*/@Testpublic voidtestSecond2(){
SqlSession sqlSession=MyBatisUtils.openSession();
UserMapper2 userMapper= sqlSession.getMapper(UserMapper2.class);
User user1= userMapper.getUserById(2);
System.out.println(user1);//User{id=2, username='yangbind', password='1234'}
sqlSession.close();
SqlSession sqlSession2=MyBatisUtils.openSession();
UserMapper2 userMapper2= sqlSession2.getMapper(UserMapper2.class);
User user2= userMapper2.getUserById(2);
System.out.println(user2);//User{id=2, username='yangbind', password='1234'}
System.out.println(user1 == user2); //true
sqlSession2.close();/*Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@798162bc]
==> Preparing: select * from `user` where id=?
==> Parameters: 2(Integer)
<== Columns: id, username, password
<== Row: 2, yangbind, 1234
<== Total: 1
User{id=2, username='yangbind', password='1234'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@798162bc]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@798162bc]
Returned connection 2038522556 to pool.
Cache Hit Ratio [com.yang.mapper.UserMapper2]: 0.5
User{id=2, username='yangbind', password='1234'}
true*/}
这个大体上就是缓存,最终看一下分页插件的使用
先看一下引用的jar包
我们之前在看mybats的配置文件是,看到了plugins这个配置,我们就是在这里面进行引用第三方插件,看一下引用
接下来看一下使用方法
@Testpublic voidtestPage() {
SqlSession sqlSession=MyBatisUtils.openSession();
UserMapper2 userMapper= sqlSession.getMapper(UserMapper2.class);//这个就是指定使用分页器,在查询之前声明,否则不起作用,第一个参数是第几页,第二个参数是一页几条
Page page = PageHelper.startPage(1,2);
List users =userMapper.getUsers();for(User user : users) {
System.out.println(user);/*User{id=2, username='yangbind', password='1234'}
User{id=3, username='xiong', password='9012'}*/}
System.out.println(page.getPageNum());//1 获取当前页吗
System.out.println(page.getPageSize()); //2 获取当前页的条数
System.out.println(page.getPages()); //6 获取总页数
System.out.println(page.getTotal()); //11 获取总条数
}
@Testpublic voidtestPage2() {
SqlSession sqlSession=MyBatisUtils.openSession();
UserMapper2 userMapper= sqlSession.getMapper(UserMapper2.class);//这个就是指定使用分页器,在查询之前声明,否则不起作用,第一个参数是第几页,第二个参数是一页几条
Page page = PageHelper.startPage(1,2);
List users =userMapper.getUsers();//将查询的结果做进一步封装,可以获取是否有下一页以及是否有上一页,并且可以返回页码,第二个参数是指定页码
PageInfo pageInfo = new PageInfo<>(users, 2);for(User user : users) {
System.out.println(user);/*User{id=2, username='yangbind', password='1234'}
User{id=3, username='xiong', password='9012'}*/}for(User user : pageInfo.getList()) {
System.out.println(user);/*User{id=2, username='yangbind', password='1234'}
User{id=3, username='xiong', password='9012'}*/}
System.out.println(pageInfo.getPageNum());//1 获取当前页吗
System.out.println(pageInfo.getPageSize()); //2 获取当前页的条数
System.out.println(pageInfo.getPages()); //6 获取总页数
System.out.println(pageInfo.getTotal()); //11 获取总条数
System.out.println(pageInfo.isHasPreviousPage()); //false 获取是否有上一页
System.out.println(pageInfo.isHasNextPage()); //true 获取是否有下一页
System.out.println(Arrays.toString(pageInfo.getNavigatepageNums())); //[1, 2] 获取展示的页码
}