【MyBatis】动态SQL和mybatis分页的运用

目录

一、mybatis动态SQL

1、if

2、foreach

3、choose、when、otherwise

二、mybatis模糊查询

1、mybatis模糊查询的三种方式

2、mybatis中#与$的区别

三、MyBatis结果映射(单表多表查询)

使用mybatis的各种场景

四、mybatis分页

1、利用sql和map

2、利用分页插件

五、mymatis特殊字符


 

一、mybatis动态SQL

 

update 表名 set name=?,age=? where id=?

如果我们的前台没有传参,比如没有传入我们的name值,name就会把字段值改为null,这就违背了我们编码的初衷。

许多人会使用类似于where 1 = 1 来作为前缀,在代码中会用if判断是否为null,再用and进行一个sql拼接。

 

我们可以用常用的几个标签:

1、if

再if标签里面,test里面的条件满足了,才会把后面的sql语句进行拼接

<!-- 示例 -->
<select id="updateByPrimaryKeySelective" parameterType="com.tgq.model.Book">
        SELECT * FROM Book WHERE price>= 9.9
        <if test="name != null and name != ''">
            AND name like '%${name}%'
        </if>
</select>

2、foreach

 

我们再调用删除的方法的时候,通常会与SQL语句中的IN查询条件结合使用。

delete from student where id in()

parameterType为List(链表)或者Array(数组),后面在引用时,参数名必须为list或者array。如在foreach标签中,collection属性则为需要迭代的集合,由于入参是个List,所以参数名必须为list。

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

测试一下结果

   @Test
    public void selectById() {
        List<Integer> bids = Arrays.asList(new Integer[]{30, 31, 32, 33});
        bookbiz.selectById(bids).forEach(System.out::println);
    }

bb6ed27ae19c48fd9f4deab4566fcf84.png

3、choose、when、otherwise

你可以使用choosewhenotherwise元素来实现类似于switch语句的逻辑。

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

如果传入的id不为空,则会在SQL语句中包含AND id = #{id}这个条件;如果传入的name不为空,则会在SQL语句中包含AND name = #{name}这个条件;否则,会在SQL语句中包含AND status = 'active'这个条件。

二、mybatis模糊查询

我们写入一个xml文件,log4j2.xml 用于打印出来你的sql语句

<?xml version="1.0" encoding="UTF-8"?>

<!-- status : 指定log4j本身的打印日志的级别.ALL< Trace < DEBUG < INFO < WARN < ERROR
	< FATAL < OFF。 monitorInterval : 用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s. -->
