目录
1.单个参数:------------>八大基础类型与String类型
1.传递String的参数,必须使用@Param方式,并且指明参数名
2.在service接口,方法应该要将@param()去除掉,只留下变量名,在mapper层可以留下@param(),以防万一,防止报错
注: (1)mybatis中使用OGNL表达式传递参数 (2) 优先使用#{...} (3) ${...}方式存在SQL注入风险
案例③:使用resultType返回Map,适用于多表查询返回结果集,object>
为什么要重写mybatis的分页? Mybatis的分页功能很弱,它是基于内存的分页(查出所有记录再按偏移量offset和边界limit取结果),在大数据量的情况下这样的分页基本上是没有用的
5.在BookMapper.xml文件中进行sql语句的编写
8.在你需要进行分页的Mybatis方法前调用PageHelper.startPage静态方法即可,紧跟在这个方法后的第一个Mybatis查询方法会被进行分页
allowMultiQueries=true 允许一条SQL语句包含多个执行SQL以分号;分隔,是必须添加的
一:动态sql的基本概念
动态sql是指在进行sql操作的时候,传入的参数对象或者参数值,根据匹配的条件,有可能需要动态的去判断是否为空,循环,拼接等情况;
二:基本的动态sql标签
if标签
①:在BookMapper类定义包含if标签方法
//if标签
List<Book> queryBookByIf(Book book);②:在BookMapper.xml文件中进行sql语句的编写
<select id="queryBookByIf" resultType="com.zking.model.Book">
select
<include refid="Base_Column_List"></include>
from t_book where 1=1
<if test="null!=btype and ''!=btype">
and btype= #{btype}
</if>
</select> 当满足test条件时,才会将if标签内的sql语句拼接上去
③:在iBookService类编写代码
//mybatis之if标签
List<Book> queryBookByIf(Book book);④:进行 junit4单元测试
------测试带type类型的查询:
@Test
//mybatis之if标签
public void queryBookByIf(){
List<Book> bookList = iBookService.queryBookByIf(Book.builder().btype("历史").build());
bookList.forEach(System.out::println);
}测试结果如下: 
查看sql语句:

---测试不type类型的查询:
@Test
//mybatis之if标签
public void queryBookByIf(){
List<Book> bookList = iBookService.queryBookByIf(Book.builder().build());
bookList.forEach(System.out::println);
}测试结果如下:

查看sql语句:

trim标签
trim标签属性:
| prefix | 前缀 |
| suffix | 后缀 |
| suffixOverride | 去除后缀指定的字符 |
| prefixOverrides | 去除前缀指定的字符 |
①:在BookMapper类定义包含trim标签方法
int insertSelective(Book record);②:在BookMapper.xml文件中进行sql语句的编写
<insert id="insertSelective" parameterType="com.zking.model.Book" >
insert into t_book
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="bid != null" >
bid,
</if>
<if test="bname != null" >
bname,
</if>
<if test="bprice != null" >
bprice,
</if>
<if test="btype != null" >
btype,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="bid != null" >
#{bid,jdbcType=INTEGER},
</if>
<if test="bname != null" >
#{bname,jdbcType=VARCHAR},
</if>
<if test="bprice != null" >
#{bprice,jdbcType=INTEGER},
</if>
<if test="btype != null" >
#{btype,jdbcType=VARCHAR},
</if>
</trim>
</insert>
- foreach标签
foreach标签属性:
| collection | 被遍历的集合或数组 |
| item | 每次循环遍历的集合名 |
| separator | 每次循环的分隔符 |
| index | 下标 |
| open | 开始位置 |
| close | 关闭位置 |
①:编写一个类,储存需要遍历的集合或者数组
package com.zking.vo;
import com.zking.model.Book;
import lombok.Data;
import java.util.List;
/**
* @author 唐渊
* @create 2022-07-23 9:26
*/
@Data //getter/setter/toString
public class BookVo extends Book {
//foreach标签需要遍历的list集合
private List<Integer> ids;
}
②:在BookMapper类定义包含foreach标签方法
//foreach标签
List<Book> queryBookByForeach(BookVo bookVo);③:在BookMapper.xml文件中进行sql语句的编写
(1)方式一:
<select id="queryBookByForeach" resultType="com.zking.model.Book">
select
<include refid="Base_Column_List"></include>
from t_book where 1=1 and bid in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>(2)方式二:
select
<include refid="Base_Column_List"></include>
from t_book where 1=1
<foreach collection="ids" item="id" separator="," open=" and bid in (" close=")">
#{id}
</foreach>(3)方式三:
select
<include refid="Base_Column_List"></include>
from t_book where 1=1 and bid in
(
<foreach collection="ids" item="id" separator=",">
#{id}
</foreach>④:在iBookService类编写代码
//foreach标签
List<Book> queryBookByForeach(BookVo bookVo);⑤:进行 junit4单元测试
@Test
//foreach标签
public void queryBookByForeach(){
BookVo bookVo=new BookVo();
//Arrays.asList--->将数组转换为集合
bookVo.setIds(Arrays.asList(new Integer[]{13,17,3}));
List<Book> books = iBookService.queryBookByForeach(bookVo);
books.forEach(System.out::println);
}测试结果如下:

查看sql语句:

choose标签
choose 标签作用是通过条件判断来拼接 SQL 语句,类似于 Java 中的 switch 语句,从上到下,当有匹配的条件时,跳出 choose 语句;如果所有条件都不成立则执行 otherwise 标签中的内容,使用 choose 标签的时候,一定要注意最先需要判断的条件要放在前面,当匹配到某个条件时,就不会判断后面的语句了
语法格式:
<choose>
<when test=条件1>
...
</when>
<when test=条件2>
...
</when>
<when test=条件3>
...
</when>
...
<otherwise>
...
</otherwise>
</choose>①:在BookMapper类定义包含choose标签方法
//choose 标签
List<Book> queryBookByChoose(Book book);②:在BookMapper.xml文件中进行sql语句的编写
<select id="queryBookByChoose" resultType="com.zking.model.Book">
select <include refid="Base_Column_List"/> from t_book where 1=1
<choose>
<when test="null!=bname and ''!=bname">
and bname like concat('%',#{bname},'%')
</when>
<when test="null!=btype and ''!=btype">
and btype like concat('%',#{btype},'%')
</when>
<otherwise>
and bid=#{bid}
</otherwise>
</choose>
</select>③:在iBookService类编写代码
List<Book> queryBookByChoose(Book book);
④:实现接口所定义的方法
@Override
public List<Book> queryBookByChoose(Book book) {
return bookMapper.queryBookByChoose(book);
}⑤:进行 junit4单元测试
------测试带name类型的查询:
@Test
//choose 标签
public void queryBookByChoose(){
List<Book> books = iBookService.queryBookByChoose(Book.builder().bname("之").build());
books.forEach(System.out::println);
}
测试结果如下:

查看sql语句:

------测试带type类型的查询:
@Test
//choose 标签
public void queryBookByChoose(){
List<Book> books = iBookService.queryBookByChoose(Book.builder().btype("历史").build());
books.forEach(System.out::println);
}测试结果如下:

查看sql语句:

------测试带id类型的查询:
@Test
//choose 标签
public void queryBookByChoose(){
List<Book> books = iBookService.queryBookByChoose(Book.builder().bid(30).build());
books.forEach(System.out::println);
}测试结果如下:

查看sql语句:

set标签
set用于更新的sql语句中,在update时,多条件更新,每个属性后面要加逗号“,”,这个时候可能会出现多一个“,”的情况,此时我们就可以使用set去掉后边的“,”修改方法,假如实体类属性为空就不修改此属性所对于的字段
①:在BookMapper类定义包含set标签方法
//set标签
int updateByPrimaryKeySelective(Book record);②:在BookMapper.xml文件中进行sql语句的编写
<update id="updateByPrimaryKeySelective" parameterType="com.zking.model.Book" >
update t_book
<set >
<if test="bname != null" >
bname = #{bname,jdbcType=VARCHAR},
</if>
<if test="bprice != null" >
bprice = #{bprice,jdbcType=INTEGER},
</if>
<if test="btype != null" >
btype = #{btype,jdbcType=VARCHAR},
</if>
</set>
where bid = #{bid,jdbcType=INTEGER}
</update>③:在iBookService类编写代码
int updateByPrimaryKeySelective(Book record);④:实现接口所定义的方法
@Override
public int updateByPrimaryKeySelective(Book record) {
return bookMapper.updateByPrimaryKeySelective(record
);
}⑤:进行 junit4单元测试
public void updateByPrimaryKeySelective(){
book.setBid(27);
book.setBname("剑与荣光");
book.setBprice(45);
book.setBtype("文学");
iBookService.updateByPrimaryKeySelective(book);
}测试结果如下:

查看sql语句:

where标签
where标签只会在至少有一个子元素返回了SQL语句时, 才会向SQL语句中添加WHERE,并且如果WHERE之后是以AND或OR开头,会自动将其删掉。
①:在BookMapper类定义包含where标签方法
//where标签
List<Book> queryBookWhere(Book book);②:在BookMapper.xml文件中进行sql语句的编写
<select id="queryBookWhere" resultType="com.zking.model.Book">
select <include refid="Base_Column_List"/> from t_book
<where>
<if test="bname!=null and bname!=''">
and bname like concat('%',#{bname},'%')
</if>
<if test="btype!=null and btype!=''">
and btype like concat('%',#{btype},'%')
</if>
</where>
</select>③:在iBookService类编写代码
//where标签
List<Book> queryBookWhere(Book book);④:实现接口所定义的方法
@Override
public List<Book> queryBookWhere(Book book) {
return bookMapper.queryBookWhere(book);
}⑤:进行 junit4单元测试
------测试带name类型的查询:
@Test
//where标签
public void queryBookWhere(){
List<Book> books = iBookService.queryBookWhere(Book.builder().bname("的").build());
books.forEach(System.out::println);
}测试结果如下:

查看sql语句:

------测试带type类型的查询:
@Test
//where标签
public void queryBookWhere(){
List<Book> books = iBookService.queryBookWhere(Book.builder().btype("文学").build());
books.forEach(System.out::println);
}测试结果如下:

查看sql语句:

二:参数传递
1.单个参数:------------>八大基础类型与String类型
①:案例演示:Integer参数传递
(1):在BookMapper类定义方法
List<Book> queryBookByInteger(Integer bid);(2):在BookMapper.xml文件中进行sql语句的编写
<select id="queryBookByInteger" resultType="com.zking.model.Book">
select <include refid="Base_Column_List"></include> from t_book where 1=1
<if test="null!=bid and ''!=bid ">
and bid=#{bid}
</if>
</select>(3):在iBookService类编写代码
List<Book> queryBookByInteger(Integer bid);(4):实现接口所定义的方法
@Override
public List<Book> queryBookByInteger(Integer bid) {
return bookMapper.queryBookByInteger(bid);
}(5):进行 junit4单元测试
---------不传递参数的测试演示(相当于查询所有)
@Test
public void queryBookByInteger(){
List<Book> books = iBookService.queryBookByInteger(null);
books.forEach(System.out::println);
}测试结果如下:

查看sql语句:

ok,不带id参数的测试,通过sql语句可以看出来,相当于查询所有,那么我们再来进行带id参数的测试:
@Test
public void queryBookByInteger(){
List<Book> books = iBookService.queryBookByInteger(3);
books.forEach(System.out::println);
}测试结果如下:

很好,出现了bug,没有bug的程序猿,都是没有完整的程序猿人生的,我们一起来分析为什么会出现bug:
报错提示:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'bid' in 'class java.lang.Integer'
### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'bid' in 'class java.lang.Integer'
分析:当前的bid没有getter方法,反射异常-----传递的是一个属性,不是一个实体类,如何给它getter方法?
<if test="null!=bid and ''!=bid ">
and bid=#{bid}
</if>所以,错误点在BookMapper.xml文件中的bid,如果传递的是八大基本类型,就必须使用value,value就代表当前的值
所以,将BookMapper.xml文件中的变为:
<if test="null!=value and ''!=value ">
and bid=#{bid}
</if>继续测试,看效果,查看是否可以运行:

查看sql语句:

OK,nice,将xml文件在的bid全部转换为value(注意:数据库的字段除外),来继续尝试:
<if test="null!=value and ''!=value ">
and bid=#{value}
</if>测试结果如下:

查看sql语句:

ok,nice~~~~nice,继续往下走~~~~··
ps:
传递Integer类型的参数,相当于传递八大基础类型
②:案例演示:String参数传递
(1):在BookMapper类定义方法
//传递String类型的参数
List<Book> queryBookByString(String btype);(2):在BookMapper.xml文件中进行sql语句的编写
<select id="queryBookByString" resultType="com.zking.model.Book">
select <include refid="Base_Column_List"/> from t_book where 1=1
<if test="null!=value and ''!=value ">
and btype=#{btype}
</if>
</select>(3):在iBookService类编写代码
//传递String类型的参数
List<Book> queryBookByString(String btype);(4):实现接口所定义的方法
@Override
public List<Book> queryBookByString(String btype) {
return bookMapper.queryBookByString(btype);
}(5):进行 junit4单元测试
@Test
//传递String类型的参数
public void queryBookByString(){
List<Book> books = iBookService.queryBookByString("历史");
books.forEach(System.out::println);
}测试结果如下:

报错提示:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.lang.IllegalArgumentException: invalid comparison: java.lang.String and [C
### Cause: java.lang.IllegalArgumentException: invalid comparison: java.lang.String and [C
很好,又出现了bug,继续分析:
这个异常是属于参数异常的类型,不知道是什么哪里有问题,那么我们不妨大胆猜测一下,有可能是xml文件中不能使用value,改用btype尝试尝试
<if test="null!=btype and ''!=btype ">
and btype=#{btype}
</if>好,继续运行,看看结果:

报错提示:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'btype' in 'class java.lang.String'
### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'btype' in 'class java.lang.String'
分析:很好,出现了bug,不过这个bug是和上面出现的bug一样,说明还是要使用value,那么我们就将所有的btype统统改为value,继续运行运行尝试尝试~~~
<if test="null!=value and ''!=value ">
and btype=#{value}
</if>测试结果如下:

OK,还是在报我们第一次运行时的错误,一直在报错,这就告诉我们String类型的传参不能这么直接传参,还需要额外的参数,那么我们将BookMapper类的方法用注解的方法进行额外的设参,用@param()的方式进行设参,例如我的String类型的传递参数问题解决方案如下:
//bookType-->参数
//btype-->变量名
List<Book> queryBookByString(@Param("bookType") String btype);mapper的映射文件如老样子,不进行修改,进行运行,看是否会报错
<if test="null!=value and ''!=value ">
and btype=#{value}
</if>测试结果如下:

报错提示:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'value' not found. Available parameters are [param1, bookType]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'value' not found. Available parameters are [param1, bookType]
好,天意难违,bug还是出现了,我们继续来分析:这里的报错显示value没有被分析,那么我们不妨可以得出是xml映射文件的sql语句中,不能使用value,要使用我们在mapper层中定义方法所用@param()方式设置的参数,来继续进行测试一波
<if test="null!=bookType and ''!=bookType">
and btype=#{bookType}
</if>测试结果如下:

好,传递String类型的参数的bug终于解决掉了,当我们遇到bug时,不要放弃,要大胆尝试,从另一个方向分析出发,可能会有意外的收获
注意:
1.传递String的参数,必须使用@Param方式,并且指明参数名
2.在service接口,方法应该要将@param()去除掉,只留下变量名,在mapper层可以留下@param(),以防万一,防止报错
2.多个参数
①:在BookMapper类定义方法
List<Book> queryBookParams(@Param("booktype") String btype,
@Param("bookid") Integer bid);②:在BookMapper.xml文件中进行sql语句的编写
<select id="queryBookParams" resultType="com.zking.model.Book">
select <include refid="Base_Column_List"/> from t_book where 1=1
<if test="null!=booktype and ''!=booktype">
and btype=#{booktype}
</if>
<if test="null!=bookid and ''!=bookid">
and bid=#{bookid}
</if>
</select>③:在iBookService类编写代码
List<Book> queryBookParams(String btype, Integer bid);注意:方法在mapper层和service接口所定义的方法格式不一样,请认真区分并遵循
④:实现接口所定义的方法
@Override
public List<Book> queryBookParams(String btype, Integer bid) {
return bookMapper.queryBookParams(btype,bid);
}⑤:进行 junit4单元测试
public void queryBookParams(){
List<Book> books = iBookService.queryBookParams("文学", 27);
books.forEach(System.out::println);
}测试结果如下:

查看sql语句:

3.实体参数
①:在BookMapper类定义方法
List<Book> queryBookByObject(Book book);②:在BookMapper.xml文件中进行sql语句的编写
@Override
public List<Book> queryBookByObject(Book book) {
return bookMapper.queryBookByObject(book);
}③:在iBookService类编写代码
List<Book> queryBookByObject(Book book);④:实现接口所定义的方法
@Override
public List<Book> queryBookByObject(Book book) {
return bookMapper.queryBookByObject(book);
}⑤:进行 junit4单元测试
@Test
public void queryBookByObject(){
List<Book> books = iBookService.queryBookByObject(Book.builder().btype("文学").build());
books.forEach(System.out::println);
}
测试结果如下:

查看sql语句:

三:模糊查询
①:在BookMapper类定义方法
List<Book> queryBookLike(Book book);②:在BookMapper.xml文件中进行sql语句的编写
<select id="queryBookLike" resultType="com.zking.model.Book">
select <include refid="Base_Column_List"/> from t_book where 1=1
<if test="null!=bname and ''!=bname">
and bname like #{bname}
</if>
</select>③:在iBookService类编写代码
//模糊查询
List<Book> queryBookLike(Book book);④:实现接口所定义的方法
@Override
public List<Book> queryBookLike(Book book) {
return bookMapper.queryBookLike(book);
}1.${}
重点:相当于占位符,参数传递时需要手动拼接%%百分号 例如:"zs"-->'zs' 会自动拼接单引号
⑤:进行 junit4单元测试
方式一:使用${}方式进行模糊查询
@Test
//模糊查询
public void queryBookLike(){
List<Book> books = iBookService.queryBookLike(Book.builder().bname("%与%").build());
books.forEach(System.out::println);
}
测试结果如下:

查看sql语句:

2.#{}
重点:使用$传递参数时,只会传递参数本身--->例如:"zs"-->zs 不会自动拼接单引号
⑤:进行 junit4单元测试
方式二:使用#{}方式进行模糊查询
@Test
//模糊查询
public void queryBookLike(){
List<Book> books = iBookService.queryBookLike(Book.builder().bname("与").build());
books.forEach(System.out::println);
}测试结果如下:
<if test="null!=bname and ''!=bname">
and bname like '%${bname}%'
</if>
查看sql语句:

3.concat()---->mysql函数
⑤:进行 junit4单元测试
方式三:使用concat()方式进行模糊查询
<if test="null!=bname and ''!=bname">
and bname like concat('%',#{bname},'%')
</if>测试结果如下:

查看sql语句:

问题:#{...}与${...}区别?
答:参数类型为字符串,#会在前后加单引号['],$则直接插入值
注:
(1)mybatis中使用OGNL表达式传递参数
(2) 优先使用#{...}
(3) ${...}方式存在SQL注入风险
问:SQL注入风险
数据库要执行 SQL 访问数据,数据库是个执行机构,它只会检查传来的 SQL 是不是合乎语法,而并不会关心这个语句是否会造成伤害(数据泄露或破坏)。正因为只要符合语法规则就会执行的机制,导致 SQL 有了注入的风险。
SQL 本身就是个字符串,而且一般没有加密,字符串可能被黑客劫持修改,这样就可能造成数据库执行了不该执行的动作。
SQL 注入的惯用做法是通过把 SQL 子串插入到 Web 表单项或页面请求(Url)的查询字符串中提交,最终达到欺骗服务器执行恶意操作的目的。
常见案例包括通过植入 SQL 骗过登录验证。而之前很多影视网站泄露 VIP 会员密码的事件,很多就是通过 SQL 植入到 WEB 表单暴露的,并且这类表单特别容易受到攻击。通过 SQL 植入,不仅可以非法获取账号信息,严重的还能够篡改、删除重要数据信息。
四:查询返回结果集
返回结果集的类型:
- resultMap:适合使用返回值是自定义实体类的情况
- resultType:适合使用返回值的数据类型是非自定义的,即jdk的提供的类型
案例①:使用resultMap返回自定义类型集合
(1):在BookMapper类定义方法
List<Book> queryBookByResultMap();(2):在BookMapper.xml文件中进行sql语句的编写
<select id="queryBookByResultMap" resultMap="BaseResultMap">
/*有参构造方法*/
select <include refid="Base_Column_List"/> from t_book
</select>(3):在iBookService类编写代码
List<Book> queryBookByResultMap();(4):实现接口所定义的方法
@Override
public List<Book> queryBookLike(Book book) {
return bookMapper.queryBookLike(book);
}(5):进行 junit4单元测试
@Test
//查询返回结果集
//resultMap:适合使用返回值是自定义实体类的情况
//resultType:适合使用返回值的数据类型是非自定义的,即jdk的提供的类型
public void queryBookByResultMap(){
List<Book> books = iBookService.queryBookByResultMap();
books.forEach(System.out::println);
}测试结果如下:

查看sql语句:

案例②: 使用resultType返回单个对象
(1):在BookMapper类定义方法
Book querySingleBookById(Integer bid);(2):在BookMapper.xml文件中进行sql语句的编写
<select id="querySingleBookById" resultType="com.zking.model.Book">
select <include refid="Base_Column_List"/> from t_book where bid=#{bid}
</select>(3):在iBookService类编写代码
Book querySingleBookById(Integer bid);(4):实现接口所定义的方法
@Override
public Book querySingleBookById(Integer bid) {
return bookMapper.querySingleBookById(bid);
}(5):进行 junit4单元测试
@Test
//使用resultType返回单个对象
public void querySingleBookById(){
Book book = iBookService.querySingleBookById(13);
System.out.println(book);
}
测试结果如下:

查看sql语句:

案例③:使用resultType返回Map<String,Object>,适用于多表查询返回结果集
(1):在BookMapper类定义方法
Map<String,Object> querySingleBookByMap(Integer bid);(2):在BookMapper.xml文件中进行sql语句的编写
<select id="querySingleBookByMap" resultType="java.util.Map">
select <include refid="Base_Column_List"/> from t_book where bid=#{bid}
</select>(3):在iBookService类编写代码
Map<String,Object> querySingleBookByMap(Integer bid);(4):实现接口所定义的方法
@Override
public Map<String, Object> querySingleBookByMap(Integer bid) {
return bookMapper.querySingleBookByMap(bid);
}(5):进行 junit4单元测试
@Test
public void querySingleBookByMap(){
Map<String, Object> book1 = iBookService.querySingleBookByMap(13);
System.out.println(book1);
}测试结果如下:

查看sql语句:

案例④: 使用resultType返回List<Map>
(1):在BookMapper类定义方法
List<Map<String,Object>> queryBookByMaps();(2):在BookMapper.xml文件中进行sql语句的编写
<select id="queryBookByMaps" resultType="java.util.Map">
select <include refid="Base_Column_List"/> from t_book
</select>(3):在iBookService类编写代码
List<Map<String,Object>> queryBookByMaps();(4):实现接口所定义的方法
@Override
public List<Map<String, Object>> queryBookByMaps() {
return bookMapper.queryBookByMaps();
}(5):进行 junit4单元测试
@Test
//List<Map>和Map集合,适用于多表联查返回综合结果集
public void queryBookByMaps(){
List<Map<String, Object>> maps = iBookService.queryBookByMaps();
System.out.println(maps);
}测试结果如下:

查看sql语句:

五:分页查询
为什么要重写mybatis的分页?
Mybatis的分页功能很弱,它是基于内存的分页(查出所有记录再按偏移量offset和边界limit取结果),在大数据量的情况下这样的分页基本上是没有用的
实现分页查询的步骤:
1.导入分页插件
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>例如:我的pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zking</groupId>
<artifactId>mybatis01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>mybatis01 Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<!--junit-->
<junit.version>4.13</junit.version>
<!--servlet-->
<servlet.version>4.0.1</servlet.version>
<!--mybatis-->
<mybatis.version>3.4.5</mybatis.version>
<!--mysql-->
<mysql.version>5.1.44</mysql.version>
<!--log4j2-->
<log4j2.version>2.9.1</log4j2.version>
<!--lombok-->
<lombok.version>1.18.24</lombok.version>
</properties>
<dependencies>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--记得修改mybatis.cfg.xml添加如下内容-->
<!--<setting name="logImpl" value="LOG4J2"/>-->
<!--核心log4j2jar包-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
<!--web工程需要包含log4j-web,非web工程不需要-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>${log4j2.version}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!--pagehelper-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>
</dependencies>
<build>
<finalName>mybatis01</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<!--自动生成代码-->
<!--高版本不兼容-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
<configuration>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build>
</project>
2. 将pagehelper插件配置到mybatis中
<!-- 配置分页插件PageHelper, 4.0.0以后的版本支持自动识别使用的数据库 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
</plugin>例如:我的mybatis.cfg..xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 引入外部配置文件 -->
<properties resource="jdbc.properties"/>
<!--设置mybatis参数-->
<settings>
<!--日志输出-->
<setting name="logImpl" value="LOG4J2"/>
</settings>
<!-- 别名 -->
<!--<typeAliases>
<typeAlias type="com.zking.model.Book" alias="book"/>
</typeAliases>-->
<plugins>
<!--pagehelper分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
<!-- 配置mybatis运行环境 -->
<environments default="development">
<environment id="development">
<!-- type="JDBC" 代表使用JDBC的提交和回滚来管理事务 -->
<transactionManager type="jdbc"/>
<!-- mybatis提供了3种数据源类型,分别是:POOLED,UNPOOLED,JNDI -->
<!-- POOLED 表示支持JDBC数据源连接池 -->
<!--
DBHelper-> 每一次增删改查都需要创建连接,使用完了之后要关闭连接
连接池->第一次先固定初始化多少连接并存放到连接池中,当需要的时候再去连接池中拿,用完了之后再还回去
-->
<!-- UNPOOLED 表示不支持数据源连接池 -->
<!-- JNDI 表示支持外部数据源连接池 -->
<dataSource type="POOLED">
<property name="driver"
value="${jdbc.driver}"/>
<property name="url"
value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/zking/mapper/BookMapper.xml"/>
</mappers>
</configuration>3.导入pageBean文件在util包下
package com.zking.util;
import java.io.Serializable;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
public class PageBean implements Serializable {
private static final long serialVersionUID = 2422581023658455731L;
//页码
private int page=1;
//每页显示记录数
private int rows=10;
//总记录数
private int total=0;
//是否分页
private boolean isPagination=true;
//上一次的请求路径
private String url;
//获取所有的请求参数
private Map<String,String[]> map;
public PageBean() {
super();
}
//设置请求参数
public void setRequest(HttpServletRequest req) {
String page=req.getParameter("page");
String rows=req.getParameter("rows");
String pagination=req.getParameter("pagination");
this.setPage(page);
this.setRows(rows);
this.setPagination(pagination);
this.url=req.getContextPath()+req.getServletPath();
this.map=req.getParameterMap();
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Map<String, String[]> getMap() {
return map;
}
public void setMap(Map<String, String[]> map) {
this.map = map;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public void setPage(String page) {
if(null!=page&&!"".equals(page.trim()))
this.page = Integer.parseInt(page);
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
this.rows = rows;
}
public void setRows(String rows) {
if(null!=rows&&!"".equals(rows.trim()))
this.rows = Integer.parseInt(rows);
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public void setTotal(String total) {
this.total = Integer.parseInt(total);
}
public boolean isPagination() {
return isPagination;
}
public void setPagination(boolean isPagination) {
this.isPagination = isPagination;
}
public void setPagination(String isPagination) {
if(null!=isPagination&&!"".equals(isPagination.trim()))
this.isPagination = Boolean.parseBoolean(isPagination);
}
/**
* 获取分页起始标记位置
* @return
*/
public int getStartIndex() {
//(当前页码-1)*显示记录数
return (this.getPage()-1)*this.rows;
}
/**
* 末页
* @return
*/
public int getMaxPage() {
int totalpage=this.total/this.rows;
if(this.total%this.rows!=0)
totalpage++;
return totalpage;
}
/**
* 下一页
* @return
*/
public int getNextPage() {
int nextPage=this.page+1;
if(this.page>=this.getMaxPage())
nextPage=this.getMaxPage();
return nextPage;
}
/**
* 上一页
* @return
*/
public int getPreivousPage() {
int previousPage=this.page-1;
if(previousPage<1)
previousPage=1;
return previousPage;
}
@Override
public String toString() {
return "PageBean [page=" + page + ", rows=" + rows + ", total=" + total + ", isPagination=" + isPagination
+ "]";
}
}
4.在BookMapper类定义方法
//分页查询
List<Book> queryBookPager(Book book, PageBean pageBean);
5.在BookMapper.xml文件中进行sql语句的编写
@Override
public List<Book> queryBookPager(Book book, PageBean pageBean) {
return bookMapper.queryBookPager(book);
}6.在iBookService类编写代码
//分页查询
List<Book> queryBookPager(Book book, PageBean pageBean);7.实现接口所定义的方法
@Override
public List<Book> queryBookPager(Book book, PageBean pageBean) {
return bookMapper.queryBookPager(book);
}8.在你需要进行分页的Mybatis方法前调用PageHelper.startPage静态方法即可,紧跟在这个方法后的第一个Mybatis查询方法会被进行分页
//设置分页处理
if (null != pageBean && pageBean.isPaginate()) {
PageHelper.startPage(pageBean.getCurPage(), pageBean.getPageRecord());
}
9. 获取分页信息(二种方式)
①:使用插件后,查询实际返回的是Page<E>,而非List<E>,Page继承了ArrayList,同时还包含分页相关的信息
Page<Book> page = (Page<Book>)list;
System.out.println("页码:" + page.getPageNum());
System.out.println("页大小:" + page.getPageSize());
System.out.println("总记录:" + page.getTotal());②:使用PageInfo
PageInfo pageInfo = new PageInfo(list);
System.out.println("页码:" + pageInfo.getPageNum());
System.out.println("页大小:" + pageInfo.getPageSize());
System.out.println("总记录:" + pageInfo.getTotal());我的分页获取信息,使用第二种方式:
@Test
//分页查询
public void queryBookPager(){
PageBean pageBean=new PageBean();
//判断是否分页
if(null!=pageBean&&pageBean.isPagination())
//pageBean.getPage()---当前页数
//pageBean.getRows()----每页条数
PageHelper.startPage(pageBean.getPage(),pageBean.getRows());
List<Book> bookList = iBookService.queryBookPager(Book.builder().build(), pageBean);
System.out.println(bookList.getClass());
if(null!=pageBean&&pageBean.isPagination()) {
PageInfo pageInfo = new PageInfo(bookList);
System.out.println("页码:" + pageInfo.getPageNum());
System.out.println("页大小:" + pageInfo.getPageSize());
System.out.println("总记录:" + pageInfo.getTotal());
List list = pageInfo.getList();
list.forEach(System.out::println);
}
}测试结果如下:

查看sql语句:

六:特殊字符处理
| > | > |
| < | < |
| & | & |
| 空格 |  ; |
问题:为什么需要转义?
答:因为在mybatis中的sql语句是写在xml文件中的,但是在处理范围查询的时候经常要用到<,>,$等特殊字符
案例:价格范围查询
①:定义一个特殊类,用来存放范围值
package com.zking.vo;
import com.zking.model.Book;
import lombok.Data;
import java.util.List;
/**
* @author 唐渊
* @create 2022-07-23 9:26
*/
@Data //getter/setter/toString
public class BookVo extends Book {
//最小价格
private Integer min;
//最大价格
private Integer max;
}
②:在BookMapper类定义方法
//范围查询,转义字符处理
List<Book> queryBookRange(BookVo bookVo);③:在BookMapper.xml文件中进行sql语句的编写
<select id="queryBookRange" resultType="com.zking.model.Book">
select <include refid="Base_Column_List"/> from t_book where 1=1
<if test="null!=min and ''!=min">
and bprice>#{min}
</if>
<if test="null!=max and ''!=max">
and bprice<#{max}
</if>
</select>④:在iBookService类编写代码
//范围查询,转义字符处理
List<Book> queryBookRange(BookVo bookVo);⑤:实现接口所定义的方法
@Override
public List<Book> queryBookRange(BookVo bookVo) {
return bookMapper.queryBookRange(bookVo);
}⑥:进行 junit4单元测试
@Test
//范围查询,转义字符处理
public void queryBookRange(){
BookVo bookVo=new BookVo();
bookVo.setMin(50);
bookVo.setMax(200);
List<Book> books = iBookService.queryBookRange(bookVo);
//不使用转义字符前
//元素内容必须由格式正确的字符数据或标记组成。
books.forEach(System.out::println);
}测试结果如下:

查看sql语句:

七:批量新增
mybatis原则上是不能够一次批量执行多条SQL语句,比如新增、删除、更新等,但是修改jdbc url连接带参数可以实现相同效果
spring:
datasource:
username: root
password: mysql
# allowMultiQueries 是否允许一条SQL语句包含多个执行SQL以分号;分隔
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC&allowMultiQueries=true
driver-class-name: com.mysql.cj.jdbc.Driver重点:
allowMultiQueries=true 允许一条SQL语句包含多个执行SQL以分号;分隔,是必须添加的
①:在BookMapper类定义方法
//批量新增
int insertBatch(List<Book> list);②:在BookMapper.xml文件中进行sql语句的编写
<insert id="insertBatch">
insert into t_book ( bname,bprice,btype ) values
<foreach collection="list" separator="," item="it">
(#{it.bname},#{it.bprice},#{it.btype})
</foreach>
</insert>③:在iBookService类编写代码
//批量新增
int insertBatch(List<Book> list);④:实现接口所定义的方法
@Override
public int insertBatch(List<Book> list) {
return bookMapper.insertBatch(list);
}⑤:进行 junit4单元测试
@Test
//批量新增
public void insertBatch(){
List<Book> list = new ArrayList<>();
book.setBname("西罗马帝国");
book.setBprice(34);
book.setBtype("历史");
list.add(book);
book.setBname("世界大战一");
book.setBprice(65);
book.setBtype("历史");
list.add(book);
int i = iBookService.insertBatch(list);
if (i>0) {
System.out.println("yes");
}
}测试结果如下:

查看sql语句:

八:批量更新
①:在BookMapper类定义方法
//批量更新
int updateForNew(List<Book> List);
②:在BookMapper.xml文件中进行sql语句的编写
<update id="updateForNew">
insert into t_book (bname,bprice,btype) values
<foreach collection="list" separator="," item="it">
(#{it.bname},#{it.bprice},#{it.btype})
</foreach>
on duplicate key update bname=values(bname);
</update>③:在iBookService类编写代码
@Override
int updateForNew(List<Book> List) ;
④:实现接口所定义的方法
@Override
public int updateForNew(List<Book> list) {
return bookMapper.insertBatch(list);
}⑤:进行 junit4单元测试
@Test
//批量更新
public void updateForNew(){
List<Book> list = new ArrayList<Book>();
book.setBname("上帝之鞭");
book.setBprice(56);
book.setBtype("历史");
list.add(book);
book.setBname("西楚霸王");
book.setBprice(77);
book.setBtype("历史");
list.add(book);
int i = iBookService.updateForNew(list);
if (i>0) {
System.out.println("yes");
}
}测试结果如下:

九:批量删除
①:在BookMapper类定义方法
//批量删除
int deleteBatch(List<Integer> idList);②:在BookMapper.xml文件中进行sql语句的编写
<delete id="deleteBatch">
delete from t_book where bid in
<foreach collection="list" item="it" separator="," open="(" close=")">
#{it}
</foreach>
</delete>
③:在iBookService类编写代码
//批量删除
int deleteBatch(List<Integer> idList);④:实现接口所定义的方法
@Override
public int deleteBatch(List<Integer> idList) {
return bookMapper.deleteBatch(idList);
}
⑤:进行 junit4单元测试
@Test
//批量删除
public void deleteBatch(){
List<Integer> list = new ArrayList<>();
list.add(31);
list.add(32);
int i = iBookService.deleteBatch(list);
if (i>0) {
System.out.println("yes");
}
}测试结果如下:

查看sql语句:

代码操作有点多,但全是干货
