Mybatis学习笔记

目录

一.MyBatis简介

1.Java学习流程

2.MyBatis特性

3.Mybatis的下载

4.MyBatis和其他持久化层技术对比

二.搭建MyBatis

1.开发环境

2.创建maven工程

3.创建MyBatis的核心配置文件

4. 创建Mapper接口

5.创建MyBatis的映射文件

6.通过juint测试功能

7.加入log4j日志功能

日志的级别

三.核心配置文件详解

TypeAliases标签

 Package标签映入多个映射文件

四.MyBatis的增删改插

五.MyBatis获取参数值的两种方式(重点)

MyBatis获取参数值的各种情况

1.mapper接口方法的参数为单个字面量类型

2.mapper接口方法的参数为多个时

3.若mapper接口方法的参数有多个时,可以手动将这些参数放在mapper中存储

4.如果mapper接口方法的参数是实体类类型的参数(重点)

5.使用@Param注解(最常用的方式,除了第三种情况以外,其他情况都可以用它)

总结

六.MyBatis的各种查询功能 

1、查询一个实体类对象

2、查询一个list集合

3、查询单个数据

4、查询一条数据为map集合

5、查询多条数据为map集合

方法一 用list集合包含map集合

重点

方法二通过注释@mapkey来直接返回多个不同的map

七. 特殊SQL的执行

1.模糊查询

2.批量删除

3.动态设置表名

4.添加功能获取自增主键

八.自定义映射resultMap

1.resultMap处理字段和属性的映射关系

若字段名和实体类中的属性基本一致,只需要下划线和驼峰互相转换

若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射

2.多对一映射处理

a>级联属性赋值

b>association

c>分布查询

分布查询的优点:延迟加载

 3.一对多映射

a>collection

九.动态SQL

1.if标签

2.where标签

3.trim标签

4.choose,when,otherwise标签

5.for-each标签

Mapper映射获取传递值分类:

6.SQL片段

十.MyBatis的缓存

1.MyBatis的一级缓存

2.MyBatis的二级缓存

3.二级缓存的相关配置

4.Mybatis缓存查询的顺序 

5.整合第三方缓存EHCache

a>添加依赖

b>各个jar包的功能

c>创建EHCache的配置文件ehcahe.xml

d>设置二级缓存的类型

e>加入logback日志

f>EHCache配置文件说明

十一.MyBatis的逆向工程

1.创建逆向工程的步骤(MyBatis逆向工程简洁版)

1.添加依赖和插件

2.创建MyBatis核心配置文件

3.创建逆向工程的配置文件

4.执行MBG插件的generate目标

2.QBC查询(MyBatis逆向工程奢华尊享版)

数据查询

数据修改

十二.分页插件

1.分页插件使用步骤

a>添加依赖

b>配置分页插件

2.分页插件的使用

方法一:直接输出pageHelper.startPage的值

方法二:使用pageinfo获取数据集合


一.MyBatis简介

  • MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下,iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github
  • iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO)

1.Java学习流程

2.MyBatis特性

(1).MyBatis是支持定制化SQL,存储过程以及高级映射的持久层框架

(2).MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集

(3).MyBatis可以使用简单的xml或注解方式用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的java对象)映射成数据库中的记录

(4)MyBatis是一个半自动的ORM(Object Relation Mapping)框架

3.Mybatis的下载

MyBatis下载地址:https://github.com/mybatis/mybatis-3

4.MyBatis和其他持久化层技术对比

  • JDBC
    • SQL夹杂在Java代码中耦合度高,导致硬编码(代码写死)内伤
    • 维护不易且实际开发需求中SQL有变化,频繁修改的情况多见
    • 代码冗长,开发效率低
  • Hibernate和JPA
    • 操作简单,开发效率高
    • 程序中的长难复杂SQL需要绕过框架(简单的增删改插不需要)
    • 内部自动生成的SQL,不容易做特殊优化
    • 基于全映射的全自动框架,大量字段的POJO(JavaBean类)进行部分映射时会比较困难(想查一个表中的单独几个字段不容易)
    • 反射操作太多,导致数据库性能下
  • MyBatis
    • 轻量级,性能出色
    • SQL和Java编码分开,功能边界清晰。JAva代码专注业务,SQL语句专注数据
    • 开发效率稍逊于HIbernate,但是完全能够接受

二.搭建MyBatis

1.开发环境

IDEA:idea 2019.3

构建工具:maven 3.5.4

MySQL版本:MYSQL 8.0.2

MyBAtis版本:MyBatis 3.5.7

2.创建maven工程

(1)创建maven父工程

  1. 第一步选中maven工程创建

  2. 输入你的ArtifactId和groupId(项目唯一标识符,坐标)

  3. 点击finish,Maven父工程创建成功

  4. 设置项目Maven,点击Maven右边的小扳手就可以打开设置Maven仓库和setting配置文件,Maven下载依赖的时候先去寻找本地仓库有没有,没有再去自己setting配置中的镜像网站下载,没配置直接中央仓库下载,配置好点击apply

  5. 创建子工程(在这里面编写自己的模块功能)

     

  6.  配置打包方式,packaging为jar包表明是一个jar工程 

     

  7. 导入依赖

     <dependencies>
            <!-- Mybatis核心 -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
            </dependency>
            <!-- junit测试 -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <scope>test</scope>
            </dependency>
            <!-- MySQL驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
        </dependencies>

     父工程的最要作用就是管理子工程jar依赖的版本问题,在父工程导入的依赖,在子工程中使用就不需要在编写版本号

3.创建MyBatis的核心配置文件

习惯上命名为mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合Spring之后,这个配置文件可以省略,所以大家操作可以直接复制,粘贴。

核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息

核心配置文件存放的位置是src/main/resources目录下

 (1)创建一个xml文件

(2)代码段复制进去

<?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>
    <!--设置连接数据库的环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入映射文件-->
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>
</configuration>

4.创建Mapper接口

MyBatis中的mapper接口相当于以前的dao。但是区别在于,mapper仅仅是接口,我们不需要提供实现类

 (1)数据库student表和实体类

(2) Mapper接口创建

5.创建MyBatis的映射文件

相关概念:ORMObject Relationship Mapping)对象关系映射

  • 对象:java的实体类对象
  • 关系:关系型数据库
  • 映射:二者之间的对应关系
Java概念数据库概念
属性字段/列
对象记录/行

1.映射文件的命名规则

  • 表所对应的实体类的类名+Mapper.xml
  • 列如:表t_user,映射的实体类为User,所对应的接口UserMapper,对应的映射文件名为UserMapper.xml
  • 因此一个映射文件对应一个实体类,对应一张表的操作
  • MyBatis映射文件用于编写SQL,访问以及操作表中的数据

2.MyBatis映射文件存放的位置是src/main/resources/mappers目录下

  • mapper接口中的全类名和映射文件的命名空间(namespace)保持一致
  • mapper接口中的方法的方法名和映射文件中编写SQL的标签的id属性保持一致

3.MyBatis面向接口的两个一致和对应关系

MyBatis面向接口的两个一致
* 1.映射文件的namespace要和mapper接口的全类名一致
* 2.映射文件中SQL语句的id要和mapper接口中的方法名一致
*
* 对应关系
* 表--实体类--mapper接口--xml映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cn.cslg.mybatis.mapper.UserMapper">
<!--  sql语句方法写入 int insertUser(); -->
    <insert id="insertUser">
        insert into t_user values (null,'admin','123456',23,'男','123.@qq.com')
    </insert>
</mapper>

Mapper核心配置文件引入Mapper映射文件

    <!--引入映射文件-->
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>

6.通过juint测试功能

  1. SqlSession:代表Java程序和数据库之间的会话,(HTTPSession是Java程序和浏览器之间的会话)
  2. SqlSessionFactory:是“生产”SqlSession的“工厂”
  3. 工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们可以把创建这个对象的相关代码封装到一个“工厂类”中,以后使用这个工厂类来“生产”我们需要的对象
package com.cn.cslg.mybatis.test;

import com.cn.cslg.mybatis.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

public class MyBatisTest {
    /**
     * SqlSession默认不自动提交事务,若需要自动提交事务
     * 可以使用sqlSessionFactoryp。open(true)
     * 
     */

    @Test
    public void testMyBatisInsertUser() throws IOException {
        //1.加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        //2.获取SqlSessionFactoryBuilder,是SqlSession工厂对象的构建对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //3.获取SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);
        //4.获取SqlSession,是java和数据库之间的会话,就像httpSession是浏览器和java之间的会话,设置为true开启自动提交事务
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        //获取mapper接口对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //测试功能
        mapper.insertUser();
        //提交事务
//        sqlSession.commit();

    }
}

7.加入log4j日志功能

1.加入依赖

        <!-- log4j日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

2.加入log4j的配置文件

log4j的配置文件名为log4j.xml,存放的位置为src/main/resources目录下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!-- 命名空间爆红没有关系,不影响使用,下面的level标签中是指的输出范围 -->
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
        </layout>
    </appender>
    <logger name="java.sql">
        <level value="debug" />
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info" />
    </logger>
    <root>
        <level value="debug" />
        <appender-ref ref="STDOUT" />
    </root>
</log4j:configuration>

日志的级别

FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)

从左到右打印的内容越来越详细

三.核心配置文件详解

核心配置文件中的标签必须按照固定的顺序

properties、settings、typeAliases、typeHandlers、objectFactory、objectWrapperFactory、reflectorFactory、plugins、environments、databaseIdProvider、mappers
 

