MyBatis的场景应用(动态SQL、模糊查询及映射结果)附(Mybatis中#和$的区别)

一.Mybatis简介

MyBatis(之前被称为iBatis)是一种开源的持久化框架,它将面向关系数据库的持久层操作封装起来,使得开发人员可以通过简单的配置来实现对数据库的操作。MyBatis提供了灵活且强大的SQL映射功能,能够将数据库表的记录映射到Java对象上,从而简化了数据库操作和数据持久化的过程。

以下是MyBatis的一些主要特点和功能:

  1. 简单易用:MyBatis采用了简单的XML或注解配置方式,使得开发人员可以快速上手并且容易理解。

  2. 灵活性:MyBatis不强制开发者遵循特定的编程模型,开发人员可以使用自己习惯的编码风格进行开发。同时,MyBatis支持动态SQL,可以根据不同的条件生成不同的SQL语句,增加了灵活性和可扩展性。

  3. SQL映射:MyBatis提供了强大的SQL映射功能,通过配置SQL语句和结果映射规则,将数据的提取、转换和映射工作交给MyBatis框架完成,减少了手动编写JDBC代码的工作量。

  4. 执行器和事务:MyBatis提供了多种执行器(Simple、Reuse、Batch)和事务管理方式,开发人员可以根据需要选择适合的执行器和事务管理方式,以提高数据库操作的性能和可靠性。

  5. 插件机制:MyBatis支持自定义插件,开发人员可以通过插件机制来扩展或修改MyBatis的行为,例如增加缓存支持、打印SQL日志等。

总的来说,MyBatis是一个轻量级且灵活的持久化框架,它通过将数据库操作和Java对象之间的映射工作进行封装,简化了数据持久化的过程。它的目标是提供开发人员更直观、高效的数据库访问方式,并且与各种Java框架(如Spring、Spring Boot)和持久化标准(如JPA)可以良好集成。

二.场景应用

1.动态SQL

1.1 介绍

MyBatis中的动态SQL是指在SQL语句中根据不同的条件动态生成不同的SQL片段,从而实现灵活的查询和更新操作。动态SQL使得我们能够根据不同的情况生成不同的SQL语句,避免了编写大量重复的SQL语句,提高了代码的可维护性和可读性

1.2 案例演示

 (1).foreach元素的使用

当我们的SQL语句的查询条件的数量存在多个时,可以使用foreach元素进行遍历条件结果,最后进行SQL查询,在我们Mybatis生成的xml中进行配置

<select id="selectById" resultType="com.YU.model.Book" parameterType="java.lang.List" >
    select 
    <include refid="Base_Column_List" />
    from t_mvc_book
    where bid in
    <foreach collection="bids" item="bid" open="(" close=")" separator=",">
       #{bid}
    </foreach>
  </select>

在BookMapper接口中定义方法

List<Book> selectById(@Param("bids") List bids);

 在业务逻辑层中编写接口类及它的实现类

List<Book> selectById(List bids);
 @Override
    public List<Book> selectById(List bids) {
        return BookMapper.selectById(bids);
    }

开始测试

在测试类中编写Junit测试方法

@Test
    public void selectById(){
        //创造一个集合
        List<Integer> bids = Arrays.asList(new Integer[]{58, 89, 61});
        bookBiz.selectById(bids).forEach(System.out::println);
    }

运行结果:

小结:我们在不确定使用SQL语句时可以使用动态SQL ,大大得增加了SQL语句的灵活性,我们可以通过Mybatis中封装好的foreach元素配合动态SQL遍历出不确定的条件元素,总而言之:动态SQL为我们的开发增加了可扩展性,提高了我们的开发效率,降低了开发难度

当然在Mybatis中不止有foreach一个元素可供动态SQL使用,还有if、choose、when、otherwise、bind元素等等,下面介绍它们的用法,就不进行测试了

(2) if元素:

if元素用于在SQL语句中添加条件判断。可以根据某个条件的值来选择是否包含特定的SQL片段。示例:

<select id="getUserList" parameterType="User" resultType="User">
  SELECT * FROM users
  WHERE 1=1
  <if test="name != null">
    AND name = #{name}
  </if>
  <if test="age != null">
    AND age = #{age}
  </if>
</select>

上述示例中,根据传入的User对象的name和age属性是否为空,决定是否在SQL语句中添加对应的查询条件。

(3) choose、when、otherwise元素:

choose元素用于实现类似于Java的switch语句的功能。当某个条件满足时,执行对应的SQL片段。示例:

<select id="getUserList" parameterType="User" resultType="User">
  SELECT * FROM users
  WHERE 1=1
  <choose>
    <when test="name != null">
      AND name = #{name}
    </when>
    <when test="age != null">
      AND age = #{age}
    </when>
    <otherwise>
      AND status = 'ACTIVE'
    </otherwise>
  </choose>
</select>

上述示例中,当name和age都为空时,查询条件为status=‘ACTIVE’;当name不为空时,查询条件为name=#{name};当age不为空时,查询条件为age=#{age}。

(4) bind元素:

"bind"元素是一种用于定义和绑定变量的XML元素。该元素可以在MyBatis的XML映射文件中使用,用于在SQL语句中定义和使用临时变量。示例:

<select id="getUserById" resultType="User">
  <bind name="userId" value="1"/>
  SELECT * FROM users WHERE id = #{userId}
</select>

在上面的示例中,“bind"元素用于定义一个名为"userId"的变量,并将其值设置为"1”。然后,该变量可以在后续的SQL语句中使用,通过"#{userId}"的方式引用变量的值。

"bind"元素有两个属性:

  • “name”:指定变量的名称。
  • “value”:指定变量的值。

2.Mybatis中的模糊查询

 三种写法

(1).#
<select id="like1" resultType="com.YU.model.Book" parameterType="java.util.List" >
    select
    <include refid="Base_Column_List" />
    from t_mvc_book
    where bname like #{bname}
  </select>

测试结果 

(2). $
<select id="like2" resultType="com.YU.model.Book" parameterType="java.util.List" >
    select
    <include refid="Base_Column_List" />
    from t_mvc_book
    where bname like '${bname}'
  </select>

 

(3).concat
<select id="like3" resultType="com.YU.model.Book" parameterType="java.util.List" >
    select
    <include refid="Base_Column_List" />
    from t_mvc_book
    where bname like concat('%',#{bname},'%')
  </select>

总结:由测试结果可以得出Mybatis中 '#' '$区别

1.内在形式:$是占位符传参,#是预处理SQL

2.外在形式:$传参时不带引号,需要自行添加,#传参自带引号

3.$传参存在sql注入的危险,#不存在

4.$可以用作动态列,完成动态SQL的开发

在实际开发过程中,尽量不使用$··,#能很大程度防止SQL注入

三.resultMap和resultType的区别 

1.简介

resultTyperesultMap是Mybatis中映射查询结果的两种方式

  1. resultTyperesultType是一种简单的映射方式,用于指定查询结果的目标类型。你可以通过指定目标类型的全限定名或简单类型名来使用它。例如,如果你有一个User类,你可以使用resultType="com.example.User"来告诉MyBatis将查询结果映射到该类的对象。在使用resultType时,MyBatis通过反射创建目标类型的对象,并将查询结果的列与目标对象的属性进行匹配。

  2. resultMapresultMap提供了更灵活和详细的结果映射方式。通过使用resultMap,你可以定义一个映射规则,将查询结果中的列映射到指定Java对象的属性。你可以在resultMap中指定列名和属性名之间的映射关系,还可以执行一些其他的映射操作,如类型转换、关联对象的加载等。通过使用resultMap,你可以更好地控制结果的映射过程。

2.区别

  • 简单性:resultTyperesultMap更简单,只需要指定目标类型即可,而resultMap需要定义详细的映射规则。
  • 灵活性:resultMapresultType更灵活,可以定义复杂的映射规则,并在映射过程中执行一些额外的操作。
  • 可读性:由于resultType只指定目标类型,因此在查看代码时,可能需要跳转到目标类型的定义处以了解其属性。而resultMap可以在同一个地方定义所有的映射规则,使代码更易读。

3.总结与归纳

(1)

在使用MyBatis中拥有多个场景,返回的结果是多样的,resultType/resultMap

1返回单表的对应的实体类,仅有一个查询结果,可以用resultType/resultMap

2返回单表的对应的实体类,有多个查询结果,可以用resultType/resultMap

3返回多表对应结果,仅有一个查询结果,通常用resultType也可以用resultMap

4返回多表对应结果,有多个查询结果,通常用resultType也可以用resultMap

5返回单个列段,仅有一个查询结果,就用resultType

6返回单个列段,有多个查询结果,就用resultType

(2)

 如果是单表的情况下,resultType与resultMap都可以使用。
1 使用resultMap返回映射关系,指的是实体类与数据库字段的关系

2 使用resultType返回List<T>

3 使用resultType返回单个对象

4 使用resultType返回List<Map>【适用于多表查询返回结果集】

5 使用resultType返回Map<String,Object>【适用于多表查询返回单个结果集】

四. Mybatis中的分页

1. 分页的重要性与介绍

分页,顾名思义,即将大数据集按照一定的页数进行切割,以便在用户界面上进行逐页展示,提升用户体验和数据加载效率。Mybatis作为一款Java持久层框架,内置了强大的分页插件,能够轻松应对海量数据的分页查询需求。

2. 案例演示:Mybatis实现分页

1.导入分页的pom.xml依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.2</version>
</dependency>

2.在Mybatis.cfg.xml中配置拦截器

<plugins>
    <!-- 配置分页插件PageHelper, 4.0.0以后的版本支持自动识别使用的数据库 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
    </plugin>
</plugins>

3.导入PageBean工具类

package com.zking.pagination.entity;

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.编写繁琐的接口类,接口实现类(接口类就不放上去了)

@Override
    public List<Book> like4(String bname, PageBean pageBean) {
        if(pageBean!=null&&pageBean.isPagination()){
            PageHelper.startPage(pageBean.getPage(),pageBean.getRows());
        }
        List<Book> books = BookMapper.like4(bname);
        if(pageBean!=null&&pageBean.isPagination()){
            PageInfo<Book> info = new PageInfo<>(books);
            System.out.println("当前页"+info.getPageNum());
            System.out.println("展示记录数" + info.getPageNum());
            System.out.println("符合查询条件的总记录数" + info.getTotal());
            pageBean.setTotal((int) info.getTotal());
        }
        return books;
    }

5.开始测试

 @Test
    public void Testlike4(){
        PageBean pageBean = new PageBean();
        pageBean.setRows(20);
        pageBean.setPage(2);
        bookBiz.like4("%圣墟%",pageBean).forEach(System.out::println);
    }

这里我们为分页设置参数,查询第二页,每次查询20条数据

测试结果:

数据结果过多,就不演示了 

五.mybatis中如何处理特殊字符

1.<![CDATA[]]>的介绍

<![CDATA[]]>是XML中的一个特殊语法,用于标记CDATA(不解析为XML实体)的部分,

使用<![CDATA[ ]]>可以方便地处理包含特殊字符的内容,而无需使用转义字符或动态拼接SQL语句。这样可以提高代码的可读性和维护性。

需要注意的是,尽管使用<![CDATA[ ]]>可以确保内容中的特殊字符不被解析为XML实体,但仍然需要谨慎处理防止SQL注入攻击。建议在执行SQL操作时,始终使用参数绑定和其他安全措施来保护应用程序的安全性。

2.案例演示 

<select id="queryByMinMax" resultType="com.YU.model.Book" parameterType="com.YU.dto.BookDto" >
    select
    <include refid="Base_Column_List" />
    from t_mvc_book
    where <![CDATA[price <#{max} and price >#{min}]]>
  </select>
price <#{max} and price >#{min}中的“<”,“>”为特殊字符,在被<![CDATA[ ]]>包裹时不会被解析为XML实体,而作为原始文本处理

今天的学习总结就到这里了,大家的支持是博主更新的动力,觉得写得好的话记得点赞加收藏

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ChatYU.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值