尚硅谷ssm整合学习之mybatis部分

SSM学习

一、mybatis

1、mybatis历史

mybatis源自于Apache的一个开源项目ibatis。ibatis是一个基于Java的持久层框架,ibatis提供的持久层框架(将数据存储在关系型数据库中),ibatis提供的持久层框架包括sql maps(数据库中的记录与Java中的实体类对象相映射)和data access objects(dao)

2、mybatis特性

  1. mybatis支持定制化SQL、存储过程以及高级映射的优秀持久层框架

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

  3. mybatis可以使用简单的xml或注解用于配置和原始映射,将接口和Java的pojo(普通的Java对象)映射成为数据库中的记录

  4. mybatis是一个半自动的orm(对象关系映射)框架

3、常见持久层技术的对比

  1. jdbc
    • sql夹杂在Java代码中耦合度高,导致硬编码内伤,数据在源代码中写死
    • 维护不易且实际开发需求中SQL有变化,频繁修改的情况多见
    • 代码冗长,开发效率低
  2. hibernate(全自动)和jpa
    • 操作简便,开发效率高
    • 程序中的长难复杂SQL需要绕过框架
    • 内部自动生产的SQL,不易做特殊化
    • 基于全映射的全自动框架,大量字段的pojo进行部分映射时比较困难
    • 反射操作太多,导致数据库性能下降
  3. mybatis
    • 轻量级,性能出色
    • SQL和Java编码分开,功能边界清晰,Java代码专注业务,SQL语句专注书籍
    • 开发小路略显与hibernate,但可以接受

4、搭建mybatis

  1. 开发环境

    idea 2021.3,maven,mysql8,mybatis

    驱动类drive-class-name:mysql8使用mysql8驱动,驱动使用:com.mysql.cj.jdbc.Driver

    连接地址url:mysql8版本的url:jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC

  2. 创建maven工程

    • 打包方式:jar

      <groupId>org.atguigu.mybatis</groupId>
      <artifactId>mybatis_helloworld</artifactId>
      <version>1.0-SNAPSHOT</version>
      <packaging>jar</packaging>
      
    • 引入依赖

      <dependencies>
          <!-- Mybatis核心 -->
          <dependency>
              <groupId>org.mybatis</groupId>
              <artifactId>mybatis</artifactId>
              <version>3.5.7</version>
          </dependency>
          <!-- junit测试 -->
          <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>4.12</version>
              <scope>test</scope>
          </dependency>
          <!-- MySQL驱动 -->
          <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
              <version>8.0.17</version>
          </dependency>
          <!-- log4j日志 -->
          <dependency>
              <groupId>log4j</groupId>
              <artifactId>log4j</artifactId>
              <version>1.2.17</version>
          </dependency>
      </dependencies>
      
    • 创建mybatis的核心配置文件

      一般名为:mybatis-config.xml,这个文件名并非强制要求,在整合spring后,该配置文件可以省略,

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

      配置文件存放在src/main/resources目录下(main下的Java目录主要来存放主程序,resources目录中存放配置文件,test目录下存放的是测试程序)

      <?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"/>
          <typeAliases>
              <package name="com.atguigu.mybatis.pojo"/>
          </typeAliases>
          <!--连接数据库的环境-->
          <environments default="development">
              <environment id="test">
                  <transactionManager type="JDBC"/>
                  <dataSource type="POOLED">
                      <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                      <property name="url" value="jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8&amp;serverTimezone=UTC"/>
                      <property name="username" value="root"/>
                      <property name="password" value="123456"/>
                  </dataSource>
              </environment>
          </environments>
          <!--mybatis的映射文件-->
          <mappers>
              <mapper resource="mappers/UserMapper.xml"/>
          </mappers>
      </configuration>
      

      通过读取核心配置文件,获取到一个操作数据库的对象,通过这个对象就可以去执行SQL语句,SQL语句写在映射文件中,从而需要将映射文件与核心配置文件关联起来,通过标签配置核心配置文件,从而实现加载核心配置文件去执行对应的SQL语句

  3. 创建mapper接口(一个mapper接口对应了映射文件中的一个SQL语句)

    mybatis中的mapper接口相当于dao,但此处mapper接口仅仅是接口,并不需要实现类,实现类是通过代理模式来创建的,不需要手动创建实现类对象

    public interface UserMapper {
        /**
        添加用户信息
        */
        int insertUser();
    }
    
  4. 创建mybatis的映射文件(用于操作数据库)

    ORM(对象关系数据库)

    Java的实体类对象、关系型数据库

    • 映射文件的命名规则:表对应的实体类的类名+Mapper.XML(如UserMapper.xml),因此一个映射文件对应一个实体类,对应一张表的操作,映射文件存放于src/main/resources/mappers目录下

    • 映射文件的命名空间namespace要与mapper接口的全类名保持一致

    • 映射文件中编写的SQL的标签的id值与mapper接口中需要调用的方法的方法名保持一致

      <?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.atguigu.mybatis.mapper.UserMapper">
          <!--insertUser-->
          <insert id="insertUser">
              insert into t_user values(null,'admin','123456',23,'男','123@qq.com')
      </mapper>
      
  5. 测试实现简单插入用户信息功能

    编写测试类和测试方法

    //获取核心配置文件的字节输入流
    InputStream is=Resources.getResourcesAsStream("mybatis-config.xml");
    //获取sqlSessionFactoryBuilder对象
    SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
    //获取sqlSessionFactory对象,利用sqlSessionFactoryBuilder和核心配置文件的输入流对象一起创建
    SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(is);
    //获取SqlSession会话对象(先采用无参的构建),是MyBatis提供的用于操作数据库的对象
    SqlSession sqlSession=sqlSessionFactory.openSession(true);
    //openSession()参数为true则表示自动提交,否则需要sqlSession.commit()手动提交
    //获取SqlSession的代理实现类对象,因为UserMapper是一个接口无法直接调用其方法,而提供gerMapper方法在底层会创建UserMapper的实现类,返回的是UserMapper接口类型。代理模式创建接口的实现类,这里会重写接口中的方法,通过之前映射文件与接口的匹配实现的
    UserMapper mapper=sqlSession.getMapper(UserMapper.class);
    //该代理对象就能进行接口的具体实现,会通过映射文件对应到目标SQL语句
    int result=mapper.insertUser();
    //除了利用工厂模式代理对象实现,还可以利用sqlSession本身实现,该种方法耦合性会很高,比较少用
    //提供SQL以及其的唯一标示,找到SQL并执行,唯一标示是namespace.sqlId
    /*
    int result=sqlSession.insert("com.atguigu.mybatis.mapper.UserMapper.insertUser");
    底层还是通过mapper接口的全类名找到映射文件,通过接口的方法名找到对应的SQL语句
    */
    System.out.println("结果"+result);
    //提交事务
    //sqlSession.commit();openSession中设置参数true可以实现自动提交
    //关闭SqlSession对象
    sqlSession.close();
    
  6. 加入log4j日志功能

    log4j是apache的一个开源项目(官网http://jakarta.apache.org/log4j),引入log4j可以在项目中使用log4j,可以控制日志信息输出到控制台、文件、甚至是数据库中。可以控制每一条日志的输出格式,通过定义日志的输出级别,可以更灵活的控制日志的输出过程,方便项目的调试。

    Log4J 在 org.apache.log4j.Level 类中定义了OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL七种日志级别。

    一般使用五个日志,FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)

    从左到右打印的信息越来越详细

    • 加入依赖

      <!-- log4j日志 -->
          <dependency>
              <groupId>log4j</groupId>
              <artifactId>log4j</artifactId>
              <version>1.2.17</version>
          </dependency>
      
    • 加入log4j的配置文件

      log4j的配置文件名为log4j.xml,命名固定,存放在resources目录下

      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
      <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>
      