<Configuration status="WARN" monitorInterval="30">
    <Properties>
        <!-- 配置日志文件输出目录 ${sys:user.home} -->
        <Property name="LOG_HOME">/root/workspace/lucenedemo/logs</Property>
        <property name="ERROR_LOG_FILE_NAME">/root/workspace/lucenedemo/logs/error</property>
        <property name="WARN_LOG_FILE_NAME">/root/workspace/lucenedemo/logs/warn</property>
        <property name="PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t-%L] %-5level %logger{36} - %msg%n</property>
    </Properties>

    <Appenders>
        <!--这个输出控制台的配置 -->
        <Console name="Console" target="SYSTEM_OUT">
            <!-- 控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
            <ThresholdFilter level="trace" onMatch="ACCEPT"
                             onMismatch="DENY" />
            <!-- 输出日志的格式 -->
            <!-- %d{yyyy-MM-dd HH:mm:ss, SSS} : 日志生产时间 %p : 日志输出格式 %c : logger的名称
                %m : 日志内容,即 logger.info("message") %n : 换行符 %C : Java类名 %L : 日志输出所在行数 %M
                : 日志输出所在方法名 hostName : 本地机器名 hostAddress : 本地ip地址 -->
            <PatternLayout pattern="${PATTERN}" />
        </Console>

        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用 -->
        <!--append为TRUE表示消息增加到指定文件中,false表示消息覆盖指定的文件内容,默认值是true -->
        <File name="log" fileName="logs/test.log" append="false">
            <PatternLayout
                    pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </File>
        <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size, 则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 -->
        <RollingFile name="RollingFileInfo" fileName="${LOG_HOME}/info.log"
                     filePattern="${LOG_HOME}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
            <ThresholdFilter level="info" onMatch="ACCEPT"
                             onMismatch="DENY" />
            <PatternLayout
                    pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
            <Policies>
                <!-- 基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1 hour。 modulate=true用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am...而不是7am. -->
                <!-- 关键点在于 filePattern后的日期格式,以及TimeBasedTriggeringPolicy的interval, 日期格式精确到哪一位,interval也精确到哪一个单位 -->
                <!-- log4j2的按天分日志文件 : info-%d{yyyy-MM-dd}-%i.log -->
                <TimeBasedTriggeringPolicy interval="1"
                                           modulate="true" />
                <!-- SizeBasedTriggeringPolicy:Policies子节点, 基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小. -->
                <!-- <SizeBasedTriggeringPolicy size="2 kB" /> -->
            </Policies>
        </RollingFile>

        <RollingFile name="RollingFileWarn" fileName="${WARN_LOG_FILE_NAME}/warn.log"
                     filePattern="${WARN_LOG_FILE_NAME}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="warn" onMatch="ACCEPT"
                             onMismatch="DENY" />
            <PatternLayout
                    pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
            <Policies>
                <TimeBasedTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="2 kB" />
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
            <DefaultRolloverStrategy max="20" />
        </RollingFile>

        <RollingFile name="RollingFileError" fileName="${ERROR_LOG_FILE_NAME}/error.log"
                     filePattern="${ERROR_LOG_FILE_NAME}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd-HH-mm}-%i.log">
            <ThresholdFilter level="error" onMatch="ACCEPT"
                             onMismatch="DENY" />
            <PatternLayout
                    pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
            <Policies>
                <!-- log4j2的按分钟 分日志文件 : warn-%d{yyyy-MM-dd-HH-mm}-%i.log -->
                <TimeBasedTriggeringPolicy interval="1"
                                           modulate="true" />
                <!-- <SizeBasedTriggeringPolicy size="10 MB" /> -->
            </Policies>
        </RollingFile>

    </Appenders>

    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 -->
    <Loggers>
        <!--过滤掉spring和mybatis的一些无用的DEBUG信息 -->
        <logger name="org.springframework" level="INFO"></logger>
        <logger name="org.mybatis" level="INFO"></logger>

        <!-- 第三方日志系统 -->
        <logger name="org.springframework" level="ERROR" />
        <logger name="org.hibernate" level="ERROR" />
        <logger name="org.apache.struts2" level="ERROR" />
        <logger name="com.opensymphony.xwork2" level="ERROR" />
        <logger name="org.jboss" level="ERROR" />


        <!-- 配置日志的根节点 -->
        <root level="all">
            <appender-ref ref="Console" />
            <appender-ref ref="RollingFileInfo" />
            <appender-ref ref="RollingFileWarn" />
            <appender-ref ref="RollingFileError" />
        </root>

    </Loggers>

</Configuration>

394740466ec1480e9f88c09143bc801e.png

1、mybatis模糊查询的三种方式

  1. 第一种
    <select id="like01" resultType="com.tgq.model.Book" parameterType="java.util.List">
            select
            <include refid="Base_Column_List"/>
            from t_mvc_book
            where bname like #{bname}
        </select>

    测试的结果

     @Test
        public void like01() {
            bookbiz.like01("%圣%").forEach(System.out::println);
        }
    

    2de1b11be3a345ef96fb711366ddd25e.png

     

  2. 第二种:$需要单引号,$传参是占位符的形式
     

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

     测试的结果

      @Test
        public void like02() {
            bookbiz.like02("%圣%").forEach(System.out::println);
        }
    

    8781356219b643db88e6e6691b78dad8.png

     

     

  3. 第三种
     

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

     测试的结果

        @Test
        public void like03() {
            bookbiz.like03("%圣%").forEach(System.out::println);
        }
    

    57390fae7d0f41ad8acb960ec0fa2737.png

     

2、mybatis中#与$的区别

  1. $是占位符赋值,#是预处理SQL。
  2. 外在形式,$传参不带引号'',#传参自带引号''。
  3. $传参存在sql注入,#不存在。
  4. $可以用来做动态列,完成动态sql的开发。

 

三、MyBatis结果映射(单表多表查询)

 

 

<resultMap id="BaseResultMap" type="com.tgq.model.Book">
        <constructor>
            <idArg column="bid" jdbcType="INTEGER" javaType="java.lang.Integer"/>
            <arg column="bname" jdbcType="VARCHAR" javaType="java.lang.String"/>
            <arg column="price" jdbcType="REAL" javaType="java.lang.Float"/>
        </constructor>
    </resultMap>

这个代码中,column属性写的是数据库里面的字段,里面有一个name的属性,是实体类里面的字段,当数据库里面的字段喝实体类里面的字段是一样的可以不用写name属性;

如果数据库里面出现了下划线,比如data_time,就需要在

<arg column="data_time",name="DataTime" jdbcType="VARCHAR" javaType="java.lang.String"/>

 

 

使用mybatis的各种场景