<?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>
    <!--设置连接数据库的环境-->
    <!--
        environments:配置多个连接数据库环境
        属性:
            default:设置默认使用的环境的id

    -->
    <environments default="development">
        <!--
            environment:配置某个具体的环境
            属性:
                id:表示连接数据库的环境的唯一标识,不能重复
            -->
        <environment id="development">
            <!--
                transactionManager:设置事务管理方式
                属性:
                    type="JDBC|MANAGED"
                    JDBC:表示当前环境中,执行SQL时,使用的事JDBC中原生的事务管理方式,事物的提交和回滚需要手动操作
                    MANAGED:别管理,列如Spring
                -->
            <transactionManager type="JDBC"/>
            <!--
                dataSource:配置数据源的类型
                type="POOLED|UNPOOLED|NDI"
                POOLED:表示使用数据库连接池缓存数据库链接
                UNPOOLED:表示不适用竖亥句酷连接池
                JNDI:表示使用上下稳重的数据源
                  -->
            <dataSource type="POOLED">
                <!--      设置链接数据库的驱动          -->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!--      设置链接数据库的链接地址          -->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <!--      设置链接数据库的用户名          -->
                <property name="username" value="root"/>
                <!--      设置链接数据库的密码          -->
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入映射文件-->
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>
</configuration>

TypeAliases标签

 typeAliases用来设置类名的别名,通常使用typeAliases下面的package标签设置一整个实体类的包的别名,在Mybatis核心文件中的配置

    <!--
        typeAlias:设置某个类型的别名
        属性:
            type:设置需要设置别名的类型
            alias:设置某个类型的别名,若不设置该属性,那么该类型拥有默认的别名,即类名不区分大小写
        -->
    <typeAliases>
        <typeAlias type="com.cn.cslg.mybatis.pojo.User" alias="User"></typeAlias>
    <!-- 以包为单位,将包下所有的类型设置默认的类型别名, 且类名不区分大小写    -->
        <package name="com.cn.cslg.mybatis.pojo"/>
    </typeAliases>

在Mapper映射文件中的使用类名的别名,resultType中的User的首字母大小写不用区分

   
    
    <select id="selectAllUser" resultType="User">
        select * from t_user
    </select>


    <select id="selectAllUser" resultType="user">
        select * from t_user
    </select>

 Package标签映入多个映射文件

包的方式映入Mapper映射文件,解决了配置多个包的问题,其中需要注意在resourc文件夹下面创建多个目录需要用‘/’分割而不是用'.'分隔。

  1. 首先必须mapper接口所在的包要和映射文件所在的包一致
  2. mapper接口要和映射文件的名字一致

    <mappers>
<!--        <mapper resource="mappers/UserMapper.xml"/>-->

        <!--
             已包为单位映入映射文件
             要求:
             1.mapper接口所在的包要和映射文件所在的包一致
             2.mapper接口要和映射文件的名字一致
             -->
        <package name="com.cn.cslg.mybatis.mapper"/>
    </mappers>

四.MyBatis的增删改插

1.添加

    <insert id="insertUser">
        insert into t_user values (null ,'admin','123456',23,'男','123.@qq.com')
    </insert>

2.删除

    <delete id="deleteUser">
        delete from t_user where id ='3'
    </delete>

3.修改

    <update id="updateUser">
        update t_user set username = 'zkkk' where id=1
    </update>

4.查询实体类对象

查询实体类的对象需要设置resultType为实体类返回值,可以通过TypeAliases在MyBatis核心配置文件中通过TypeAliases标签来更改类的别名

<!--    User selectUser();-->
<!--
    查询功能的标签必须设置resultType或者resultMap
    resultType:设置默认的映射关系
    resultMap:设置自定义的映射关系
-->
    <select id="selectUser" resultType="com.cn.cslg.mybatis.pojo.User">
        select * from t_user where id = 1
    </select>

5.查询多个实体类对象

通过更改类的别名来,在resultType中简单的命名,因为通过package标签进行多个映射文件导入,所以别名即为实体类的类名不用区分大小写!

 <mappers>
<!--        <mapper resource="mappers/UserMapper.xml"/>-->

        <!--
             已包为单位引入映射文件
             要求:
             1.mapper接口所在的包要和映射文件所在的包一致
             2.mapper接口要和映射文件的名字一致
             -->
        <package name="com.cn.cslg.mybatis.mapper"/>
    </mappers>
<!--    List<User> selectAllUser();-->
    <select id="selectAllUser" resultType="User">
        select * from t_user
    </select>

注意:

1.查询的标签select必须设置属性resultType或resultMap ,用于设置实体类和数据库的映射关系

resultType:自动映射,用于属性名和标准字段名一致的情况

resultMap:自定义映射,用于一对多或多对一字段名和属性名不一致的情况

2.当查询的数据为多条时,不能使用实体类作为返回值,只能使用集合,否则会抛出异常TooManyResultsException;但是若查询的数据只有一条,可以使用实体类或集合为返回值

五.MyBatis获取参数值的两种方式(重点)

  • MyBatis获取参数值的两种方式:${}和#{}
  • ${}本质字符串拼接,拼接的值为字符串类型或者日期类型,需要注意单引号问题
  • #{}本质占位符赋值

MyBatis获取参数值的各种情况

1.mapper接口方法的参数为单个字面量类型

  • 可以通过${}和#{}以任意的名称获取,最好是见名识意,但是需要注意${}的单引号问题
    <!--User getUserByUserName(String UserName);-->
    <select id="getUserByUserName" resultType="User">
    <!--select * from t_user where username = #{UserName}-->
        <!-- 注意单引号-->
        select * from t_user where username = '${UserName}'
    </select>

2.mapper接口方法的参数为多个时

  • 此时MyBatis会将这些参数放在map集合中,以两种方式进行存储
  1. a>以arg0,arg1...为键,以参数为值
  2. b>以param1,param2...为键,以参数为值
  3. 因此只需要通过#{}或者${}以键的方式访问值即可,注意${}的引号问题,l例子#{arg0}
    <!--User checkLogin(String username,String password);-->
    <select id="checkLogin" resultType="User">
        <!--    select * from t_user where username=#{arg0} and password=#{arg1}-->
        select * from t_user where username=#{param1} and password=#{param2}
    </select>

3.若mapper接口方法的参数有多个时,可以手动将这些参数放在mapper中存储

因此只需要通过#{}或者${}以键的方式访问值即可,注意${}的引号问题,l例子#{username}
    public void testGetAllUser(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
        //传输多个参数为mapper集合的时候判断登录
        Map<String,Object> map = new HashMap<>();
        map.put("username","zkkk");
        map.put("password","123456");
        User user = mapper.checkLoginByMap(map);
        System.out.println(user);

    }
    <!--User checkLoginByMap(Map<String,Object>map);-->
    <select id="checkLoginByMap" resultType="User">
        select * from t_user where username=#{username} and password=#{password}
    </select>

4.如果mapper接口方法的参数是实体类类型的参数(重点)

因此只需要通过#{}或者${}以属性的方式访问值即可,注意${}的引号问题,l例子#{类中的属性}
package com.cn.cslg.mybatis.pojo;