5、核心配置文件详解

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

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>
    <!--引入properties文件,此后就可以在当前文件中使用${key}的方式访问value-->
    <!--专门设置一个properties文件,用于配置连接数据库的信息-->
    <properties resource="jdbc.properties"/>
    <!--
        typeAliases标签用于设置访问的类的别名,在mybatis的范围中使用别名来表示具体的类型
        type:需要起别名的类型
        alias:别名
    -->
    <typeAliases>
        <!--<typeAlias type="com.atguigu.mybatis.pojo.User" alias="abc"></typeAlias>-->
        <!--可以只设置type属性值,这样需要访问的类型的别名会默认为类名(不区分大小写),如User/user-->
        <!--<typeAlias type="com.atguigu.mybatis.pojo.User"></typeAlias>-->
        <!--还可以使用package标签,通过包设置类型别名,就可以使用默认的类名来访问该包下的类,不用一个一个的单独设置-->
        <package name="com.atguigu.mybatis.pojo"/>
    </typeAliases>
    <!--连接数据库的环境-->
    <environments default="development">
        <!--environments用于配置多个连接数据库的环境,复数标签则表示其中可以包含多个单数标签,这些单数标签就表示了一个具体连接数据库的环境-->
        <!--default:设置默认使用的环境的id-->
        <environment id="development">
            <!--id属性时唯一标示,不能重复-->
            <!--transactionManager:事物管理器
            type属性:设置事物的具体管理方式,type=JDBC/MANAGED
            JDBC:表示使用JDBC原生的事务管理方式(事物可以自动提交或手动提交)
            MANAGED:事务被管理,如spring
            -->
            <transactionManager type="JDBC"/>
            <!--
                dataSource:数据源
                type属性:设置数据源的类型
                type=POOLED/UNPOOLED/JNDI
                POOLED:表示使用数据库连接池,数据库连接交给数据库连接池管理,数据连接直接由连接池管理
                UNPOOLED:表示不适用数据库连接池,每次获取数据库连接都要重新获取
                JNDI:表示使用上下文中的数据源
            -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>

        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8&amp;serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--mybatis的映射文件-->
    <mappers>
        <!--<mapper resource="mappers/UserMapper.xml"/>-->
        <!--
           以包的方式引入映射文件,必须满足两个条件
           1,mapper接口和映射文件所在的包必须一致(注意,其实Java下和resources下的包看起来目录不一样,但在最终加载后是同一个路径目录,在target中可以看到)
           2,mapper接口的名字必须和映射文件的名字保持一致
           这样就不需要每创建一个接口就引入一个配置文件,只需将配置文件创建在对应的包下
        -->
        <package name="com.atguigu.mybatis.mapper"/>
    </mappers>
</configuration>

properties文件:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username=root
jdbc.password=123456

6、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. 修改

    <!--int updateUser()-->
    <update id="updateUser">
        update t_user set username='root',password='123' where id='3'
    </update>
    
  4. 查询一个实体类对象

    <!--getUserById-->
    <select id="getUserById" resultType="com.atguigu.mybatis.pojo.User">
            select * from t_user where id = 1
     </select>
    
  5. 查询list集合

    <!--getAllUser-->
    <select id="getAllUser" resultType="com.atguigu.mybatis.pojo.User">
        select * from t_user
    </select>
    

注意:

  • ​ 查询的标签select必须设置resultType或resultMap属性,用于设置实体类和数据库表的映射关系
  • resultType:自动映射,用于属性名与表中字段一致的情况
  • resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况

7、MyBatis获取参数值的两种情况

​ mybatis获取参数的两种方式:${}和#{}。