使用mybatis的各种场景,返回的结果是多样的,resultType/resultMap?

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

       <select id="selectresultType" resultType="com.tgq.model.Book" parameterType="java.lang.Integer">
              select
              <include refid="Base_Column_List"/>
              from t_mvc_book
              where bid = #{bid,jdbcType=INTEGER}
          </select>
    • 编写接口实现接口
      Book selectresultType(Integer bid);
          @Override
          public Book selectresultType(Integer bid) {
              return bookMapper.selectresultType(bid);
          }

       

    • 测试方法
          @Test
          public void selectresultType() {
              System.out.println("测试resultType查询");
              Book book = bookbiz.selectByPrimaryKey(12);
              System.out.println(book);
          }
    • 测试结果465bec7840e14d9daaf9a0368f884cfb.png

       

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

     

    • resultType/resultMap。resultType一个或多个都用实体对象
      <select id="selectresultType01" resultType="com.tgq.model.Book">
              select
              <include refid="Base_Column_List"/>
              from t_mvc_book
          </select>
      
          <select id="selectresultMap01" resultMap="BaseResultMap">
              select
              <include refid="Base_Column_List"/>
              from t_mvc_book
          </select>

       

    • 编写接口方法和实现接口
          List<Book> selectresultType01();
      
          List<Book> selectresultMap01();
      @Override
          public List<Book> selectresultType01() {
              return bookMapper.selectresultType01();
          }
      
          @Override
          public List<Book> selectresultMap01() {
              return bookMapper.selectresultMap01();
          }

       

    • 测试方法
          @Test
          public void selectresultType01() {
              bookbiz.selectresultType01().forEach(System.out::println);
          }
      
          @Test
          public void selectresultMap01() {
              bookbiz.selectresultMap01().forEach(System.out::println);
          }
      

       

    • 测试结果是一样的af7c19fee19340589cce42f84c2a682f.png
  3. 返回多表的对应的实体类,仅有一个查询结果,通常用resultType,也可以用resultMap。
  4. 返回多表的对应的实体类,有多个查询结果,通常用resultType,也可以用resultMap。

     

    • 编写配置,我们可以不用配置的表,可以调用其他的表
      <select id="select01" resultType="java.util.Map" parameterType="java.util.Map">
              select s.*, sc.cid, sc.score
              from t_mysql_student s,
                   t_mysql_score sc
              where s.sid = sc.sid
                and sc.sid = #{sid}
                and sc.cid = #{cid}
          </select>
      
          <select id="select02" resultType="java.util.Map" parameterType="java.util.Map">
              select s.*, sc.cid, sc.score
              from t_mysql_student s,
                   t_mysql_score sc
              where s.sid = sc.sid
          </select>

       

    • 编写接口和实现类
          Map select01(Map map);
      
          List<Map> select02(Map map);
          @Override
          public Map select01(Map map) {
              return bookMapper.select01(map);
          }
      
          @Override
          public List<Map> select02(Map map) {
              return bookMapper.select02(map);
          }

       

    • 测试方法
          @Test
          public void select01() {
              Map map = new HashMap();
              map.put("sid", "01");
              map.put("cid", "01");
              System.out.println(bookbiz.select01(map));
          }
      
          @Test
          public void select02() {
              Map map = new HashMap();
              map.put("sid", "01");
              map.put("cid", "01");
              bookbiz.select02(map).forEach(System.out::println);
          }

       

    • 测试结果
      4f6aabd244a9444d819e9e8bfe1d116a.png
      02747aceaf6e48a3ad6ed6bd61e4356d.png
  5. 返回单个列表,仅有一个查询结果,就用resultType。
  6. 返回单个列表,有多个查询结果,就用resultType。
    • 配置xml
      <!--单个-->
          <select id="selectByString01" resultType="java.lang.String" parameterType="java.lang.Integer">
              select bname
              from t_mvc_book
              where bid = #{bid,jdbcType=INTEGER}
          </select>
          <!--多个-->
          <select id="selectByString02" resultType="java.lang.String" parameterType="java.lang.String">
              select bname
              from t_mvc_book
              where bname like concat('%', #{bname}, '%')
          </select>
    • 编写接口和实现接口

          String selectByString01(Integer bid);
      
          List<String> selectByString02(String bname);
        @Override
          public String selectByString01(Integer bid) {
              return bookMapper.selectByString01(bid);
          }
      
          @Override
          public List<String> selectByString02(String bname) {
              return bookMapper.selectByString02(bname);
          }

       

    • 测试方法

          @Test
          public void selectByString01() {
              System.out.println(bookbiz.selectByString01(60));
          }
      
          @Test
          public void selectByString02() {
              Map map = new HashMap();
              map.put("sid", "01");
              map.put("cid", "01");
              bookbiz.selectByString02("圣墟").forEach(System.out::println);
          }

       

    • 测试结果f08d67ed531f473499bb1ed57b9b25eb.png

      981485ca2b9c4db6ae0edb897a67d41c.png

resultType:对应返回类型。

resultMap:对应返回映射关系,指的是实体类与数据表字段关系。

单表查询,返回单列,返回多表查询结果,是用resultType

查询的结果,需要有关属性的体现,那么就用resultMap

 

四、mybatis分页

快速实现mybatis分页

1、利用sql和map

 

我们首先在我们的配置xml里面编写sql

    <select id="selectFy01" resultType="com.tgq.model.Book" parameterType="java.util.Map">
        select
        <include refid="Base_Column_List"/>
        from t_mvc_book
        where bname like #{bname} limit #{start},#{size}
    </select>

编写我们的接口并且实现我们的接口

    List<Book> selectFy01(Map map);
    @Override
    public List<Book> selectFy01(Map map) {
        return bookMapper.selectFy01(map);
    }

测试方法

    @Test
    public void selectFy01() {
        Map map = new HashMap();
        map.put("bname", "%圣墟%");
        map.put("start", 10);
        map.put("size", 10);
        bookbiz.selectFy01(map).forEach(System.out::println);
    }

2f2057afd6184cb2b8a3e5f10b4d83b1.png

 

 

2、利用分页插件

因为我们的Mybatis的分页功能很弱,它是基于内存的分页(查出所有记录再按偏移量offset和边界limit取结果),在大数据量的情况下这样的分页基本上是没有用的。

编写一个分页类

package com.tgq.utils;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.Map;

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
				+ "]";
	}
}