public class User {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private String sex;
    private String email;
    ....
}
    <!--int insertUser(User user);-->
    <insert id="insertUser">
        insert into t_user values(null ,#{username},#{password},#{age},#{sex},#{email})
    </insert>
    public void insertUser(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
        User user =new User(null,"Lisi","123456",12,"男","123456");
        int a = mapper.insertUser(user);
        System.out.println(a);

    }

5.使用@Param注解(最常用的方式,除了第三种情况以外,其他情况都可以用它)

此时MyBatis会将这些参数放在一个map集合之中,以两种方式进行存储
 a>@Param注解的值为键,以参数为值
 b>以param1,param2...为键,以参数位置
 因此只需要通过#{}和${}以键的方式访问值即可,但是需要注意${}的单引号问题
    public void testCheckLoginByParam(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
        User user = mapper.checkLoginByParam("zkkk", "123456");
        System.out.println(user);
    }
    /**
     * mapper接口
     * 验证登录(使用@param注解)
     */
    User checkLoginByParam(@Param("username") String username,@Param("password") String password);
    <!--User checkLoginByParam(@Param("username") String username,@Param("password") String password);-->
    <select id="checkLoginByParam" resultType="User">
        select * from t_user where username=#{username} and password=#{password}
    </select>

总结

  • 所有获取传传递值的情况可以分为两种
  • a>实体类型类型参数
  • b>使用@Param标识参数

六.MyBatis的各种查询功能 

1、查询一个实体类对象

 1.若查询出的数据只有一条
* a>可以通过实体类对象收
* b>可以通过list集合接收
* c>可以通过Map集合来接收
    /**
     * 根据id查询用户信息
     */
    User getUserId(@Param("Uid") Integer id);
  <!--User getUserId(@Param("Uid") Integer id);-->
    <select id="getUserId" resultType="User">
        select * from t_user where id=#{Uid}
    </select>

2、查询一个list集合

可以通过设置返回值为List<User>

  /**
     * 查询所有的用户信息
     */

    List<User> getAllUser();
 <!--List<User> getAllUser();-->
    <select id="getAllUser" resultType="User">
        select * from t_user
    </select>

3、查询单个数据

   /** MyBatis中设置了默认的类型别名
     * Java.lang.Integer-->int,integer
     * int-->_int,_integer
     * Map-->map
     * String-->string
     */

     /**
     * 查询用户信息总记录数
     */

    Integer getCount();
    <!--Integer getCount();-->
    <!--resultType 也可以写Integer integer int Int 类型别名不区分大小写-->
    <select id="getCount" resultType="java.lang.Integer">
        select count(*) from t_user
    </select>

4、查询一条数据为map集合

    /**
     * 根据id查询用户信息为一个map集合
     */
    Map<String,Object>getUserByIdToMap(@Param("id") Integer id);
    <!--Map<String,Object>getUserByIdToMap(@Param("id") Integer id);-->
    <select id="getUserByIdToMap" resultType="map">
        select * from t_user where id=#{id}
    </select>
结果:{password=123456, sex=男, id=1, age=23, email=123.@qq.com, username=zkkk}

5、查询多条数据为map集合

方法一 用list集合包含map集合

    List<Map<String,Object>>getAllUserByListMap();
    <!--List<Map<String,Object>>getAllUserByListMap();-->
    <select id="getAllUserByListMap" resultType="map">
        select * from t_user
    </select>

重点

     注意此时的resultType必须为map对象,因为查询的所有user对象被一一存入map中然后将每个map在存放在list集合中

     如果返回值是user的话,那么你会发现他其实list存放的不是多个包含User的map集合,而仅仅是多个User对象存储在list中,并未存放在map中

 public void getAllUserByListMap(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
        List<Map<String, Object>> allUserByListMap = mapper.getAllUserByListMap();
        System.out.println(allUserByListMap);
        System.out.println(allUserByListMap);
        //System.out.println(allUserByListMap.get(0));
        for (Map<String,Object> map: allUserByListMap){
            System.out.println(map);
            System.out.println(map.get("id"));
        }

    }
输出结果
[{password=123456, sex=男, id=1, age=23, email=123.@qq.com, username=zkkk}, {password=123456, sex=男, id=4, age=23, email=1281628436@qq.com, username=admin}, {password=123456, sex=男, id=7, age=23, email=123.@qq.com, username=admin}, {password=123456, sex=男, id=8, age=23, email=123.@qq.com, username=admin}, ]



{password=123456, sex=男, id=1, age=23, email=123.@qq.com, username=zkkk}
1
{password=123456, sex=男, id=4, age=23, email=1281628436@qq.com, username=admin}
4
{password=123456, sex=男, id=7, age=23, email=123.@qq.com, username=admin}
7
{password=123456, sex=男, id=8, age=23, email=123.@qq.com, username=admin}
8

如果是resultType返回值是User的话,输出内容,我们通过获得list集合中每一个元素map的key判断list集合中的元素为map集合

    <!--List<Map<String,Object>>getAllUserByListMap();-->
    <select id="getAllUserByListMap" resultType="user">
        select * from t_user
    </select>

 他输出了所有的内容但是会报一个没有key的错误,同时从输出上也能看出来这是一个list集合中包含着多个User对象

 @Test
    public void getAllUserByListMap(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
        List<Map<String, Object>> allUserByListMap = mapper.getAllUserByListMap();
        //System.out.println(allUserByListMap);
        System.out.println(allUserByListMap);
        System.out.println(allUserByListMap.get(0).get("id"));
        //for (Map<String,Object> map: allUserByListMap){
        //    System.out.println(map);
        //    System.out.println(map.get("id"));
        //
        //}

    }
结果

[User{id=1, username='zkkk', password='123456', age=23, sex='男', email='123.@qq.com'}, User{id=4, username='admin', password='123456', age=23, sex='男', email='1281628436@qq.com'}, User{id=7, username='admin', password='123456', age=23, sex='男', email='123.@qq.com'}, User{id=8, username='admin', password='123456', age=23, sex='男', email='123.@qq.com'}, User{id=9, username='admin', password='123456', age=23, sex='男', email='123.@qq.com'}, User{id=10, username='admin', password='123456', age=23, sex='男', email='123.@qq.com'}, User{id=11, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=12, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=13, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=14, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=15, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=16, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=17, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=18, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=19, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=20, username='Lisi', password='123456', age=12, sex='男', email='123456'}]

java.lang.ClassCastException: com.cn.cslg.mybatis.pojo.User cannot be cast to java.util.Map

方法二通过注释@mapkey来直接返回多个不同的map

可以在mapper接口的方法上添加@MapKey注解,此时就可以将每条数据转换的map集合作为值,以某个字段的值作为键,比如id,放在同一个map集合里面
    /**
     * 查询所有用户信息为map集合
     */
    @MapKey("id")
    Map<String,Object> getAllUserToMap();
 <select id="getAllUserToMap" resultType="map">
        select * from t_user
    </select>
 @Test
    public void getUserByIdMap(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
        Map<String, Object> userByIdToMap = mapper.getUserByIdToMap(1);
        //System.out.println(userByIdToMap.get("username"));
        System.out.println(mapper.getUserByIdToMap(1));
        System.out.println(mapper.getAllUserToMap());
    }
输出结果:
{1={password=123456, sex=男, id=1, age=23, email=123.@qq.com, username=zkkk}, 4={password=123456, sex=男, id=4, age=23, email=1281628436@qq.com, username=admin}, 7={password=123456, sex=男, id=7, age=23, email=123.@qq.com, username=admin}, 8={password=123456, sex=男, id=8, age=23, email=123.@qq.com, username=admin}, }

七. 特殊SQL的执行

1.模糊查询

    /**
     * 根据用户名模糊查询用户信息
     */
    List<User> getUserByLike(@Param("username") String username);
  <!--List<User> getUserByLike(@Param("username") String username);-->
    <select id="getUserByLike" resultType="User">
        <!--占位符方法不行,下面三种都可以,推荐23两种
        select * from t_user where username like '%#{username}%'-->
        <!--select * from t_user where username like '%${username}%'-->
        <!--select * from t_user where username like concat('%',#{username},'%')-->
        select * from t_user where username like "%"#{username}"%"
    </select>

2.批量删除

 /**
     * 批量删除
     */
    int deleteMore(@Param("ids") String ids);
 <!--int deleteMore(@Param("ids") String ids);-->
    <delete id="deleteMore">
        <!--现实中为了防止sql注入通常使用foreach循环删除-->
        delete from t_user where id in (${ids})
    </delete>
    @Test
    public void testDeleteMore(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        SQLMapper mapper = sqlSession.getMapper(SQLMapper.class);
        int i = mapper.deleteMore("10,11,12");
        System.out.println(i);
    }

3.动态设置表名

    /**
     * 查询指定表中的数据
     */
    List<User> getUserByTableName(@Param("tableName") String tableName);
    <!--List<User> getUserByTableName(@Param("tableName") String tableName);-->
    <select id="getUserByTableName" resultType="User">
        select * from ${tableName}
    </select>

4.添加功能获取自增主键

t_clazz(cd,cname)

t_student(sid,sname,cid)

1.添加班级信息

2.获取新添加班级的id

3.为班级分配学生,将某个学生的班级id修改为新添加的班级id

userGeneratedKeys:设置当前便签中的sql使用了自增的id

keyPorperty:将自增的主键的值付给传输到映射文件中参数的某个属性

属性并不是在类中定义了才算,只要有get和set方法都算属性

    /**
     * 添加用户信息
     */
    void insertUser(User user);
    <!--
        void insertUser(User user);
        userGeneratedKeys:设置当前标签中的sql使用了自增的id
        keyProperty:将自增的主键的值赋值给传输到映射文件中参数的某个属性
    -->
    <insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
        insert into t_user values (null,#{username},#{password},#{age},#{sex},#{email})
    </insert>

八.自定义映射resultMap

1.resultMap处理字段和属性的映射关系

若字段名和实体类中的属性基本一致,只需要下划线和驼峰互相转换

 a>为字段起别名,保持和属性名一致

<select id="getAllEmp0" resultType="Emp">
    <!--设置别名方法解决字段名和属性名不一致的情况,在字段名后面空格打上属性名-->
    select eid,emp_name empName,age,sex,email from t_emp
</select>

 b>设置全局配置,将_自动映射为驼峰

 <setting name="mapUnderscoreToCamelCase" value="true"/>

若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射

 resultMap:设置自定义映射关系

  • 属性
    • id:唯一标识,不能重复
    • type:设置映射关系中的实体类类型
  • 子标签:
    • id:设置主键的映射关系
    • result:设置普通字段的映射关系
  • 子标签属性:
    • property:设置映射关系中的属性名,必须是type属性所设置的实体类类型中的属性名
    • colum:设置映射关系中的字段名,必须是sql语句查询出的字段名

    <!--
        resultMap:设置自定义映射关系
        id:唯一标识,不能重复
        type:设置映射关系中的实体类类型
        子标签:
        id:设置主键的映射关系
        result:设置普通字段的映射关系
        属性:
        property:设置映射关系中的属性名,必须是type属性所设置的实体类类型中的属性名
        colum:设置映射关系中的字段名,必须是sql语句查询出的字段名
        -->
    <resultMap id="empResultMap" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
        <!--<result property="did" column="did"></result>-->
    </resultMap>

2.多对一映射处理

查询所有员工信息包括其所对应的部门信息

//员工表
public class Emp {
    private Integer eid;
    private String empName;
    private Integer age;
    private String sex;
    private String email;
    private Dept dept;
}

//部门表
public class Dept {
    private Integer did;
    private String deptName;
}

a>级联属性赋值

 <!--处理多对一映射关系方式1:级联属性赋值-->
    <!--Emp getEmpAndDept(@Param("eid") Integer eid);-->
    <resultMap id="empAndDeptResultMapOne" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
        <result property="dept.did" column="did"></result>
        <result property="dept.deptName" column="dept_name"></result>
    </resultMap>

b>association

 association:处理多对一的映射关系
 property:需要处理的属性名
 JavaType:该属性的类型

<!--处理多对一映射关系方式2:association-->
    <resultMap id="empAndDeptResultMapTwo" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
        <!--
        association:处理多对一的映射关系
        property:需要处理的属性名
        JavaType:该属性的类型
        -->
        <association property="dept" javaType="Dept">
            <id property="did" column="did"></id>
            <result property="deptName" column="dept_name"></result>
        </association>
    </resultMap>

c>分布查询

首先查出员工的所有信息,通过员工信息里面的did在查出员工所属部门信息

1.先查出员工的所有信息

2.通过did参训员工所对应的部门

 /**
     * 通过分布查询员工以及员工所对应的部门信息
     * 分布查询第一步:查询员工信息
     */
    Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);

select:设置分布查询的sql的唯一表示(namespace.SQLID或mapper接口的全类名,方法名)

colum:设置分布查询的条件,例子是根据员工表中的did属性查询所属部门信息

 <!--Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);-->

    <resultMap id="getEmpAndDeptByStep" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
        <!--
            select:设置分布查询的sql的唯一表示(namespace.SQLID或mapper接口的全类名,方法名)
            column:设置分布查询的条件
            -->
        <association property="dept"
                     select="com.cn.cslg.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"             
                     column="did"
                                            ></association>
    </resultMap>

    <select id="getEmpAndDeptByStepOne" resultMap="getEmpAndDeptByStep">
        select * from t_emp where eid=#{eid}
    </select>
/**
     * 通过分布查询员工以及员工所对应的部门信息
     * 分布查询第二步:通过did参训员工所对应的部门
     */

    Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);