​ 因为接口没有实现类,从而在映射文件中通过该两种方式来获取从方法中的传入的参数

  • ​ ${}的本质是字符串拼接,使用字符串拼接的方式拼接SQL,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号’'。可能会出现sql注入的情况
  • #{}使用占位符赋值的方式拼接SQL,此时为字符串类型或日期类型的字段进行赋值时不需要添加单引号。可以避免SQL注入的情况
  1. 获取单个字面量类型的参数

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

    此时可以使用${}或#{}以任意的名称在映射文件中获取参数的值,因为此处底层直接获取了实参的值,但一般保持参数名一致

    /**
     * 获取单个字面量参数
     * @param username
     * @return
     */
    User getUserByUsername(String username);
    
    <!--User getUserByUsername(String username);-->
    <select id="getUserByUsername" resultType="User">
        <!--select * from t_user where username = #{username}-->
        select * from t_user where username = '${username}'
    </select>
    
    public void TestGetUserByUsername(){
        SqlSession sqlSession= SqlSessionUtils.getSqlSession();
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        User user=userMapper.getUserByUsername("admin");
        System.out.println(user);
    }
    
  2. 获取多个字面量类型的参数(由mybatis自动创建map集合)

    若mapper接口中的方法参数为多个时

    此时mybatis会自动将这些参数放入一个map集合中,可以通过两种键-值的方式进行获取

    • 以arg0,arg1…为键,以参数为值进行获取
    • 以param1,param2…为键,以参数为值进行获取

    具体还是需要通过#{}或${}访问map集合的键来获取到具体对应的值(参数值)

    /**
     * 获取多个字面量参数
     * @param username
     * @param password
     * @return
     */
    User checkLogin(String username,String password);
    
    <!--User checkLogin(String username,String password);-->
    <!--在获取多个参数时,mybatis将多个参数放入map集合中,需要以键值对的方式进行数据存取,有两种存放参数的键1:arg0,arg1;2:param1,param2。存在一定顺序-->
    <select id="checkLogin" resultType="User">
        select * from t_user where username = #{param1} and password = #{param2}
    </select>
    
    public void TestCheckLogin(){
        SqlSession sqlSession= SqlSessionUtils.getSqlSession();
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        User user=userMapper.checkLogin("admin","123456");
        System.out.println(user);
    }
    
  3. 获取map集合类型的参数(手动创建map集合)

    若mapper接口中的方法需要的参数有多个时,此时可以手动在调用方法前创建map集合,把map集合作为参数

    在映射文件中同样是通过${}或#{}来获取值,此种情况则只需要通过访问map中的键就可以获取到对应的值

    /**
     * 通过自定义map集合方式获取参数
     * @param map
     * @return
     */
    User checkLoginByMap(Map<String,Object> map);
    
    public void TestCheckLoginByMap(){
        SqlSession sqlSession= SqlSessionUtils.getSqlSession();
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        Map<String,Object> map=new HashMap<String,Object>();
        map.put("username","admin");
        map.put("password","123456");
        User user=userMapper.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接口中的方法参数为实体类对象时,此时可以使用${}或#{}两种方法,直接通过访问实体类对象的属性名来获取到对应的属性值

    /**
     * 通过实体类型来获取参数
     * @param user
     */
    void insertUser(User user);
    
    <!--void insertUser(User user)-->
    <insert id="insertUser">
        insert into t_user values(null,#{username},#{password},#{age},#{gender},#{email})
    </insert>
    
    public void TestInsertUser(){
        SqlSession sqlSession= SqlSessionUtils.getSqlSession();
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        User user=new User(null,"root","123456",25,"男","123@qq.com");
        userMapper.insertUser(user);
    }
    
  5. 使用@Param注解标示参数

    该种情况是对手动创建map集合的改进,通过@Param注解标示mapper接口中方法的参数,让被标示的参数有一个固定的键,可以通过${}或#{}访问这个自定义的键来获取到对应的参数值,此时mybatis会将这些参数放入map集合中

    /**
    使用@Param注解方法实现查询
     */
    User checkLoginByParam(@Param("username") String username,@Param("password") String password);
    

​ 此处对第一个参数username设定了一个键为username,第二个参数设定了一个键为password

​ 此种情况又两种方式获取参数值

​ 第一种:以@Param注解的value属性值为键,以参数为值

​ 第二种:以param1,param2…为键,以参数为值

8、mybatis的各种查询功能

  1. 查询一个实体类对象

    根据输入的id查询user实体类对象

    /**
     * 根据用户id查询用户信息
     */
    User getUserById(@Param("id") Integer id);
    
    <!--User getUserById(@Param("id") Integer id);-->
    <select id="getUserById" resultType="User">
        select * from t_user where id = #{id}
    </select>
    
    public void getUserById(){
        SqlSession sqlSession= SqlSessionUtils.getSqlSession();
        SelectMapper mapper=sqlSession.getMapper(SelectMapper.class);
        User user=mapper.getUserById(1);
        System.out.println(user);
    }
    
  2. 查询一个list集合

    当查询的数据不止一条时,不能使用实体类作为返回值,否则会抛出TooManyResultException的异常(底层sqlsession会根据方法中的返回值的类型去调用底层实现对应的类型方法,当可求的数据量与实际求得的数据量不一致时,会报异常),可以将多条数据放入list集合中。若查询的数据只有一条时,既可以使用实体类作为返回值,也可以使用list集合作为返回值

    /**
     * 查询所有用户的信息
     */
    List<User> getAllUser();
    
    <!--List<User> getAllUser();-->
    <select id="getAllUser" resultType="User">
        select * from t_user
    </select>
    
    public void getAllUser(){
        SqlSession sqlSession=SqlSessionUtils.getSqlSession();
        SelectMapper mapper=sqlSession.getMapper(SelectMapper.class);
        List<User> list=mapper.getAllUser();
        list.forEach(System.out::println);
    }
    

    User{id=‘1’, username=‘admin’, password=‘123456’, age=23, gender=‘男’, email=‘123@qq.com’}
    User{id=‘2’, username=‘admin1’, password=‘123456’, age=23, gender=‘男’, email=‘123@qq.com’}
    User{id=‘4’, username=‘root’, password=‘123456’, age=25, gender=‘男’, email=‘123@qq.com’}

  3. 查询单个数据(如利用聚集函数查询某个字段的总数、平均值等)

    以id字段查询表中的记录数

    在mybatis中,对于java中的常用类型都设置的类型别名(别名不区分大小写)以在resultType中使用

    java.lang.Integer–>integer,

    int–>_int| _integer,

    Map–>map,

    String–>string

    /**
     * 根据id查询记录数
     */
    Integer getCount();
    
    <!--Integer getCount();-->
    <select id="getCount" resultType="java.lang.Integer">
        select count(id) from t_user
    </select>
    
    public void getCount(){
        SqlSession sqlSession=SqlSessionUtils.getSqlSession();
        SelectMapper mapper=sqlSession.getMapper(SelectMapper.class);
        Integer number=mapper.getCount();
        System.out.println(number);
    }
    

    使用聚集函数通过某一字段(属性)来获取数据时,若某记录该字段属性为null,则不被计数

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

    当我们查询出的数据没有一个固定的实体类类型的时候,就可以把该条数据转化为一个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>
    
    public void getUserByIdToMap(){
        SqlSession sqlSession=SqlSessionUtils.getSqlSession();
        SelectMapper mapper=sqlSession.getMapper(SelectMapper.class);
        Map<String,Object> map=mapper.getUserByIdToMap(1);
        System.out.println(map);
        //{password=123456, gender=男, id=1, age=23, email=123@qq.com, username=admin}
    }
    

    实体类中的属性是固定的,map集合没有固定的键,而查询出来的数据中有值为null的键-值则该部分的键-值变号放在查询出的map集合中

  5. 查询出多条数据放入map集合中

    查询出所有的用户信息放入一个map集合中

    1. 方式一

      由于表中的数据以map集合的方式查询时,一条数据对应一个map集合;若有多条数据就需要多个map集合来接收。该种情况可以使用list集合的方式存放多个map集合来解决,否则就会出现TooManyResultException异常。注意泛型的书写

      /**
       * 获取多条数据转化为map集合,以list集合作为解决方式
       */
      List<Map<String,Object>> getAllUserToMap();
      
      <!--List<Map<String,Object>> getAllUserToMap();-->
      <select id="getAllUserToMap" resultType="map">
          select * from t_user
      </select>
      
      public void getAllUserToMap(){
          SqlSession sqlSession=SqlSessionUtils.getSqlSession();
          SelectMapper mapper=sqlSession.getMapper(SelectMapper.class);
          List<Map<String,Object>> list=mapper.getAllUserToMap();
          System.out.println(list);
          //[{password=123456, gender=男, id=1, age=23, email=123@qq.com, username=admin}, {password=123456, gender=男, id=2, age=23, email=123@qq.com, username=admin1}]
      }
      
    2. 方式二

      使用@MapKey注解来解决

      若需要最终以一个map集合的形式返回多条数据,则可以使用@MapKey注解来设置每一条数据的键,值则是每条数据对应的map集合(可以理解为一个大map集合里嵌套了多个小的map集合)

      /**
       * 获取多条数据转化为map集合,以注解作为解决方式
       */
      @MapKey("id")
      Map<String,Object> getAllUserToMapSecond();
      
      <!--Map<String,Object> getAllUserToMapSecond()-->
      <select id="getAllUserToMapSecond" resultType="map">
          select * from t_user
      </select>
      
      public void getAllUserToMapSecond(){
          SqlSession sqlSession=SqlSessionUtils.getSqlSession();
          SelectMapper mapper=sqlSession.getMapper(SelectMapper.class);
          Map<String,Object> map=mapper.getAllUserToMapSecond();
          System.out.println(map);
          //{1={password=123456, gender=男, id=1, age=23, email=123@qq.com, username=admin},
          // 2={password=123456, gender=男, id=2, age=23, email=123@qq.com, username=admin1},
          // 4={password=123456, gender=男, id=4, age=25, email=123@qq.com, username=root},
          // 5={password=123456, gender=男, id=5, age=25, email=123@qq.com, username=root}}
      }
      

9、特殊SQL的执行

  1. 模糊查询

    查询username属性中含有’a’的记录,有3种实现方法

    • 方法一

      **使用单引号和KaTeX parse error: Expected 'EOF', got '#' at position 11: {}的组合**(因为#̲{}会被解析成?占位符,而?在…{}就可以实现字符串拼接)

      /**
       * 通过名字实现模糊查询,把查询到的数据map转化为list集合中
       */
      List<User> getUserByLikeName(@Param("LikeName") String LikeName);
      
      <select id="getUserByLikeName" resultType="User">
          select * from t_user where username like '%${LikeName}%'
          <!--select * from t_user where username like concat('%',#{LikeName},'%')-->
          <!--select * from t_user where username like "%"#{LikeName}"%"-->
      </select>
      
      public void getUserByLikeName(){
          SqlSession sqlSession= SqlSessionUtils.getSqlSession();
          SpecialSqlMapper mapper=sqlSession.getMapper(SpecialSqlMapper.class);
          List<User> list=mapper.getUserByLikeName("a");
          list.forEach(System.out::println);
      }
      
    • 方法二

      使用mysql中的concat字符串拼接函数

      <!--List<User> getUserByLikeName(String LikeName);-->
      <select id="getUserByLikeName" resultType="User">
          <!--select * from t_user where username like '%${LikeName}%'-->
          select * from t_user where username like concat('%',#{LikeName},'%')
          <!--select * from t_user where username like "%"#{LikeName}"%"-->
      </select>
      
    • 方法三

      使用双引号加#{}的组合

      <!--List<User> getUserByLikeName(String LikeName);-->
      <select id="getUserByLikeName" resultType="User">
          <!--select * from t_user where username like '%${LikeName}%'-->
          <!--select * from t_user where username like concat('%',#{LikeName},'%')-->
          select * from t_user where username like "%"#{LikeName}"%"
      </select>
      
  2. 批量删除

    该处采用字符串拼接的方式进行批量删除,该处只能使用${}的方法获取参数,#{}会自动添加单引号产生错误的SQL语句

    /**
     * 根据id拼接批量删除记录数据
     */
    int deleteMoreUser(@Param("ids") String ids);
    
    <!--int deleteMoreUser(@Param("ids") String ids);-->
    <delete id="deleteMoreUser">
        delete from t_user where id in (${ids})
    </delete>
    
    public void deleteMoreUser(){
        SqlSession sqlSession= SqlSessionUtils.getSqlSession();
        SpecialSqlMapper mapper=sqlSession.getMapper(SpecialSqlMapper.class);
        int count=mapper.deleteMoreUser("7,8");
        System.out.println(count);
    }
    
  3. 动态设置表名

    根据输入的不同表名,实现对不同表的查询,该处也只能使用${},因为获取参数为表的名字,使用#{}会自动添加单引号,sql语句中表名位置不能有单引号,会产生错误

    /**
     * 根据输入表名实现不同表的查询
     */
    List<User> getUserList(@Param("tableName") String tableName);
    
    <!--List<User> getUserList(@Param("tableName") String tableName);-->
    <select id="getUserList" resultType="User">
        select * from ${tableName}
    </select>
    
    public void getUserList(){
        SqlSession sqlSession= SqlSessionUtils.getSqlSession();
        SpecialSqlMapper mapper=sqlSession.getMapper(SpecialSqlMapper.class);
        List<User> list=mapper.getUserList("t_user");
        list.forEach(System.out::println);
    }
    
  4. 添加功能进行时获取自增的主键值

    从jdbc中衍生而来的功能,在数据库为主键设置自动递增,添加数据时未为主键赋值,有时候需要在添加完一条数据后,拿到增加后的主键值(如为某个新增的班级添加学生,新增班级得拿到班级号后,才能为学生分配班级)

    /**
     * 获取自增的主键
     */
    void insertUser(User user);
    
    <!--void insertUser(User user);-->
    <insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
        insert into t_user values(null,#{username},#{password},#{age},#{gender},#{email})
    </insert>
    
    public void insertUser(){
        SqlSession sqlSession=SqlSessionUtils.getSqlSession();
        SpecialSqlMapper mapper=sqlSession.getMapper(SpecialSqlMapper.class);
        User user=new User(null,"xiaomin","123456",20,"男","123@qq.com");
        mapper.insertUser(user);
        System.out.println(user);
    }
    

    insert标签中的useGenerateKeys属性表明“是否使用自增的主键”,默认值为false,此时我们需要获取,从而赋值true,同时还需要设置另一个属性keyProperty,因为增删改方法有统一的返回值是受影响的行数,因此只能将获取到的自增的主键放在传输的参数user对象的某个属性之中,而id又恰好是那个自增的主键

10、自定义映射之resultMap

  1. 处理表中字段名与实体类对应属性名名字不一致的映射

    若字段名和属性名不一致,但是字段名符合数据库的规则(使用了"_"),实体类中的属性符合java规则,采用驼峰式命名,此时可以采用两种方法解决二者之间的映射

    1. 可以在SQL语句中通过起别名的方式,保证字段名和实体类属性名保持一致

      <select id="getEmpByEmpId" resultType="Emp">
          select emp_id empId,emp_name empName,age empAge,gender empGender from t_emp where emp_id = #{empId}
      </select>
      
    2. 可以在mybatis的核心配置文件中设置一个全局配置信息mapUnderscoreToCamelCase(在settings标签中设置),可以在查询表中数据的时候,自动将字段名的下划线去掉并转化为驼峰式

      例如字段名user_name,在配置完mapUnderscoreToCamelCase后会被映射为userName,可以看出映射同样是由一定的规则的,不能随便映射

      <!--将下划线映射为驼峰的配置-->
      <settings>
          <setting name="mapUnderscoreToCamelCase" value="true"/>
      </settings>
      
    3. 当字段名和属性名不一致,且不想设置全局配置mapUnderscoreToCamelCase时,可以采用resultMap属性。使用resultMap可以自定义字段名和属性名的映射关系

      resultMap:自定义映射

      ​ 属性:id(表示自定义映射的唯一标示)

      ​ type(表示查询的数据要映射的实体类的类型)

      ​ 子标签:id(设置主键的映射关系)

      ​ result(设置普通字段的映射关系)

      ​ association(设置多对一的映射关系)

      ​ collection(设置一对多的映射关系)

      ​ 属性:column(设置映射关系中需要映射的表中的字段名)

      ​ property(设置映射关系中需要映射的实体类的属性名)

      /**
       * 数据库表字段名与实体类的属性名不一致
       */
      Emp getEmpByEmpId(@Param("empId") Integer empId);
      
      <resultMap id="empResultMap" type="Emp">
          <id column="emp_id" property="empId"></id>
          <result column="emp_name" property="empName"></result>
          <result column="age" property="age"></result>
          <result column="gender" property="gender"></result>
      </resultMap>
      <select id="getEmpByEmpId" resultMap="empResultMap">
          select * from t_emp where emp_id = #{empId}
      </select>
      
      public void getEmpById(){
          SqlSession sqlSession=SqlSessionUtils.getSqlSession();
          EmpMapper mapper=sqlSession.getMapper(EmpMapper.class);
          Emp emp=mapper.getEmpByEmpId(1);
          System.out.println(emp);
          //Emp{empId=1, empName='张三', empAge=20, empGender='男'}
      }
      
  2. 处理多对一的映射

    例如查询员工信息以及员工对应的部门信息使用级联方式处理映射关系

    在员工类中,增加了部门的实体类属性private Dept dept,该属性又含有自己的属性,分别是deptId和deptName因此在进行查询时不能只查一个表,需要进行多表查询,因此也不能使用resultType,而是使用resultMap

    1. 级联方式处理映射关系

      将t_emp和t_dept两个表进行连接查询

      /**
       * 级联方式处理多对一映射
       */
      Emp getEmpAndDeptByEmpId(@Param("empId") Integer empId);
      
      <resultMap id="empAndDeptResultMap" type="Emp">
          <!--type属性表示要处理的数据实体类类型-->
          <id column="emp_id" property="empId"></id>
          <result column="emp_name" property="empName"></result>
          <result column="age" property="age"></result>
          <result column="gender" property="gender"></result>
          <result column="dept_id" property="dept.deptId"></result>
          <result column="dept_name" property="dept.deptName"></result>
          <!--注意此处property属性的写法,要将查询到的dept_id和dept_name赋值给emp对象中dept属性的属性-->
      </resultMap>
      
      <select id="getEmpAndDeptByEmpId" resultMap="empAndDeptResultMap">
          select emp.*,dept.*
          from t_emp emp left join t_dept dept on emp.emp_id=dept.dept_id
          where emp_id = #{empId}
      </select>
      
    2. 使用association处理映射关系

      在resultMap标签中存在一个association标签,可以使用association标签将实体类类型的属性单独赋值

      <resultMap id="empAndDeptResultMap" type="Emp">
          <id column="emp_id" property="empId"></id>
          <result column="emp_name" property="empName"></result>
          <result column="age" property="age"></result>
          <result column="gender" property="gender"></result>
          <association property="dept" javaType="Dept">
              <!--property属性指明需要处理的映射关系中对象的实体类属性的名字-->
              <!--javaType属性用于指明需要单独处理的实体类的类型名-->
              <id column="dept_id" property="deptId"></id>
              <result column="dept_name" property="deptName"></result>
          </association>
      
    3. 使用分步查询处理

      一步一步进行查询,需要使用多个SQL语句。先查询出员工信息,得到部门号,再根据部门号查询出对应的部门信息,该种解决方法只能使用association标签来解决,需要涉及到association中的两个属性,分别是select和column

      因为需要使用多个SQL语句,最好重新另外创建一个接口来对应部门表,将关于部门的查询写入对应部门的接口中

      1. 查询员工信息

        /**
         * 分步查询处理员工和部门信息的多对一映射,第一步
         */
        Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId);
        
        <!--Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId);-->
        <resultMap id="empAndDeptByStepResultMap" type="Emp">
            <id column="emp_id" property="empId"></id>
            <result column="emp_name" property="empName"></result>
            <result column="age" property="age"></result>
            <result column="gender" property="gender"></result>
            <!--
        		select属性:设置分布查询,查询某个属性的值的关联SQL语句的唯一标示(namespace.sqlid)
        		colum属性:将SQL语句的某个查询结果传入下一个查询的方法中(参数值)
        -->
            <association property="dept"        select="com.atguigu.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo" column="dept_id" fetchType="eager">
            </association>
        </resultMap>
        <select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap">
            select * from t_emp where emp_id = #{empId}
        </select>
        
      2. 查询部门信息

        deptId则为根据association属性中column设置的查询结果传入的参数值

        /**
         * 分步查询处理员工和部门信息的多对一映射,第二步
         */
        Dept getEmpAndDeptByStepTwo(@Param("deptId") Integer deptId);
        
        <mapper namespace="com.atguigu.mybatis.mapper.DeptMapper">
            <!--Dept getEmpAndDeptByStepTwo(@Param("deptId") Integer deptId);-->
            <select id="getEmpAndDeptByStepTwo" resultType="Dept">
                select * from t_dept where dept_id = #{deptId}
            </select>
        </mapper>
        

      分布查询的优点:可以实现延迟加载(懒加载)

      (如果当前只需要员工信息,而不需要部门信息,则开启延迟加载后,则只会执行获取员工信息的SQL,不会执行查询部门信息的SQL,从而减少内存的使用)

      实现延迟加载需要在配置文件中添加全局配置信息:lazyLoadingEnabled和aggressiveLazyLoading

      <setting name="lazyLoadingEnabled" value="true"/>
      <setting name="aggressiveLazyLoading" value="false"/>
      

      lazyLoadingEnabled:延迟加载的全局开关。默认值为false,当开启后,所有分布查询的关联对象都会延迟加载

      aggressiveLazyLoading:当开启后,任何方法的调用都会加载该对象的所有属性,否则每个属性会按需加载,默认值为false(开启后,不管有没有需要获取部门的信息,员工和部门的查询SQL都会执行)

      延时加载与这两个配置均有关系

      按需加载:可以在开启aggressiveLazyLoading后,通过association和collection中的fetchType属性设置当前分布查询是否使用延迟加载,因为lazyLoadingEnabled和aggressiveLazyLoading两个配置是针对全局生效,而有时只需要个别使用延迟或按需,fetchType=“lazy”(延迟加载)|eager(立即加载)

11、动态SQL

mybatis框架的动态SQL时一种根据特定条件动态凭借SQL语句的功能,本质上是映射文件中的一系列标签的使用。在mybatis的动态拼接中有两个特殊值分别是:null和"“(空字符串)。一系列动态SQL标签的使用就是关于临界值的判断问题,而判断的临界值即为null和”"(空字符串)

  1. if标签

    if标签而通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会被执行,反之标签中的内容不会被执行

    /**
     * 用于测试if条件判断拼接sql
     * @return
     */
    List<Emp> testGetEmpByCondition(Emp emp);
    
    <select id="testGetEmpByConditionOne" resultType="Emp">
        select * from t_emp 1=1
        <if test="empName != '' and empName != null">
            and emp_name = #{empName}
        </if>
        <if test="gender != '' and gender != null">
            and gender = #{gender}
        </if>
        <if test="age != '' and age != null">
            and age = #{age}
        </if>
    </select>
    
    public void getEmpByConditionTest(){
        SqlSession sqlSession=SqlSessionUtils.getSqlSession();
        DynamicMapper mapper=sqlSession.getMapper(DynamicMapper.class);
        Emp emp=new Emp(null,"",20,"");
        List<Emp> list=mapper.testGetEmpByCondition(emp);
        list.forEach(System.out::println);
    }
    

    每一个if标签都是一个单独的判断,判断test属性内容,若符合条件则把标签内容拼接入SQL中,为了让SQL语句保持正确执行,通常在if标签前加上1=1的判断,这是一个恒成立的条件,防止当if不成立导致where单挂的语法错误,也使得原SQL语句都会执行一次

  2. where标签

    where标签一般与if标签结合在一起使用,为了防止where在SQL语句中单挂导致的SQL语法错误,加入了where标签与if标签一起使用,可以解决各种情况

    1>若where标签中的if条件满足,则where标签会自动添加where关键字,并且将条件前方用于拼接的多余的and关键字去掉

    2>若where标签中的if条件都不满足,则where标签没有任何功能,不会添加where关键字

    (where标签不会去掉条件中附在后面的and)

    <select id="testGetEmpByChoose" resultType="Emp">
        select <include refid="empColumns"></include> 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="gender != null and gender != ''">
                    gender = #{gender}
                </when>
            </choose>
        </where>
    </select>
    
  3. trim标签

    trim标签用于去掉或添加标签中的属性被赋值的内容

    常用属性:

    prefix:在trim标签中的内容的前面添加某些内容

    suffix:在trim标签中的内容的后面添加某些内容

    prefixOverriders:在trim标签中的内容的前面去掉某些内容

    suffixOverriders:在trim标签中的内容的后面去掉某些内容

    一般用prefix在前面添加where,用suffixOverriders把后面的and去掉

    <select id="testGetEmpByCondition" resultType="Emp">
        select * from t_emp
        <trim prefix="where" suffixOverrides="and">
            <if test="empName != '' and empName != null">
                emp_name = #{empName} and
            </if>
            <if test="gender != '' and gender != null">
                gender = #{gender} and
            </if>
            <if test="age != '' and age != null">
                age = #{age}
            </if>
        </trim>
    </select>
    
  4. choose,when,otherwise联合标签

    该组联合标签相当于if-else if-else,用于多条件嵌套判断拼接SQL

    <!--List<Emp> testGetEmpByChoose(Emp emp);-->
    <select id="testGetEmpByChoose" 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="gender != null and gender != ''">
                    gender = #{gender}
                </when>
            </choose>
        </where>
    </select>
    
    public void getEmpByChooseTest(){
        SqlSession sqlSession=SqlSessionUtils.getSqlSession();
        DynamicMapper mapper=sqlSession.getMapper(DynamicMapper.class);
        Emp emp=new Emp(null,"",20,"");
        List<Emp> list=mapper.testGetEmpByChoose(emp);
        list.forEach(System.out::println);
    }
    

    使用when标签,则相互之间都是一个一个往下判断进行,直到otherwise,一般when标签至少设置一个,otherwise标签最多只能设置一个

  5. foreach标签

    foreach标签用于拼接批量操作的SQL中,用foreach判断存入数据的集合或数组对象(mybatis会将传入的list集合放入map集合中,以list为键,以集合为值)

    属性:

    collection:用于填入需要获取循环处理的集合或数组名

    item:用于填入一个名字,用于表示当前需要处理的集合或数组

    seperator:用于批量添加内容,如and或逗号

    open:填入内容指循环从何内容开始

    close:填入内容指循环从何内容结束

    open和close一般分别填入左括号和右括号

    • 批量添加集合

      /**
       * 通过list集合批量添加员工信息
       */
      void insertMoreEmp(@Param("emps") List<Emp> emps);
      
      <!--void insertMoreEmp(@Param("emps") List<Emp> emps);-->
      <insert id="insertMoreEmp">
          insert into t_emp values
          <foreach collection="emps" item="emp" separator=",">
              (null,#{emp.empName},#{emp.age},#{emp.gender},null)
          </foreach>
      </insert>
      
      public void insertMoreEmpTest(){
          SqlSession sqlSession=SqlSessionUtils.getSqlSession();
          DynamicMapper mapper=sqlSession.getMapper(DynamicMapper.class);
          Emp emp1=new Emp(null,"小明1",20,"男");
          Emp emp2=new Emp(null,"小明2",20,"男");
          Emp emp3=new Emp(null,"小明3",20,"男");
          List<Emp> list= Arrays.asList(emp1,emp2,emp3);
          mapper.insertMoreEmp(list);
      }
      
    • 批量删除数组(or方式)

      /**
       * 通过存储empId的数组批量删除员工信息
       */
      void deleteMoreEmp(@Param("empIds") Integer[] empIds);
      
      <delete id="deleteMoreEmp">
          delete from t_emp where
          <foreach collection="empIds" item="empId" separator="or">
              emp_id = #{empId}
          </foreach>
      </delete>
      
      public void deleteMoreEmpTest(){
          SqlSession sqlSession=SqlSessionUtils.getSqlSession();
          DynamicMapper mapper=sqlSession.getMapper(DynamicMapper.class);
          Integer[] empIds=new Integer[]{6,7};
          mapper.deleteMoreEmp(empIds);
      }
      
    • 批量删除数组(in方式)

      <!--void deleteMoreEmp(@Param("empIds") Integer[] empIds);-->
      <delete id="deleteMoreEmp">
          delete from t_emp where
          <foreach collection="empIds" item="empId" separator="," open="(" close=")">
              #{empId}
          </foreach>
      </delete>
      
  6. SQL片段

    SQL片段,可以记录一段公共SQL片段,在使用的地方通过include标签引入

    <sql id="empColumns">
        emp_id,emp_name,age,gender,dept_id
    </sql>
    
    <select id="testGetEmpByChoose" resultType="Emp">
        select <include refid="empColumns"></include> from t_emp
        <where>
            ...
        </where>
    </select>
    

12、mybatis的缓存

主要针对查询功能,mybatis会把第一次查询出来的数据进行缓存,等到下一次做相同数据的相同SQL语句的查询时,会直接从缓存中获取对应数据,不需要再执行一次SQL

  1. mybatis的一级缓存

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

    一级缓存失效的四种情况:

    • 不同的SqlSession对应不同的一级缓存(若使用不同的SqlSession对象查询相同数据,不会从缓存中获取,要重新执行SQL)
    • 同一个SqlSession,但是查询条件不同(查询的不是同样的数据)
    • 同一个SqlSession,但是在两次查询期间执行了任何一次增删改操作(增删改操作会清空缓存,因为不确定缓存中的数据是否被修改,从而得重新查询一次)
    • 同一个SqlSession,但是两次查询期间手动清空了缓存(使用SqlSession.clear()方法)
  2. mybatis的二级缓存

    二级缓存是SqlSessionFactory级别,一个同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存,此后若执行相同的查询语句,结果就会从缓存中获取,但二级缓存的开启需要条件

    二级缓存开启的条件:

    1. 在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置
    2. 映射文件中设置标签(会在控制台中输出命中率)
    3. 二级缓存必须在SqlSession关闭或提交后才有效(二级缓存范围大,而第一次查询出的数据会被先放入一级缓存SqlSession中,而只有当SqlSession关闭或提交后,才会把数据保存到二级缓存中
    4. 查询的数据所转换的实体类类型必须实现序列化的接口

    二级缓存失效的情况:

    两次查询期间执行了任意一次增删改操作会使一二级缓存都失效

  3. 二级缓存的相关配置(了解)

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

    1. eviction属性:缓存回收策略,默认的是 LRU

      LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
      FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
      SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
      WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

    2. flushInterval属性:刷新间隔,单位毫秒(每次指定时间清空缓存)

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

    3. size属性:引用数目,正整数

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

    4. readOnly属性:只读, true/false

      true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。(设置为true时,实体类不需要序列化接口了)
      false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false

  4. mybatis缓存查询的顺序

    从大范围查询到小范围

    先查询二级缓存,因为二级缓存中可能会有其他程序已经查询出来的数据,可以直接拿来用

    如果二级缓存没有命中,再查询二级缓存

    如果一级缓存也没有命中,则查询数据库

    SqlSession关闭后,一级缓存中的数据会放入二级缓存中

  5. 整合第三方缓存EHCache(了解)

    针对mybatis的二级缓存可以使用原生的二级缓存,也可以整合第三方缓存

    1. 添加依赖

      <!--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>
      
    2. 各jar包的功能

      jar包名称作用
      mybatis-ehcacheMybatis和EHCache的整合包
      ehcacheEHCache核心包
      slf4j-apiSLF4J日志门面包
      logback-classic支持SLF4J门面接口的一个具体实现
    3. 创建EHCache的配置文件ehcache.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:\atguigu\ehcache"/>
          <defaultCache
          maxElementsInMemory="1000"
          maxElementsOnDisk="10000000"
          eternal="false"
          overflowToDisk="true"
          timeToIdleSeconds="120"
          timeToLiveSeconds="120"
          diskExpiryThreadIntervalSeconds="120"
          memoryStoreEvictionPolicy="LRU">
          </defaultCache>
      </ehcache>
      
    4. 设置二级缓存的类型(在映射文件中通过cache标签加入)

      <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
      
    5. 加入logback日志

      存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现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.atguigu.mybatis.mapper" level="DEBUG"/>
      </configuration>
      

13、mybatis的逆向工程

正向工程:先创建java实体类,由框架负责根据实体类生成数据库表。hibernate是支持正向工程的

逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成java实体类、mapper接口、mapper映射文件

  1. 步骤

    1. 添加依赖和插件

      <!--依赖mybatis核心包-->
      <dependencies>
          <dependency>
              <groupId>org.mybatis</groupId>
              <artifactId>mybatis</artifactId>
              <version>3.5.7</version>
          </dependency>
          <!-- junit测试 -->
          <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>4.12</version>
              <scope>test</scope>
          </dependency>
          <!-- log4j日志 -->
          <dependency>
              <groupId>log4j</groupId>
              <artifactId>log4j</artifactId>
              <version>1.2.17</version>
          </dependency>
          <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
              <version>8.0.17</version>
          </dependency>
          <dependency>
              <groupId>com.github.pagehelper</groupId>
              <artifactId>pagehelper</artifactId>
              <version>5.2.0</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>
                      <!-- MySQL驱动 -->
                      <dependency>
                          <groupId>mysql</groupId>
                          <artifactId>mysql-connector-java</artifactId>
                          <version>8.0.16</version>
                      </dependency>
                  </dependencies>
              </plugin>
          </plugins>
      </build>
      
    2. 创建mybatis核心配置文件

    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="MyBatis3">
                  <!-- 数据库的连接信息 -->
                  <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                                  connectionURL="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"
                                  userId="root"
                                  password="123456">
                  </jdbcConnection>
                  <!-- javaBean的生成策略-->
                  <javaModelGenerator targetPackage="com.atguigu.mybatis.pojo" targetProject=".\src\main\java">
                      <property name="enableSubPackages" value="true" />
                      <property name="trimStrings" value="true" />
                  </javaModelGenerator>
                  <!-- SQL映射文件的生成策略 -->
                  <sqlMapGenerator targetPackage="com.atguigu.mybatis.mapper" targetProject=".\src\main\resources">
                      <property name="enableSubPackages" value="true" />
                  </sqlMapGenerator>
                  <!-- Mapper接口的生成策略 -->
                  <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.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. 执行MGB插件的gernerate目标

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4tvakC6P-1664239576481)(C:\Users\86137\AppData\Roaming\Typora\typora-user-images\image-20220926204046502.png)]

      效果:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-msmwFFqh-1664239576483)(C:\Users\86137\AppData\Roaming\Typora\typora-user-images\image-20220926204123826.png)]

  2. OBC查询(带条件的查询)

    创建 Criteria 对象 可以使用Example类中的 createCriteria() 或者 or() . 如果 Criteria 对象是用 createCriteria() 创建的,它会自动为 List 属性添加一个 Criteria 对象,这使得它更容易写一个简单的where子句, 如果您不需要 or 或者其他几个子句组合的话. 用 or(Criteria criteria) 方法创建 Criteria 对象, 方法里的 criteria 对象会被添加进 Criteria 对象的列表中

    public void testMBG(){
        SqlSession sqlSession= SqlSessionUtils.getSqlSession();
        EmpMapper mapper=sqlSession.getMapper(EmpMapper.class);
        //查询id为1的员工信息,根据主键条件来查询
        /*Emp emp=mapper.selectByPrimaryKey(1);
        System.out.println(emp);*/
    	//根据条件查询,当条件为null时,查询出全部的数据信息
        /*List<Emp> list=mapper.selectByExample(null);
        list.forEach(System.out::println);*/
    	//多条件查询时,需要手动创建条件,并将每一个条件通过createCriteria()或or()方法拼接到SQL中中,前者拼接and连接条件,后者拼接or连接条件
        /*EmpExample example=new EmpExample();
        example.createCriteria().andEmpNameEqualTo("张三").andAgeEqualTo(20);
        example.or().andGenderEqualTo("男");
        List<Emp> list=mapper.selectByExample(example);
        list.forEach(System.out::println);*/
    
        Emp emp=new Emp(1,"小黑",null,"女");
        //mapper.updateByPrimaryKey(emp);
        //测试选择性修改,修改的属性值为null则不会修改当前属性的属性值
        mapper.updateByPrimaryKeySelective(emp);
    }
    

14、分页插件

​ 若要简单生成如下类型的分页模块,使用分页插件可以提高效率

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8XP9k9nJ-1664239576485)(C:\Users\86137\AppData\Roaming\Typora\typora-user-images\image-20220927082947070.png)]

​ 分页模块中有如下重要的数据:

limit index,pageSize(index和pageSize关键数据)
pageSize:每页显示的条数(一般为一个固定的数值)
pageNum:当前页的页码(需要从浏览器传到服务器端)
index:当前页的起始索引,index=(pageNum-1)*pageSize
count:总记录数
totalPage:总页数

totalPage = count / pageSize;
if(count % pageSize != 0){
totalPage += 1;
}(从而计算出末页)

pageSize=4,pageNum=1,index=0 limit 0,4
pageSize=4,pageNum=3,index=8 limit 8,4
pageSize=4,pageNum=6,index=20 limit 8,4

  1. 分页插件的使用步骤

    1. 添加依赖

      <dependency>
          <groupId>com.github.pagehelper</groupId>
          <artifactId>pagehelper</artifactId>
          <version>5.2.0</version>
      </dependency>
      
    2. 配置分页插件

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

      <plugins>
          <!--配置分页插件-->
          <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
      </plugins>
      
    3. 分页插件的使用

      • 在查询功能之前使用PageHelper.startPage(int pageNum,int pageSize)方法开启分页功能(会返回一个page集合对象,存储了一部分的分页信息数据)

        pageSize:每页显示的条数(一般为一个固定的数值)
        pageNum:当前页的页码(需要从浏览器传到服务器端

      • 再通过查询获取需要的全部目标数据,放入list集合中

      • 在获取到list集合数据后,使用PageInfo pageInfo=new PageInfo<>(List list,int navigatePages)方法获取分页相关的数据

        list:分页之后的数据(

        navigatePages:导航分页的页码数

      • 分页相关数据

        PageInfo{
        pageNum=8, pageSize=4, size=2, startRow=29, endRow=30, total=30, pages=8,
        list=Page{count=true, pageNum=8, pageSize=4, startRow=28, endRow=32, total=30,
        pages=8, reasonable=false, pageSizeZero=false},
        prePage=7, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true,
        hasNextPage=false, navigatePages=5, navigateFirstPage4, navigateLastPage8,
        navigatepageNums=[4, 5, 6, 7, 8]
        }
        pageNum:当前页的页码
        pageSize:每页显示的条数
        size:当前页显示的真实条数
        total:总记录数
        pages:总页数
        prePage:上一页的页码
        nextPage:下一页的页码
        isFirstPage/isLastPage:是否为第一页/最后一页
        hasPreviousPage/hasNextPage:是否存在上一页/下一页
        navigatePages:导航分页的页码数
        navigatepageNums:导航分页的页码,[1,2,3,4,5]

分页插件的使用步骤

  1. 添加依赖

    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.2.0</version>
    </dependency>
    
  2. 配置分页插件

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

    <plugins>
        <!--配置分页插件-->
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
    
  3. 分页插件的使用

    • 在查询功能之前使用PageHelper.startPage(int pageNum,int pageSize)方法开启分页功能(会返回一个page集合对象,存储了一部分的分页信息数据)

      pageSize:每页显示的条数(一般为一个固定的数值)
      pageNum:当前页的页码(需要从浏览器传到服务器端

    • 再通过查询获取需要的全部目标数据,放入list集合中

    • 在获取到list集合数据后,使用PageInfo pageInfo=new PageInfo<>(List list,int navigatePages)方法获取分页相关的数据

      list:分页之后的数据(

      navigatePages:导航分页的页码数

    • 分页相关数据

      PageInfo{
      pageNum=8, pageSize=4, size=2, startRow=29, endRow=30, total=30, pages=8,
      list=Page{count=true, pageNum=8, pageSize=4, startRow=28, endRow=32, total=30,
      pages=8, reasonable=false, pageSizeZero=false},
      prePage=7, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true,
      hasNextPage=false, navigatePages=5, navigateFirstPage4, navigateLastPage8,
      navigatepageNums=[4, 5, 6, 7, 8]
      }
      pageNum:当前页的页码
      pageSize:每页显示的条数
      size:当前页显示的真实条数
      total:总记录数
      pages:总页数
      prePage:上一页的页码
      nextPage:下一页的页码
      isFirstPage/isLastPage:是否为第一页/最后一页
      hasPreviousPage/hasNextPage:是否存在上一页/下一页
      navigatePages:导航分页的页码数
      navigatepageNums:导航分页的页码,[1,2,3,4,5]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值