配置pom.xml文件

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

配置mybatis.cfg.xml

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

编写接口及接口实现类

    List<Book> selectFy02(@Param("bname") String bname);
    @Override
    public List<Book> selectFy02(String bname, PageBean pageBean) {
        if (pageBean != null && pageBean.isPagination())
        PageHelper.startPage(pageBean.getPage(),pageBean.getRows());
        List<Book> books = bookMapper.selectFy02(bname);
        if (pageBean != null && pageBean.isPagination()) {
            PageInfo<Book> info = new PageInfo<>(books);
            System.out.println("当前页:" + info.getPageNum());
            System.out.println("记录数:" + info.getPageSize());
            System.out.println("总记录数:" + info.getTotal());
            pageBean.setTotal((int) info.getTotal());
        }
        return books;
    }

测试方法

    @Test
    public void selectFy02() {
        String bname = "圣墟";
        PageBean pageBean = new PageBean();
        pageBean.setPage(2);
        pageBean.setRows(10);
        bookbiz.selectFy02(bname, pageBean).forEach(System.out::println);
    }

f211232fd7e24af98668e9c007c24ff6.png

a6a3bea82b3e4d7991e32768b5b12d3a.png

 

五、mymatis特殊字符

 

  1. pojo/entity/model:实体类
  2. vo:view object 视图对象:专门用来展示        如:java.util.Map
  3. dto:接受参数

我们在编写sql的时候会用< 和 > 

配置xml,如果要用<和>,就必须用<![CDATA[]]>包裹起来,不然会报错

<select id="selectMinMax" resultType="com.tgq.model.Book" parameterType="com.tgq.dto.BookDto">
        select
        <include refid="Base_Column_List"/>
        from t_mvc_book
        where
    <![CDATA[
 price > #{min} and price < #{max}
    ]]>

    </select>

我们的resultType可以使用BookDto ,里面有新的属性

package com.tgq.dto;

import com.tgq.model.Book;

/**
 * @软件包名 com.tgq.dto
 * @用户 tgq
 * @create 2023-08-24 下午6:24
 * @注释说明:
 */
public class BookDto extends Book {
    private float min;
    private float max;

    public float getMin() {
        return min;
    }

    public void setMin(float min) {
        this.min = min;
    }

    public float getMax() {
        return max;
    }

    public void setMax(float max) {
        this.max = max;
    }
}

编写接口和实现接口

    List<Book> selectMinMax(BookDto bookDto);
    @Override
    public List<Book> selectMinMax(BookDto bookDto) {
        return bookMapper.selectMinMax(bookDto);
    }

编写测试方法

    @Test
    public void selectMinMax() {
        BookDto bto = new BookDto();
        bto.setMin(20);
        bto.setMax(60);
        bookbiz.selectMinMax(bto).forEach(System.out::println);
    }

e4171729c5b84428937b493032918720.png
 

 

当然我们的xml可以不用<![CDATA[]]>,<和>就需要用&gt;和&lt;代替

    <select id="selectMinMax" resultType="com.tgq.model.Book" parameterType="com.tgq.dto.BookDto">
        select
        <include refid="Base_Column_List"/>
        from t_mvc_book
        where  price &gt; #{min} and price &lt; #{max}

    </select>

结果是一样的

116685763b714ccdae0e174722206fc1.png

上一章【mybatis入门Idea搭建】

 

  • 14
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 20
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

无法自律的人

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

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

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

打赏作者

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

抵扣说明:

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

余额充值