<!--Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);-->
    <select id="getEmpAndDeptByStepTwo" resultType="Dept">
        select * from t_dept where did=#{did}
    </select>

测试结果和输出

@Test
    public void testGetEmpAndDeptByStep(){
        SqlSession session = SqlSessionUtils.getSession();
        EmpMapper mapper = session.getMapper(EmpMapper.class);
        Emp empAndDeptByStepOne = mapper.getEmpAndDeptByStepOne(1);
        System.out.println(empAndDeptByStepOne);
    }
结果:
分布查询会执行两次sql语句
DEBUG 06-23 22:55:46,980 ==>  Preparing: select * from t_emp where eid=? (BaseJdbcLogger.java:137) 
DEBUG 06-23 22:55:47,018 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-23 22:55:47,050 ====>  Preparing: select * from t_dept where did=? (BaseJdbcLogger.java:137) 
DEBUG 06-23 22:55:47,051 ====> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-23 22:55:47,056 <====      Total: 1 (BaseJdbcLogger.java:137) 
DEBUG 06-23 22:55:47,057 <==      Total: 1 (BaseJdbcLogger.java:137) 
Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=Dept{did=1, deptName='A'}}

分布查询的优点:延迟加载

分布查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息;

lazyLoadingEnable:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载

aggresslveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否者,每个属性按需要加载。

此时就可以实现按需加载,获取的数据是什么,就是会执行相对应的sql语句。此时通过association和collection中国的fecthType属性设置当前的分布查询是否使用延迟加载,fetchType=“lazy(延迟加载)|eager(立即加载)”

 <!--设置MyBatis的全局配置-->
    <settings>
        <!--将_自动映射为驼峰,emp_name:empName-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--开启延迟加载-->
        <setting name="lazyLoadingEnable" value="true"/>
    </settings>
<!--Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);-->

    <resultMap id="getEmpAndDeptByStep" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
        <!--
            select:设置分布查询的sql的唯一表示(namespace.SQLID或mapper接口的全类名,方法名)
            column:设置分布查询的条件
            fetchType:当开启了全局的延迟加载之后,可通过此属性手动控制延迟加载的效果
            fetchType:"Lazy|eager":lazy表示延迟加载,eager表示立即加载
            -->
               <association property="dept"
                     select="com.cn.cslg.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
                     column="did"
                     fetchType="lazy">

        </association>
    </resultMap>

    <select id="getEmpAndDeptByStepOne" resultMap="getEmpAndDeptByStep">
        select * from t_emp where eid=#{eid}
    </select>

fetchType:当开启了全局的延迟加载之后 ,可以通过属性手动控制延迟加载的效果,前提是在mybatis核心配置文件中开启了延迟加载。

fetchType:"Lazy|eager":lazy表示延迟加载,eager表示立即加载

 <!--Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);-->
    <select id="getEmpAndDeptByStepTwo" resultType="Dept">
        select * from t_dept where did=#{did}
    </select>
@Test
    public void testGetEmpAndDeptByStep(){
        SqlSession session = SqlSessionUtils.getSession();
        EmpMapper mapper = session.getMapper(EmpMapper.class);
        Emp empAndDeptByStepOne = mapper.getEmpAndDeptByStepOne(1);
        //System.out.println(empAndDeptByStepOne);
        System.out.println(empAndDeptByStepOne.getSex());
        System.out.println(empAndDeptByStepOne.getDept());
    }

输出结果
DEBUG 06-25 21:37:58,197 ==>  Preparing: select * from t_emp where eid=? (BaseJdbcLogger.java:137) 
DEBUG 06-25 21:37:58,215 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-25 21:37:58,343 <==      Total: 1 (BaseJdbcLogger.java:137) 
男
DEBUG 06-25 21:37:58,343 ==>  Preparing: select * from t_dept where did=? (BaseJdbcLogger.java:137) 
DEBUG 06-25 21:37:58,343 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-25 21:37:58,345 <==      Total: 1 (BaseJdbcLogger.java:137) 
Dept{did=1, deptName='A'}

使用延迟加载我们会发现,我们只有用到了部门信息才会去执行查询部分信息的分布SQL语句,否则只会单单执行查询员工信息的基本语句。

 3.一对多映射

public class Dept {
    private Integer did;
    private String deptName;
    private List<Emp> emps;
}

a>collection

collection是集合标签

通过设置resultMap中的collection标签来,查询出的所有Emp类型放到list<Emp>集合中

collection:处理一对多的映射关系

ofType:表示该属性所对应的集合中存储数据的类型

  /**
     * 获取部门以及部门中所有的员工信息
     */
    Dept getDeptAndEmp(@Param("did") Integer did);

 <!--Dept getDeptAndEmp(@Param("did") Integer did);-->
    <resultMap id="getDeptAndEmpResultMap" type="Dept">
        <id property="did" column="did"></id>
        <result property="deptName" column="dept_name"></result>
        <!--
            collection:处理一对多的映射关系
            ofType:表示该属性所对应的集合中存储数据的类型
            -->
        <collection property="emps" ofType="Emp">
            <id property="eid" column="eid"></id>
            <result property="empName" column="emp_name"></result>
            <result property="age" column="age"></result>
            <result property="sex" column="sex"></result>
            <result property="email" column="email"></result>
        </collection>
    </resultMap>
    <select id="getDeptAndEmp" resultMap="getDeptAndEmpResultMap">
       select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did=#{did}
    </select>
  @Test
    public void testGetDeptAndEmp(){
        SqlSession session = SqlSessionUtils.getSession();
        DeptMapper mapper = session.getMapper(DeptMapper.class);
        Dept deptAndEmp = mapper.getDeptAndEmp(1);
        System.out.println(deptAndEmp);
    }
结果:
DEBUG 06-25 22:47:51,342 ==>  Preparing: select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did=? (BaseJdbcLogger.java:137) 
DEBUG 06-25 22:47:51,361 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-25 22:47:51,372 <==      Total: 3 (BaseJdbcLogger.java:137) 
Dept{did=1, deptName='A', emps=[Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}, Emp{eid=4, empName='男', age=45, sex='男', email='123456', dept=null}, Emp{eid=5, empName='钱七', age=456, sex='男', email='123456', dept=null}]}

b>分布查询

原理同多对一映射,将多对一映射中的association标签换为collection标签


    /**
     * 通过分布查询查询部门以及部门中所有的员工信息
     * 分布查询第一步查询部门信息
     */

    Dept getDeptAndEmpByStepOne(@Param("did") Integer did);

一对多的映射的分布查询也可以延迟加载,因为已经在核心配置文件中设置过自动开启延迟加载,所以这里用fetchType关闭这个查询延迟加载

 <!--Dept getDeptAndEmpByStepOne(@Param("did") Integer did);-->
    <resultMap id="getDeptAndEmpByStepOneResultMap" type="Dept">
        <id property="did" column="did"></id>
        <result property="deptName" column="dept_name"></result>
        <collection property="emps"
                    select="com.cn.cslg.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
                    column="did"
                    fetchType="eager"
        ></collection>
    </resultMap>
    <select id="getDeptAndEmpByStepOne" resultMap="getDeptAndEmpByStepOneResultMap">
        select * from t_dept where did = #{did};
    </select>

这是分布查询的第二步 ,返回值设置为List<Emp>和Emp结果都一样,应该是底层自动帮我们更改了

 /**
     * 通过分布查询查询部门以及部门中所有的员工信息
     * 分布查询第二步:根据did查询员工信息
     */
    List<Emp> getDeptAndEmpByStepTwo(@Param("did") Integer did);
    //Emp getDeptAndEmpByStepTwo(@Param("did") Integer did);
    <!--List<Emp> getDeptAndEmpByStepTwo(@Param("did") Integer did);-->
    <select id="getDeptAndEmpByStepTwo" resultType="Emp">
        select * from t_emp where did = #{did}
    </select>

这是关闭延迟加载输出结果

 @Test
    public void testGetDeptAndEmpByStep(){
        SqlSession session = SqlSessionUtils.getSession();
        DeptMapper mapper = session.getMapper(DeptMapper.class);
        Dept deptAndEmpByStepOne = mapper.getDeptAndEmpByStepOne(1);
        System.out.println(deptAndEmpByStepOne.getDeptName());
    }
结果
DEBUG 06-25 22:54:05,848 ==>  Preparing: select * from t_dept where did = ?; (BaseJdbcLogger.java:137) 
DEBUG 06-25 22:54:05,865 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-25 22:54:05,879 ====>  Preparing: select * from t_emp where did = ? (BaseJdbcLogger.java:137) 
DEBUG 06-25 22:54:05,879 ====> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-25 22:54:05,881 <====      Total: 3 (BaseJdbcLogger.java:137) 
DEBUG 06-25 22:54:05,881 <==      Total: 1 (BaseJdbcLogger.java:137) 
A

这是开启延迟加载输出结果,更给fetchType为lazy即可

    @Test
    public void testGetDeptAndEmpByStep(){
        SqlSession session = SqlSessionUtils.getSession();
        DeptMapper mapper = session.getMapper(DeptMapper.class);
        Dept deptAndEmpByStepOne = mapper.getDeptAndEmpByStepOne(1);
        System.out.println(deptAndEmpByStepOne.getDeptName());
    }
结果
DEBUG 06-25 22:54:59,172 ==>  Preparing: select * from t_dept where did = ?; (BaseJdbcLogger.java:137) 
DEBUG 06-25 22:54:59,191 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-25 22:54:59,243 <==      Total: 1 (BaseJdbcLogger.java:137) 
A

九.动态SQL

MyBatis框架的动态SQL技术是一种根据特定条件动态拼装的SQL语句功能,它存在的意义是为了解决凭借SQL语句字符串痛点的问题

1.if标签

  • if标签可以通过test属性的表达式进行判断,若表达式结果为true,则标签中的内容会执行拼接;反之标签中的内容不会执行
  • 需要在where后面添加一个恒成立条件例如:1=1,同时在第一个if语句前面也加上and
    • 添加恒成立的条件不会影响sql语句
    • 目的是为了防止第一个if判断失败,后面的语句直接出现and的情况
 <select id="getEmpByConditionOne" resultType="Emp">
        select <include refid="empColumns"></include> from t_emp where 1=1
        <if test="empName != null and empName !='' " >
            and emp_name=#{empName}
        </if>
        <if test=" age != null and age !='' " >
            and age=#{age}
        </if>
        <if test="sex != null and sex !='' " >
            and sex=#{sex}
        </if>
        <if test="email != null and email !='' " >
            and email=#{email}
        </if>
    </select>
  • 情况如下,为了避免出现select <include refid="empColumns"></include> from t_emp where的情况,所以需要添加一个恒成立条件
 <select id="getEmpByConditionOne" resultType="Emp">
        select <include refid="empColumns"></include> from t_emp where
        <if test="empName != null and empName !='' " >
            emp_name=#{empName}
        </if>
        <if test=" age != null and age !='' " >
            and age=#{age}
        </if>
        <if test="sex != null and sex !='' " >
            and sex=#{sex}
        </if>
        <if test="email != null and email !='' " >
            and email=#{email}
        </if>
    </select>

2.where标签

where标签:

  • 当where标签中有内容时,会自动添加where关键字,并且将内容前的多余的And或Or去掉
  • 当where标签中没有内容时,此时where标签没有任何效果

  <select id="getEmpByConditionTwo" resultType="Emp">
        select * from t_emp
        <where>
            <if test="empName != null and empName !='' " >
                and emp_name=#{empName}
            </if>
            <if test=" age != null and age !='' " >
                and age=#{age}
            </if>
            <if test="sex != null and sex !='' " >
                and sex=#{sex}
            </if>
            <if test="email != null and email !='' " >
                and email=#{email}
            </if>
        </where>
  • 注意:where标签不能将内容后面的And或Or去掉,只能把内容前的And或Or去掉,如下:这种情况下,如果条件都成立,最后一个email判断语句或多出一个And,此时idea会报Sql语言错误
 <select id="getEmpByConditionTwo" resultType="Emp">
        select * from t_emp
        <where>
            <if test="empName != null and empName !='' " >
                emp_name=#{empName} and
            </if>
            <if test=" age != null and age !='' " >
                age=#{age} and 
            </if>
            <if test="sex != null and sex !='' " >
                sex=#{sex} and 
            </if>
            <if test="email != null and email !='' " >
                email=#{email} and 
            </if>
        </where>

    </select>

3.trim标签

trim标签:

  • 若标签中有内容
    • prefix|suffix:将trim标签中内容的前面或后面添加指定内容,如and|or
    • prefixOerrides|suffixOverrides:将trim标签中的内容前面或者后面去掉指定内容
  • 若标签中没有内容
    • trim标签页没有任何效果
 <!--List<Emp> getEmpByConditionThree(Emp emp);-->
    <select id="getEmpByConditionThree" resultType="Emp">
        select * from t_emp
        <trim prefix="where" suffixOverrides="and|or" >
            <if test="empName != null and empName !='' " >
                 emp_name=#{empName} and
            </if>
            <if test=" age != null and age !='' " >
                 age=#{age} or
            </if>
            <if test="sex != null and sex !='' " >
                 sex=#{sex} and
            </if>
            <if test="email != null and email !='' " >
                 email=#{email} and
            </if>
        </trim>

    </select>
 @Test
    public void testDynamicSqlMapperThree(){
        SqlSession session = SqlSessionUtils.getSession();
        DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
        //List<Emp> empByCondition = mapper.getEmpByConditionThree(new Emp(null, "张凯" ,23, "男", "123456"));
        //System.out.println(empByCondition);
        System.out.println(mapper.getEmpByConditionThree(new Emp(null,"张凯",23,"","")));
    }

结果
DEBUG 06-27 22:54:53,308 ==>  Preparing: select * from t_emp where emp_name=? and age=? (BaseJdbcLogger.java:137) 
DEBUG 06-27 22:54:53,325 ==> Parameters: 张凯(String), 23(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-27 22:54:53,342 <==      Total: 0 (BaseJdbcLogger.java:137) 
[]

age后面的and会被自动删除

4.choose,when,otherwise标签

相当于if | else..if | else

when标签至少有一个,otherwise标签最多有一个

<!--List<Emp> getEmpByChoose(Emp emp);-->
    <select id="getEmpByChoose" resultType="Emp">
        select * from t_emp
        <where>
            <choose>
                <when test="empName != null and empName!='' ">
                    emp_name=#{empName}
                </when>
                <when test=" age != null and age !='' " >
                    age=#{age}
                </when>
                <when test="sex != null and sex !='' " >
                    sex=#{sex}
                </when>
                <when test="email != null and email !='' " >
                    email=#{email}
                </when>
                <otherwise>
                    eid=1
                </otherwise>
            </choose>
        </where>
    </select>

5.for-each标签

  • 属性:

  • collections:设置需要循环的数组或者集合

  • item:表示数组或集合中的每一个数据

  • separator:表示玄幻体之间的间隔符号

  • open:foreach标签循环的所有内容的开始符号,在循环删除数据中in()会用到

  • close:foreach标签循环的所有内容的结束符号,在循环删除数据中in()会用到

Mapper映射获取传递值分类:

  • 我们把mappe映射文件中获取传递值最终分为三种
    • 实体类:直接通过属性名访问属性
    • map集合:通过key访问
    • 其他·:直接通过@param设置的key访问
  • 批量删除

  /**
     * 通过数组实现批量删除
     */
    int deleteMoreByArray(@Param("eids") Integer[] eids);

 三种方法任选其一,一二两种是用in()删除数据,唯一的区别就是将前后()放在了open和close标签中,第三种是用or判断eid是否相等来删除

 <!--int deleteMoreByArray(@Param("eids") Integer[] eids);-->
    <delete id="deleteMoreByArray">
        delete from t_emp where eid in
        (
            <foreach collection="eids" item="eid" separator=",">
                #{eid}
            </foreach>
        )
    </delete>

    <!--int deleteMoreByArray(@Param("eids") Integer[] eids);-->
    <delete id="deleteMoreByArray">
        delete from t_emp where eid in
        (
        <foreach collection="eids" item="eid" separator="," open="(" close=")">
            #{eid}
        </foreach>
        )
    </delete>

    <!--int deleteMoreByArray(@Param("eids") Integer[] eids);-->
    <delete id="deleteMoreByArray">
        delete from t_emp where
        <foreach collection="eids" item="eid" separator="or">
            eid = #{eid}
        </foreach>

    </delete>
 /**
     * foreach实现批量删除,第一种xml方法
     */
    @Test
    public void testdeleteMoreByArray(){
        SqlSession session = SqlSessionUtils.getSession();
        DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
        System.out.println(mapper.deleteMoreByArray(new Integer[]{6,7,8}));

    }
结果
DEBUG 06-27 23:06:52,561 ==>  Preparing: delete from t_emp where eid in ( ? , ? , ? ) (BaseJdbcLogger.java:137) 
DEBUG 06-27 23:06:52,585 ==> Parameters: 6(Integer), 7(Integer), 8(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-27 23:06:52,586 <==    Updates: 0 (BaseJdbcLogger.java:137) 
0
  •  批量添加

    /**
     * 通过list集合实现批量添加
     */
    int insertMoreByList(@Param("emps") List<Emp> emps);
    <!--int insertMoreByList(@Param("emps") List<Emp> emps);-->
    <insert id="insertMoreByList">
        insert into t_emp values
        <foreach collection="emps" separator="," item="emp">
            (null ,#{emp.empName},#{emp.age},#{emp.sex},#{emp.email},null )
        </foreach>

    </insert>
    /**
     * foreach实现批量添加
     */
    @Test
    public void testinsertMoreByList(){
        SqlSession session = SqlSessionUtils.getSession();
        DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
        Emp emp = new Emp(null,"z",23,"男","1123");
        Emp emp1 = new Emp(null,"z",23,"男","1123");
        Emp emp2 = new Emp(null,"z",23,"男","1123");
        Emp emp3 = new Emp(null,"z",23,"男","1123");
        List<Emp> list = new ArrayList<>();
        list = Arrays.asList(emp,emp1,emp2,emp3);
        int i = mapper.insertMoreByList(list);
        System.out.println(i);
    }
结果
DEBUG 06-27 23:17:28,149 ==>  Preparing: insert into t_emp values (null ,?,?,?,?,null ) , (null ,?,?,?,?,null ) , (null ,?,?,?,?,null ) , (null ,?,?,?,?,null ) (BaseJdbcLogger.java:137) 
DEBUG 06-27 23:17:28,168 ==> Parameters: z(String), 23(Integer), 男(String), 1123(String), z(String), 23(Integer), 男(String), 1123(String), z(String), 23(Integer), 男(String), 1123(String), z(String), 23(Integer), 男(String), 1123(String) (BaseJdbcLogger.java:137) 
DEBUG 06-27 23:17:28,203 <==    Updates: 4 (BaseJdbcLogger.java:137) 
4

6.SQL片段


设置sql片段

<sql id="empColumns">eid,emp_name,age,sex,email</sql>

应用sql片段

<include refid="empColumns"></include> 

 <sql id="empColumns">eid,emp_name,age,sex,email</sql>
    <!--List<Emp> getEmpByConditionOne(Emp emp);-->
    <select id="getEmpByConditionOne" resultType="Emp">
        select <include refid="empColumns"></include> from t_emp where 1=1
        <if test="empName != null and empName !='' " >
            and emp_name=#{empName}
        </if>
        <if test=" age != null and age !='' " >
            and age=#{age}
        </if>
        <if test="sex != null and sex !='' " >
            and sex=#{sex}
        </if>
        <if test="email != null and email !='' " >
            and email=#{email}
        </if>
    </select>

十.MyBatis的缓存

1.MyBatis的一级缓存

一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下一次查询相同的数据,就会直接到缓存直接获取,不会从数据库重新访问

使一级缓存失效的四种结果

  1. 不同的SqlSession对应不同的一级缓存
  2. 同一个SqlSession不同的查询条件
  3. 同一个SqlSession两次查询期间执行了一次增删改插入操作,缓存影响的查询速度而不是结果
  4. 同一个SqlSession两次查询期间手动清空了缓存
  /**
     * MyBatis的一级缓存是自动开启的,作用域比较小,是同一个SqlSession范围内
     */
    @Test
    public void testCache(){
        SqlSession session = SqlSessionUtils.getSession();
        CacheMapper mapper = session.getMapper(CacheMapper.class);
        Emp empByEid = mapper.getEmpByEid(1);
        System.out.println(empByEid);
        System.out.println(mapper.getEmpByEid(1));

        System.out.println("\n一级缓存范围为同一个sqlSession,不会再一次执行sql语句,会到缓存中直接查找");
        CacheMapper mapper1 = session.getMapper(CacheMapper.class);
        System.out.println(mapper1.getEmpByEid(1));

        System.out.println("\n一级缓存范围为同一个sqlSession,如果新建一个sqlSession一级缓存将没有效果");
        SqlSession session1 = SqlSessionUtils.getSession();
        CacheMapper mapper2 = session1.getMapper(CacheMapper.class);
        System.out.println(mapper2.getEmpByEid(1));
    }

    结果:
    DEBUG 06-28 20:57:04,244 ==>  Preparing: select * from t_emp where eid = ?                 (BaseJdbcLogger.java:137) 
    DEBUG 06-28 20:57:04,263 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
    DEBUG 06-28 20:57:04,280 <==      Total: 1 (BaseJdbcLogger.java:137) 
    Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}
    Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}

    一级缓存范围为同一个sqlSession,不会再一次执行sql语句,回到缓存中直接查找
    Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}

    一级缓存范围为同一个sqlSession,如果新建一个sqlSession一级缓存将没有效果
    DEBUG 06-28 20:57:04,331 ==>  Preparing: select * from t_emp where eid = ?     (BaseJdbcLogger.java:137) 
    DEBUG 06-28 20:57:04,331 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
    DEBUG 06-28 20:57:04,333 <==      Total: 1 (BaseJdbcLogger.java:137) 
    Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}

Process finished with exit code 0
/**
     * 同一个SqlSession两次查询期间执行了一次增删改插入操作,缓存影响的查询速度而不是结果
     */
    @Test
    public void testCacheTwo(){
        SqlSession session = SqlSessionUtils.getSession();
        CacheMapper mapper = session.getMapper(CacheMapper.class);
        System.out.println(mapper.getEmpByEid(1));
        mapper.insertEmp(new Emp(null,"zk",18,"z","zz",null));
        System.out.println(mapper.getEmpByEid(1));
    }
    结果
    DEBUG 06-28 21:27:38,118 ==>  Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137) 
    DEBUG 06-28 21:27:38,141 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
    DEBUG 06-28 21:27:38,158 <==      Total: 1 (BaseJdbcLogger.java:137) 
    Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}
    DEBUG 06-28 21:27:38,160 ==>  Preparing: insert into t_emp values (null ,?,?,?,?,null ) (BaseJdbcLogger.java:137) 
    DEBUG 06-28 21:27:38,161 ==> Parameters: zk(String), 18(Integer), z(String), zz(String) (BaseJdbcLogger.java:137) 
    DEBUG 06-28 21:27:38,209 <==    Updates: 1 (BaseJdbcLogger.java:137) 
    DEBUG 06-28 21:27:38,210 ==>  Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137) 
    DEBUG 06-28 21:27:38,210 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
    DEBUG 06-28 21:27:38,211 <==      Total: 1 (BaseJdbcLogger.java:137) 
    Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=nu

  /**
     * MyBatis的一级缓存是自动开启的,作用域比较小,是同一个SqlSession范围内
     */
    @Test
    public void testCache(){
        SqlSession session1 = SqlSessionUtils.getSession();
        CacheMapper mapper2 = session1.getMapper(CacheMapper.class);
        System.out.println(mapper2.getEmpByEid(1));

        System.out.println("\n清空缓存");
        session1.clearCache();
        System.out.println(mapper2.getEmpByEid(1));
    }

    结果

    DEBUG 06-28 21:35:49,526 ==>  Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137) 
    DEBUG 06-28 21:35:49,526 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
    DEBUG 06-28 21:35:49,527 <==      Total: 1 (BaseJdbcLogger.java:137) 
    Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}

    清空缓存
    DEBUG 06-28 21:35:49,528 ==>  Preparing: select * from t_emp where eid = ?     (BaseJdbcLogger.java:137) 
    DEBUG 06-28 21:35:49,528 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
    DEBUG 06-28 21:35:49,529 <==      Total: 1 (BaseJdbcLogger.java:137) 
    Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}

2.MyBatis的二级缓存

二级换成是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被换成;此后若再次执行相同的查询语句,结果就会从缓存中获取,不会从数据库重新访问。

二级缓存开启条件:

  1. 在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置。
  2. 在映射文件文件中设置标签<cache><cache/>
  3. 二级换成必须啊在SqlSession关闭或提交之后有效
  4. 查询的数据所转换的实体类必须实现序列化接口

使二级缓存失效的情况:

两次查询之间执行了任意的增删改,会使一级和二级缓存失效

public class Emp implements Serializable 
    <cache></cache>
    <!--Emp getEmpByEid(@Param("eid")Integer eid);-->
    <select id="getEmpByEid" resultType="Emp">
        select * from t_emp where eid = #{eid}
    </select>
    /**
     * 二级缓存
     */
    @Test
    public void testTwoCache(){
        InputStream resourceAsStream=null;
        try {
            resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");

        } catch (IOException e) {
            e.printStackTrace();
        }
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
        SqlSession sqlSession = build.openSession(true);
        SqlSession sqlSession1 = build.openSession(true);
        System.out.println(sqlSession.getMapper(CacheMapper.class).getEmpByEid(1));
        sqlSession.close();
        System.out.println(sqlSession1.getMapper(CacheMapper.class).getEmpByEid(1));
        sqlSession1.close();

    }
输出结果:
    DEBUG 06-28 22:50:49,976 Cache Hit Ratio [com.cn.cslg.mybatis.mapper.CacheMapper]: 0.0 (LoggingCache.java:60) 
    DEBUG 06-28 22:50:50,172 ==>  Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137) 
    DEBUG 06-28 22:50:50,202 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
    DEBUG 06-28 22:50:50,216 <==      Total: 1 (BaseJdbcLogger.java:137) 
    Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}
    WARN  06-28 22:50:50,221 As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66 (SerialFilterChecker.java:46) 
    DEBUG 06-28 22:50:50,224 Cache Hit Ratio [com.cn.cslg.mybatis.mapper.CacheMapper]: 0.5 (LoggingCache.java:60) 
    Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}

3.二级缓存的相关配置

在mapper配置文件中添加的cache标签可以设置一些属性:

  • eviction属性:缓存回收策略

        LRU(Least Recently Used)--最近最少使用方法:移除最长时间不被使用的对象

        FIFO(First in First Out)--先进先出方法:按对象进入缓存的顺序来一处他们;

        SOFT--软引用方法:移除基于垃圾回收器状态的和软引用规则的对象;

        WEAK--弱引用:更积极地一处基于垃圾回收期状态和弱引用规则地对象;

        默认地是LRU;

  • fushInterval属性:刷新间隔,单位毫秒

        默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新

  • size属性:引用数目,正整数

        代表缓存最多可以存储多少个对象,太大容易导致内存溢出

  • readOnly属性:只读,true|false

        true:只读缓存:会给所有调用者返回缓存对象地相同实例。因此这些对象不能被修改。者提供了很重要地性能优势。

        false:读写缓存;会返回缓存对象地拷贝(通过序列化)。这回慢一些,但是安全因此默认是false

4.Mybatis缓存查询的顺序 

查询是从大到小,即二级缓存到一级缓存;存储是从小到大,SqlSession关闭后将写入二级缓存。

  • 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
  • 如果二级缓存没有命中,再查询一级缓存
  • 如果一级缓存也没有命中,则查询数据库
  • SqlSession关闭之后,一级缓存中的数据会写入二级缓存

5.整合第三方缓存EHCache

a>添加依赖

<!-- Mybatis EHCache整合包 -->
<dependency>
	<groupId>org.mybatis.caches</groupId>
	<artifactId>mybatis-ehcache</artifactId>
	<version>1.2.1</version>
</dependency>
<!-- slf4j日志门面的一个具体实现 -->
<dependency>
	<groupId>ch.qos.logback</groupId>
	<artifactId>logback-classic</artifactId>
	<version>1.2.3</version>
</dependency>

b>各个jar包的功能

jar包名称作用
mybatis-ehcacheMyBatis和EHCache的整合包
ehcacheEHCache核心包
slf4j-apiSLF4J日子门面包
logback-classic支持SLF4J门面接口的一个具体实现

c>创建EHCache的配置文件ehcahe.xml

名字必须为ehcahe.xml

<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!-- 磁盘保存路径,可以自己更改 -->
    <diskStore path="D:\ehcache\mybatis"/>
    <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="true"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

d>设置二级缓存的类型

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

e>加入logback日志

存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4H的具体实现logback来打印日志。

创建logback的配置文件logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 指定日志输出的位置 -->
    <appender name="STDOUT"
              class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 日志输出的格式 -->
            <!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
        </encoder>
    </appender>
    <!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
    <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
    <root level="DEBUG">
        <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
        <appender-ref ref="STDOUT" />
    </root>
    <!-- 根据特殊需求指定局部日志级别 -->
    <logger name="com.cn.cslg.mybatis.mapper" level="DEBUG"/>
</configuration>

f>EHCache配置文件说明

属性名是否必须作用
maxElementsInMemory在内存中缓存的element的最大数目
maxElementOnDisk在硬盘上魂村的element的最大数目,若是0表示无穷大
eternal

设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为falsename还要根据time ToldIeSeconds、timeToLiveSeconds判断

overflowToDisk设定放内存缓存溢出的时候是否将过期的element缓存到磁盘上
timeToIdleSeconds当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便回删除,默认值是0,也就是可闲置时间无限大
timeToLiveSeconds缓存element的有效生命期,默认是0,也就是element存货时间无穷大
diskSpoolBufferSizeMBDiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
diskPersistent在VM重启的时候是否启用磁盘保存的EhCache中的数据,默认是false
diskExpiryThreadIntervalSeconds磁盘缓存的清理线程运行间隔,默认是120秒。每个102s,相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy当内存缓存达到最大,有新的element加入的时候,移除缓存中element的策略。默认是LRU(最近最少使用方法),可选的有LFU(最不常使用方法)和FIFO(先进先出方法)

十一.MyBatis的逆向工程

  • 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的
  • 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
    • Java实体类
    • Mapper接口(生成的实体类名+Mapper)
    • Mapper映射文件(生成的实体类名+Mapper)

1.创建逆向工程的步骤(MyBatis逆向工程简洁版)

1.添加依赖和插件

plugin中的mysql驱动需要和外面的dependency驱动版本一样

<?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">
    <parent>
        <artifactId>Mybatis-Maven-Parent</artifactId>
        <groupId>cn.cslg.zk.mybatis</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>Mybatis-Maven-module-04</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <!-- Mybatis核心 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
        </dependency>
        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- log4j日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>

    <!-- 控制Maven在构建过程中相关配置 -->
    <build>
        <!-- 构建过程中用到的插件 -->
        <plugins>
            <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.0</version>
                <!-- 插件的依赖 -->
                <dependencies>
                    <!-- 逆向工程的核心依赖 -->
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.2</version>
                    </dependency>
                    <!-- 数据库连接池 -->
                    <dependency>
                        <groupId>com.mchange</groupId>
                        <artifactId>c3p0</artifactId>
                        <version>0.9.2</version>
                    </dependency>
                    <!-- MySQL驱动 -->
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>5.1.3</version>

                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
</project>

2.创建MyBatis核心配置文件

<?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"></properties>

    <typeAliases>
        <package name=""/>
    </typeAliases>
    <!--设置连接数据库的环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <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>
        <package name=""/>
    </mappers>
</configuration>

3.创建逆向工程的配置文件

文件名必须为:generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!--
    targetRuntime: 执行生成的逆向工程的版本
    MyBatis3Simple: 生成基本的CRUD(清新简洁版)
    MyBatis3: 生成带条件的CRUD(奢华尊享版)
    -->
    <context id="DB2Tables" targetRuntime="MyBatis3Simple">
        <!-- 数据库的连接信息 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mybatis"
                        userId="root"
                        password="123456">
        </jdbcConnection>
        <!-- javaBean的生成策略-->
        <javaModelGenerator targetPackage="com.cn.cslg.mybatis.pojo" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="com.cn.cslg.mybatis.mapper"
                         targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
        <!-- Mapper接口的生成策略 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.cn.cslg.mybatis.mapper" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
        <!-- domainObjectName属性指定生成出来的实体类的类名 -->
        <table tableName="t_emp" domainObjectName="Emp"/>
        <table tableName="t_dept" domainObjectName="Dept"/>
    </context>
</generatorConfiguration>

4.执行MBG插件的generate目标

2.QBC查询(MyBatis逆向工程奢华尊享版)

创建过程和上面的简洁版的步骤一模一样,唯一的区别需要,唯一的区别就是将generatorConfig.xml配置文件里面的context标签的targetRuntime属性从MyBatis3Simple改为MyBatis3

比起简洁版本接口中的方法更多更具体

数据查询

通过条件查询数据的时候,先调用createCriteria方法然后通过链式调用and方法,如果需要添加or条件,通过方法.or()然后再链式调用你需要的or方法

  • selectByExample:按条件查询,需要传入一个逆向工程创建出来的Example对象或者null,如果为null则会查询所有数据
  • empExample.createCriteria().xxx:创建条件对象,通过and方法添加条件
  • empExample.or().xxx:将值钱的条件通过or拼接起来
ublic void testMBG() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
        SqlSession sqlSession = build.openSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        //查询所有数据
        List<Emp> list = mapper.selectByExample(null);
        list.forEach(emp -> System.out.println(emp));

        //根据条件查询数据
        System.out.println("根据条件查询数据\n\n");
        EmpExample empExample =new EmpExample();
        empExample.createCriteria().andEmpNameEqualTo("张三").andAgeGreaterThan(20);
        //通过or方法将and条件链接起来
        empExample.or().andDidIsNotNull();
        List<Emp> list1 = mapper.selectByExample(empExample);
        list1.forEach(emp -> System.out.println(emp));
}

结果:
DEBUG 06-30 16:41:12,520 ==>  Preparing: select eid, emp_name, age, sex, email, did from t_emp (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:41:12,537 ==> Parameters:  (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:41:12,553 <==      Total: 14 (BaseJdbcLogger.java:137) 
查询所有数据
Emp{eid=1, empName='admin', age=12, sex='null', email='123', did=1}
Emp{eid=2, empName='李四', age=4, sex='男', email='123456', did=2}
Emp{eid=3, empName='王五', age=456, sex='男', email='123456', did=3}
Emp{eid=4, empName='男', age=45, sex='男', email='123456', did=1}
Emp{eid=5, empName='钱七', age=456, sex='男', email='123456', did=1}
Emp{eid=11, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=12, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=13, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=14, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=15, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=16, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=17, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=18, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=19, empName='zk', age=18, sex='z', email='zz', did=null}


根据条件查询数据
DEBUG 06-30 16:41:12,562 ==>  Preparing: select eid, emp_name, age, sex, email, did from t_emp WHERE ( emp_name = ? and age > ? ) or( did is not null ) (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:41:12,563 ==> Parameters: 张三(String), 20(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:41:12,565 <==      Total: 5 (BaseJdbcLogger.java:137) 
Emp{eid=1, empName='admin', age=12, sex='null', email='123', did=1}
Emp{eid=2, empName='李四', age=4, sex='男', email='123456', did=2}
Emp{eid=3, empName='王五', age=456, sex='男', email='123456', did=3}
Emp{eid=4, empName='男', age=45, sex='男', email='123456', did=1}
Emp{eid=5, empName='钱七', age=456, sex='男', email='123456', did=1}

数据修改

  • updateByPrimaryKey:通过主键进行直接修改,如果传入属性为null,数据库中的数据也会被设置为null
  • updateByprimaryKeySelective:通过主键进行选择性修改,如果字段为null则保持原样不修改
 @Test
    public void testMBG() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
        SqlSession sqlSession = build.openSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);

        System.out.println("\n\n直接修改前");
        System.out.println(mapper.selectByPrimaryKey(1));
        //update直接修改
        mapper.updateByPrimaryKey(new Emp(1,"admin",12,null,"123",1));
        System.out.println("\n\n直接修改后");
        System.out.println(mapper.selectByPrimaryKey(1));

        System.out.println("\n\n选择性修改前");
        System.out.println(mapper.selectByPrimaryKey(2));
        //update选择性修改,字段若为null则保持原样不修改
        mapper.updateByPrimaryKeySelective(new Emp(2,"admin",12,null,"123",1));
        System.out.println("\n\n选择性修改后");
        System.out.println(mapper.selectByPrimaryKey(2));

    }

结果:
直接修改前
DEBUG 06-30 16:58:35,714 ==>  Preparing: select eid, emp_name, age, sex, email, did from t_emp where eid = ? (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:58:35,734 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:58:35,747 <==      Total: 1 (BaseJdbcLogger.java:137) 
Emp{eid=1, empName='admin', age=12, sex='null', email='123', did=1}
DEBUG 06-30 16:58:35,748 ==>  Preparing: update t_emp set emp_name = ?, age = ?, sex = ?, email = ?, did = ? where eid = ? (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:58:35,748 ==> Parameters: admin(String), 12(Integer), null, 123(String), 1(Integer), 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:58:35,749 <==    Updates: 1 (BaseJdbcLogger.java:137) 


直接修改后
DEBUG 06-30 16:58:35,749 ==>  Preparing: select eid, emp_name, age, sex, email, did from t_emp where eid = ? (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:58:35,750 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:58:35,751 <==      Total: 1 (BaseJdbcLogger.java:137) 
Emp{eid=1, empName='admin', age=12, sex='null', email='123', did=1}


选择性修改前
DEBUG 06-30 16:58:35,751 ==>  Preparing: select eid, emp_name, age, sex, email, did from t_emp where eid = ? (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:58:35,751 ==> Parameters: 2(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:58:35,752 <==      Total: 1 (BaseJdbcLogger.java:137) 
Emp{eid=2, empName='admin', age=12, sex='男', email='123', did=1}
DEBUG 06-30 16:58:35,777 ==>  Preparing: update t_emp SET emp_name = ?, age = ?, email = ?, did = ? where eid = ? (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:58:35,778 ==> Parameters: admin(String), 12(Integer), 123(String), 1(Integer), 2(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:58:35,779 <==    Updates: 1 (BaseJdbcLogger.java:137) 


选择性修改后
DEBUG 06-30 16:58:35,779 ==>  Preparing: select eid, emp_name, age, sex, email, did from t_emp where eid = ? (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:58:35,780 ==> Parameters: 2(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-30 16:58:35,781 <==      Total: 1 (BaseJdbcLogger.java:137) 
Emp{eid=2, empName='admin', age=12, sex='男', email='123', did=1}

Process finished with exit code 0

十二.分页插件

1.分页插件使用步骤

a>添加依赖

<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper</artifactId>
	<version>5.2.0</version>
</dependency>

b>配置分页插件

在MyBatis的核心配置文件中配置插件

    <plugins>
        <!--设置分页插件-->
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>

2.分页插件的使用

使用MyBatis的分页插件功能实现分页功能

1.需要在查询功能之前开启分页

PageHelper.startPage(int pageNum,int pageSize)

2.在查询功能之后获取分页相关信息

PageInfo<Emp> pageInfo = new PageInfo<>(list,5)

list表示当前数据

5表示当前导航分页的数量

方法一:直接输出pageHelper.startPage的值

 @Test
    public void testPageHepler() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
        SqlSession sqlSession = build.openSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Page<Object> objects = PageHelper.startPage(2, 4);
        //PageHelper.startPage(5, 4);
        List<Emp> list = mapper.selectByExample(null);
        //navigagatePages 导航页码展示数
        //PageInfo<Emp> pageInfo = new PageInfo<>(list,5);
        //list.forEach(emp -> System.out.println(emp));
        System.out.println(objects);
        //System.out.println(pageInfo.getNavigatepageNums());

    }
结果
DEBUG 06-30 20:07:40,792 Cache Hit Ratio [SQL_CACHE]: 0.0 (LoggingCache.java:60) 
DEBUG 06-30 20:07:40,830 ==>  Preparing: SELECT count(0) FROM t_emp (BaseJdbcLogger.java:137) 
DEBUG 06-30 20:07:40,845 ==> Parameters:  (BaseJdbcLogger.java:137) 
DEBUG 06-30 20:07:40,855 <==      Total: 1 (BaseJdbcLogger.java:137) 
DEBUG 06-30 20:07:40,857 ==>  Preparing: select eid, emp_name, age, sex, email, did from t_emp LIMIT ?, ? (BaseJdbcLogger.java:137) 
DEBUG 06-30 20:07:40,857 ==> Parameters: 4(Long), 4(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-30 20:07:40,859 <==      Total: 4 (BaseJdbcLogger.java:137) 
Page{count=true, pageNum=2, pageSize=4, startRow=4, endRow=8, total=44, pages=11, reasonable=false, pageSizeZero=false}[Emp{eid=5, empName='钱七', age=456, sex='男', email='123456', did=1}, Emp{eid=11, empName='z', age=23, sex='男', email='1123', did=null}, Emp{eid=12, empName='z', age=23, sex='男', email='1123', did=null}, Emp{eid=13, empName='z', age=23, sex='男', email='1123', did=null}]

方法二:使用pageinfo获取数据集合

当查询获取到list集合的时候,使用PageInfo<Emp> pageInfo = new PageInfo<>(list,5)获取分页相关数据和一些自带的数据;

  • PageInfo<Emp> pageInfo = new PageInfo<>(list,5)
  • list表示当前数据
  • 5表示当前导航分页的数量,最好为奇数
 @Test
    public void testPageHepler() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
        SqlSession sqlSession = build.openSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        //Page<Object> objects = PageHelper.startPage(2, 4);
        PageHelper.startPage(5, 4);
        List<Emp> list = mapper.selectByExample(null);
        //navigagatePages 导航页码展示数
        PageInfo<Emp> pageInfo = new PageInfo<>(list,5);
        //list.forEach(emp -> System.out.println(emp));
        //System.out.println(objects);
        System.out.println(pageInfo);
    }
结果
DEBUG 06-30 20:10:15,469 Cache Hit Ratio [SQL_CACHE]: 0.0 (LoggingCache.java:60) 
DEBUG 06-30 20:10:15,508 ==>  Preparing: SELECT count(0) FROM t_emp (BaseJdbcLogger.java:137) 
DEBUG 06-30 20:10:15,522 ==> Parameters:  (BaseJdbcLogger.java:137) 
DEBUG 06-30 20:10:15,531 <==      Total: 1 (BaseJdbcLogger.java:137) 
DEBUG 06-30 20:10:15,533 ==>  Preparing: select eid, emp_name, age, sex, email, did from t_emp LIMIT ?, ? (BaseJdbcLogger.java:137) 
DEBUG 06-30 20:10:15,533 ==> Parameters: 16(Long), 4(Integer) (BaseJdbcLogger.java:137) 
DEBUG 06-30 20:10:15,535 <==      Total: 4 (BaseJdbcLogger.java:137) 

list中打印的都是你分页所查询到的数据 ,等同于上面的直接输出pageHelper

PageInfo{pageNum=5, pageSize=4, size=4, startRow=17, endRow=20, total=44, pages=11, 
list=Page{count=true, pageNum=5, pageSize=4, startRow=16, endRow=20, total=44, pages=11, reasonable=false, pageSizeZero=false}[Emp{eid=22, empName='null', age=null, sex='null', email='null', did=null}, Emp{eid=23, empName='null', age=null, sex='null', email='null', did=null}, Emp{eid=24, empName='null', age=null, sex='null', email='null', did=null}, Emp{eid=25, empName='null', age=null, sex='null', email='null', did=null}], 
prePage=4, nextPage=6, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=5, navigateFirstPage=3, navigateLastPage=7, navigatepageNums=[3, 4, 5, 6, 7]}

常用数据:

pageNum:当前页的页码

pageSize:每页显示的数据

size:当前页显示的真是条数(可能条数不够一页)

total:总记录数

pages:总分页数

prePage:上一页的页码

nextPage:下一页的页码

isFirstPage/isLastPage:是否为第一页/是否为最后一页

hasPreviousPage/hasNextPage:是否存在上一页/下一页

navigatePages:导航分页的页码数

navigatepageNums:导航分页的页码,[1,2,3,4,5]

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis是一个开源的持久化框架,可以帮助我们将数据从数据库中读取出来,然后转换为Java对象,并将Java对象写入数据库中。 在C#中使用MyBatis,需要先安装MyBatis.Net库,然后在项目中引用该库。接着,我们需要创建一个配置文件,用于配置MyBatis的数据库连接信息、SQL语句等。在配置文件中,我们需要指定一个别名,用于在程序中引用这个配置文件。 接下来,我们需要创建一个映射文件,用于将数据库中的数据映射为Java对象。在映射文件中,我们需要定义一个 resultMap,用于定义Java对象与数据库表之间的关系。我们还需要定义一个 SQL 语句,用于从数据库中读取数据,并将其转换为Java对象。 在程序中,我们需要创建一个 SqlSession 对象,用于执行SQL语句。我们可以通过SqlSession对象调用selectOne、selectList、update、delete等方法,来执行SQL语句,并将结果转换为Java对象或者操作数据库。 下面是一个简单的示例,展示了如何在C#中使用MyBatis: 1. 安装MyBatis.Net库 在Visual Studio中,选择“工具”-“NuGet包管理器”-“程序包管理器控制台”,然后输入以下命令: ``` Install-Package MyBatisNet ``` 2. 创建配置文件 在项目中创建一个名为“SqlMapConfig.xml”的文件,用于配置数据库连接信息、SQL语句等。以下是一个示例配置文件: ``` xml <?xml version="1.0" encoding="utf-8" ?> <sqlMapConfig> <database> <provider name="SqlServer" connectionString="Data Source=localhost;Initial Catalog=mydatabase;User ID=myuser;Password=mypassword;" /> </database> <sqlMap> <map resource="MyMapper.xml"/> </sqlMap> </sqlMapConfig> ``` 其中,provider元素用于指定数据库类型和连接字符串,map元素用于指定映射文件路径。 3. 创建映射文件 在项目中创建一个名为“MyMapper.xml”的文件,用于将数据库中的数据映射为Java对象。以下是一个示例映射文件: ``` xml <?xml version="1.0" encoding="utf-8" ?> <sqlMap namespace="MyMapper"> <resultMap id="MyResultMap" class="MyClass"> <result property="id" column="id"/> <result property="name" column="name"/> </resultMap> <select id="selectById" resultMap="MyResultMap"> SELECT * FROM mytable WHERE id=#id# </select> </sqlMap> ``` 其中,resultMap元素用于定义Java对象与数据库表之间的关系,select元素用于定义SQL语句。 4. 在程序中使用MyBatis 在程序中,我们需要创建一个 SqlSession 对象,用于执行SQL语句。以下是一个示例代码: ``` csharp using IBatisNet.DataMapper; using IBatisNet.DataMapper.Configuration; using IBatisNet.DataMapper.Configuration.Files; // 创建配置文件 DomSqlMapBuilder builder = new DomSqlMapBuilder(); ISqlMapper sqlMapper = builder.Configure(@"SqlMapConfig.xml"); // 执行SQL语句 MyClass obj = sqlMapper.QueryForObject<MyClass>("MyMapper.selectById", new { id = 1 }); ``` 以上是一个简单的示例,展示了如何在C#中使用MyBatis。实际上,MyBatis还有很多其他的用法和功能,需要我们在实际开发中去探索和使用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值