Mybatis

Posted littleskinny

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis相关的知识,希望对你有一定的参考价值。

Mybatis

1、什么是Mybatis?

  • 历史

    • MyBatis 本是apache的一个开源项目iBatis

    • 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis

    • 2013年11月迁移到Github

  • 定义

    • 一个基于Java的持久层框架

    • 支持定制化 SQL、存储过程以及高级映射

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

    • 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和  Java 的 Pojos(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录

  • Mybatis相关资料

    • maven仓库

      <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis</artifactId>
         <version>3.5.2</version>
      </dependency>
    • Github

      Github地址

    • 官网文档

      官网中文文档地址

 

2、持久化和持久层

  • 持久化

    • 把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)

    • 持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等

    • 持久化是将程序数据在持久状态和瞬时状态间转换的机制

    • JDBC就是一种持久化机制,文件IO也是一种持久化机制

  • 持久层

    • 完成持久化工作的代码块

 

3、第一个Mybatis程序

  • 环境搭建

    • 建数据库以及表

    • 新建Maven项目后在pom.xml中导入依赖

      <!--mysql依赖-->
      <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>5.1.47</version>
      </dependency>
      ?
      <!--mybatis依赖-->
      <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis</artifactId>
         <version>3.5.2</version>
      </dependency>
      ?
      <!--Lombok依赖-->
      <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
         <version>1.18.12</version>
      </dependency>
  • 编写Mybatis配置文件:mybatis-config.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
           PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
           "http://mybatis.org/dtd/mybatis-3-config.dtd">
    ?
    <!--configuration核心配置文件-->
    <configuration>
       
       <!--可以配置多种环境,如开发、测试,default指定用的是哪种环境-->
       <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?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                   <property name="username" value="root"/>
                   <property name="password" value="root"/>
               </dataSource>
           </environment>
       </environments>
    </configuration>
  • 编写Mybatis工具类

    • 1、构建SqlSessionFactory

      • 每个基于Mybatis的应用都是以一个SqlSessionFactory的实例为核心的

      • SqlSessionFactoryBuilder可以从XML配置文件或者一个预先定制的Configuration的实例构建出SqlSessionFactory的实例

        • 使用任意的输入流(InputStream)实例,通过字符串形式的文件路径,或者url形式的文件路径配置

        • Resources工具类可使从classpath或其他位置加载资源文件更加容易

    • 2、从SqlSessionFactory中获取SqlSession

      • SqlSession 提供了在数据库执行 SQL 命令所需的所有方法

      • SqlSession 实例来直接执行已映射的 SQL 语句

    • SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession 的生命周期和作用域

      • SqlSessionFactoryBuilder

        • 一旦创建了 SqlSessionFactory,就不再需要SqlSessionFactoryBuilder了

        • 最佳作用域是方法作用域(也就是局部方法变量)

      • SqlSessionFactory

        • 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃SqlSessionFactory或重新创建另一个实例

        • 最佳作用域是应用作用域,最简单的就是使用单例模式或者静态单例模式

      • SqlSession

        • 每个线程都应该有它自己的 SqlSession 实例

        • 最佳的作用域是请求或方法作用域

    public class MybatisUtils {
    ?
       private static SqlSessionFactory sqlSessionFactory;
    ?
       static {
           try {
               //使用mybatis第一步:获取sqlSessionFactory对象
               String resource = "mybatis-config.xml";
               InputStream inputStream = Resources.getResourceAsStream(resource);
               sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
          } catch (IOException e) {
               e.printStackTrace();
          }
      }
    ?
       //从 SqlSessionFactory 中获取 SqlSession
       public static SqlSession getSqlSession(){
           return sqlSessionFactory.openSession();
      }
    }
  • 编写代码

    • 实体类

      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class User {
      ?
         private Integer id;
         private String name;
         private String pwd;
      }
    • Dao接口,UserMapper

      public interface UserMapper {
         List<User> getUserList();
      }
    • 接口实现类,UserMapper.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">
      ?
      <!--namespace,绑定一个对应的mapper接口-->
      <mapper namespace="com.hmx.dao.UserMapper">
         
         <!--
      id:sql语句对应的执行的方法名
      resultType:返回结果,要写全限命名
      -->
         <select id="getUserList" resultType="com.hmx.pojo.User">
            select * from mybatis.mybatistest
         </select>
      </mapper>
    • 在mybatis的配置文件中给每个Mapper.xml配置mapper

      <!--每一个Mapper.xml都需要在核心配置文件中注册-->
      <mappers>
         <mapper resource="com/hmx/dao/UserMapper.xml"/>
      </mappers>
    • 测试

      public class UserMapperTest {
         public static void main(String[] args) {
      ?
             //第一步:获得SqlSession对象
             SqlSession sqlSession = MybatisUtils.getSqlSession();
      ?
             //获得mapper,执行sql语句
             UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
             List<User> userList = userMapper.getUserList();
      ?
             //输出结果
             for (User user : userList) {
                 System.out.println(user);
            }
             
             //关闭SqlSession
             sqlSession.close();
        }
      }
    • 注意点:常见错误

      • Type interface com.hmx.dao.UserDao is not known to the MapperRegistry

        原因:Mapper.xml没有在核心配置文件中注册

        解决:在mybatis的配置文件中给每个Mapper.xml配置mapper

        <!--每一个Mapper.xml都需要在核心配置文件中注册-->
        <mappers>
           <mapper resource="com/hmx/dao/UserMapper.xml"/>
        </mappers>
      • Could not find resource com/hmx/dao/UserMapper.xml

        原因:资源导入失败,Maven导出资源问题

        解决:在pom.xml中配置如下代码

        <build>
           <resources>
               <resource>
                   <directory>src/main/resources</directory>
                   <includes>
                       <include>**/*.properties</include>
                       <include>**/*.xml</include>
                   </includes>
                   <filtering>true</filtering>
               </resource>
               <resource>
                   <directory>src/main/java</directory>
                   <includes>
                       <include>**/*.properties</include>
                       <include>**/*.xml</include>
                   </includes>
                   <filtering>true</filtering>
               </resource>
           </resources>
        </build>
      • com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 1 字节的 UTF-8 序列的字节 1 无效

        原因:xml编码问题

        解决:

        1、在pom.xml中配置如下代码

        <properties>
           <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>

        若方法1没用,参考

        2、把每个xml文件的文件头

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

        改为:

        <?xml version="1.0" encoding="UTF8" ?>

 

4、增删改查

  • 实体类:User

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
    ?
       private Integer id;
       private String name;
       private String pwd;
    }
  • 接口类:UserMapper

    public interface UserMapper {
    ?
       //查询所有数据
       List<User> getUserList();
    //根据id查询数据
       User selectById(int id);
    //增加数据
       int insertUser(User user);
    //更新数据
       int updateUser(User user);
    //删除数据
       int deleteUser(int id);
    }
  • 接口类的实现UserMapper.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">
    <!--namespace绑定一个mapper接口-->
    <mapper namespace="com.hmx.dao.UserMapper">
    ?
       <select id="getUserList" resultType="com.hmx.model.User">
          select * from mybatis.mybatistest;
       </select>
    ?
       <select id="selectById" parameterType="int" resultType="com.hmx.model.User">
          select * from mybatis.mybatistest where id = #{id};
       </select>
    ?
       <insert id="insertUser" parameterType="com.hmx.model.User">
          insert into mybatis.mybatistest (id,name,pwd) values (#{id},#{name},#{pwd});
       </insert>
    ?
       <update id="updateUser" parameterType="com.hmx.model.User">
          update mybatis.mybatistest set name = #{name},pwd = #{pwd} where id = #{id};
       </update>
    ?
       <delete id="deleteUser">
          delete from mybatis.mybatistest where id = #{id};
       </delete>
    </mapper>
  • 测试

    //查询所有数据
    public static void selectAll(){
    ?
       //第一步:获得SqlSession对象
       SqlSession sqlSession = MybatisUtils.getSqlSession();
       //执行sal语句
       UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
       List<User> userList = userMapper.getUserList();
       for (User user : userList) {
           System.out.println(user);
      }
       //关闭SqlSession
       sqlSession.close();
    }
    ?
    //根据id查询数据
    public static void selectById(){
       SqlSession sqlSession = MybatisUtils.getSqlSession();
    ?
       UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
       User user = userMapper.selectById(1);
       System.out.println(user);
    ?
       sqlSession.close();
    }
    ?
    //添加数据
    public static void insertUser(){
       SqlSession sqlSession = MybatisUtils.getSqlSession();
    ?
       UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
       userMapper.insertUser(new User(9,"大黄","123"));
       sqlSession.commit();
    ?
       sqlSession.close();
    }
    ?
    //修改数据
    public static void updateUser(){
       SqlSession sqlSession = MybatisUtils.getSqlSession();
    ?
       UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
       userMapper.updateUser(new User(4,"大黑子","333"));
       sqlSession.commit();
    ?
       sqlSession.close();
    }
    ?
    //删除数据
    public static void deleteUser(){
       SqlSession sqlSession = MybatisUtils.getSqlSession();
    ?
       UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
       userMapper.deleteUser(9);
       sqlSession.commit();
    ?
       sqlSession.close();
    }

 

5、注解实现增删改查

  • Mybatis注解作用:

    • 使用注解来映射简单语句会使代码显得更加简洁

    • 但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪

    • 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句

  • mybatis注解本质

    • 本质:反射机制

    • 底层:动态代理模式

  • 注意点:

    • 注解写在接口类中的方法上

    • 使用注解开发不需要写接口的实现类(XXXMapper.xml)文件,所以在配置文件中需要绑定XXXMapper.java

      <mappers>
         <mapper class="com.hmx.mapper.UserMapper"/>
      </mappers>
  • @Param()注解

    • 方法中只有一个参数,可加可不加,最好加,多个参数必须加

    • 基本类型和String类型需要加上

    • 和sql语句中引用的#{}相匹配

  • 增删改查

    public interface UserMapper {
    ?
       //查询所有数据
       @Select("select * from mybatis.mybatistest")
       List<User> getUserList();
       
       //根据id查询数据
       @Select("select * from mybatis.mybatistest where id = #{id}")
       User selectById(@Param("id") int id);
       
       //增加数据
       @Insert("insert into mybatis.mybatistest (id,name,pwd) values (#{id},#{name},#{pwd})")
       int insertUser(User user);
       
       //更新数据
       @Update("update mybatis.mybatistest set name = #{name},pwd = #{pwd} where id = #{id}")
       int updateUser(User user);
       
       //删除数据
       @Delete("delete from mybatis.mybatistest where id = #{id};")
       int deleteUser(@Param("id") int id);
    }

 

6、Mybatis配置文件解析

configuration核心配置文件中包含了会深深影响 MyBatis 行为的设置和属性信息,可以配置如下配置:

  • properties(属性)

    • 可以直接引入外部文件

      <properties resource="db.properties"/>
      #db.properties
      driver=com.mysql.jdbc.Driver
      url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
      username=root
      password=root
    • 如果db.properties中没有配置username和password,则可以可以在properties中增加一些属性配置

      <properties resource="db.properties">
         <property name="username" value="root"/>
         <property name="password" value="root"/>
      </properties>
    • 如果两个配置文件和properties中存在同一字段,如:都含有username这个字段但是值不一样,优先使用外部配置文件

  • settings(设置)

    • 一个配置完整的settings元素的示例如下:

      <settings>
      1、<!--全局地开启或关闭所有映射器配置文件中已配置的任何缓存,有效值true|false,默认true-->
       <setting name="cacheEnabled" value="true"/>
         
      2、<!--
            延迟加载的全局开关。
            当开启时,所有关联对象都会延迟加载。
            特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。
            有效值true|false,默认false
      -->
       <setting name="lazyLoadingEnabled" value="true"/>
         
      3、<!--是否允许单个语句返回多结果集(需要数据库驱动支持),有效值true|false,默认true-->
       <setting name="multipleResultSetsEnabled" value="true"/>
         
      4、<!--
         使用列标签代替列名
      实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察
      有效值true|false,默认true
      -->
       <setting name="useColumnLabel" value="true"/>
         
      5、<!--
      允许 JDBC 支持自动生成主键,需要数据库驱动支持
      如果设置为 true,将强制使用自动生成主键
      尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)
      有效值true|false,默认false
      -->
       <setting name="useGeneratedKeys" value="false"/>
         
      6、<!--
      指定 MyBatis 应如何自动映射列到字段或属性。
      NONE 表示关闭自动映射
      PARTIAL 只会自动映射没有定义嵌套结果映射的字段
      FULL 会自动映射任何复杂的结果集(无论是否嵌套)
      默认值为PARTIAL
      -->
       <setting name="autoMappingBehavior" value="PARTIAL"/>
         
      7、<!--
      指定发现自动映射目标未知列(或未知属性类型)的行为。
      NONE: 不做任何反应
      WARNING: 输出警告日志
      FAILING: 映射失败 (抛出 SqlSessionException)
      默认值为NONE
      -->
       <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
         
      8、<!--
      配置默认的执行器
      SIMPLE 就是普通的执行器
      REUSE 执行器会重用预处理语句(PreparedStatement)
      BATCH 执行器不仅重用语句还会执行批量更新
      默认值为SIMPLE-->
       <setting name="defaultExecutorType" value="SIMPLE"/>
         
      9、<!--
      设置超时时间,它决定数据库驱动等待数据库响应的秒数
      有效值为任意正整数 默认未设置 (null)
      -->
       <setting name="defaultStatementTimeout" value="25"/>
         
      10、<!--
      为驱动的结果集获取数量(fetchSize)设置一个建议值,此参数只可以在查询设置中被覆盖。
      有效值为任意正整数 默认未设置 (null)
      -->
       <setting name="defaultFetchSize" value="100"/>
         
      11、<!--
      是否允许在嵌套语句中使用分页(RowBounds),如果允许使用则设置为 false
      有效值true|false,默认false
      -->
       <setting name="safeRowBoundsEnabled" value="false"/>
         
      12、<!--
      是否开启驼峰命名自动映射,即从经典数据库列名A_COLUMN映射到经典Java属性名aColumn
      有效值true|false,默认false
      -->
       <setting name="mapUnderscoreToCamelCase" value="false"/>
       
      13、<!--
      MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询
      SESSION,会缓存一个会话中执行的所有查询
      STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存 默认值为SESSION
      -->
       <setting name="localCacheScope" value="SESSION"/>
         
      14、<!--
      当没有为参数指定特定的JDBC类型时,空值的默认JDBC类型
      某些数据库驱动需要指定列的JDBC类型,多数情况直接用一般类型即可
      有效值为JdbcType常量,常用值:NULL、VARCHAR或OTHER,默认OTHER
      -->
       <setting name="jdbcTypeForNull" value="OTHER"/>
         
      15、<!--
      指定对象的哪些方法触发一次延迟加载。
      有效值为用逗号分隔的方法列表:equals,clone,hashCode,toString
      -->
       <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
      </settings>
    • 具体可以查看mybatis官网中关于settings的配置解释

      mybatis中文文档 - settings

  • typeAliases(类型别名)

    • 降低冗余的全限定类名书写

    • 给具体某个类设置别名

      适合实体类不是特别多的情况

      <typeAliases>
         <typeAlias type="com.hmx.model.User" alias="User"/>
      </typeAliases>
      ?
      <!--设置别名后可以直接在resultType里写别名即可-->
      <select id="" resultType="User">
      </select>
    • 给包下所有类设置别名,默认类名首字母小写为别名,可在类上加@Alise("aaa")注解指定别名

      适合实体类特别多的情况

      <typeAliases>
         <package name="com.hmx.model"/>
      </typeAliases>
  • environments(环境配置)

    • MyBatis 可以配置成适应多种环境,每个 SqlSessionFactory 实例只能选择一种环境

    • transactionManager(事务管理器),type="[JDBC|MANAGED]"

    • dataSource(数据源),type="[UNPOOLED|POOLED|JNDI]

      <environments default="development">
         <environment id="development">
             <transactionManager type="JDBC"/>
             <dataSource type="POOLED">
             </dataSource>
         </environment>
      </environments>
  • mappers(映射)

    • 方式一:

      <mappers>
         <mapper resource="com/hmx/dao/UserMapper.xml"/>
      </mappers>
    • 方式二:mapper接口和mapper.xml名字必须相同,而且必须放在一个包下

      <mappers>
         <mapper class="com.hmx.dao.UserMapper"/>
      </mappers>
    • 方式三:mapper接口和mapper.xml名字必须相同,而且必须放在一个包下

      <mappers>
         <package name="com.hmx.dao"/>
      </mappers>
  • typeHandlers(类型处理器)

  • objectFactory(对象工厂)

  • plugins(插件)

  • databaseIdProvider(数据库厂商标识)

 

注:本文往后都使用了别名配置

 

7、Map妙用

  • 通过map可以不用知道数据库中表有几列,写sql语句时也不用把对象中所有的属性都写一遍

  • 假设我们的数据库中的表的字段过多,可以考虑使用map

    • 示例一:Map查询数据

      • 接口类

        User selectByIdAndName(Map<String,Object> map);
      • 接口类的实现

        <select id="selectByIdAndName" parameterType="map"                  resultType="User">
          select * from mybatistest where id = #{Id} and name = #{Name};
        </select>
      • 测试

        public static void selectByIdAndName(){
           SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
           UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
           Map<String,Object> map = new HashMap<String, Object>();
           map.put("Id",1);
           map.put("Name","洪梦霞");
           User user = userMapper.selectByIdAndName(map);
           System.out.println(user);
        ?
           sqlSession.close();
        }
    • 示例二:Map插入数据

      • 接口类

        int insertUser(Map<String,Object> map);
      • 接口类的实现

        <insert id="insertUser" parameterType="map">
          insert into mybatistest (id,name,pwd) values (#{mapId},#{mapName},#{mapPwd});
        </insert>
      • 测试

        public static void insertUser(){
           SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
           UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
           Map<String, Object> map = new HashMap<String, Object>();
           map.put("mapId",4);
           map.put("mapName","小叶子");
           map.put("mapPwd","985");
           userMapper.insertUser(map);
           sqlSession.commit();
        ?
           sqlSession.close();
        }

 

8、模糊查询

  • 接口类

    List<User> selectByParameter(String value);
  • 实现方式一

    • 接口类的实现

      <select id="selectByParameter" resultType="User">
        select * from mybatistest where name like #{value};
      </select>
    • 测试

      public static void selectByParameter(){
         SqlSession sqlSession = MybatisUtils.getSqlSession();
      ?
         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
         List<User> userList = userMapper.selectByParameter("%小%");
         for (User user : userList) {
             System.out.println(user);
        }
      ?
         sqlSession.close();
      }
  • 实现方式二

    • 接口类的实现

      <select id="selectByParameter" resultType="User">
        select * from mybatistest where name like "%"#{value}"%";
      </select>
    • 测试

      public static void selectByParameter(){
         SqlSession sqlSession = MybatisUtils.getSqlSession();
      ?
         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
         List<User> userList = userMapper.selectByParameter("小");
         for (User user : userList) {
             System.out.println(user);
        }
      ?
         sqlSession.close();
      }

 

9、分页

  • 实体类:User

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
    ?
       private Integer id;
       private String name;
       private String pwd;
    }
  • 方式一:使用limit实现分页

    • 接口类:UserMapper

      public interface UserMapper {
         List<User> selectLimit(Map<String,Integer> map);
      }
    • 接口的实现类:UserMapper.xml

      <?xml version="1.0" encoding="UTF8" ?>
      <!DOCTYPE mapper
             PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <mapper namespace="com.hmx.mapper.UserMapper">
      ?
         <select id="selectLimit" parameterType="map" resultType="User">
            select * from mybatistest limit #{startIndex},#{pageSize};
         </select>
      </mapper>
    • 测试

      public static void selectLimit(){
         
         SqlSession sqlSession = MybatisUtils.getSqlSession();
         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
         
         HashMap<String, Integer> map = new HashMap<String, Integer>();
         map.put("startIndex",1);
         map.put("pageSize",3);
         List<User> userList = userMapper.selectLimit(map);
         
         for (User user : userList) {
             System.out.println(user);
        }
      ?
         sqlSession.close();
      }
  • 方式二:使用RowBounds实现分页

    • 接口类:UserMapper

      public interface UserMapper {
         List<User> selectRowBounds();
      }
    • 接口的实现类:UserMapper.xml

      <?xml version="1.0" encoding="UTF8" ?>
      <!DOCTYPE mapper
             PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <mapper namespace="com.hmx.mapper.UserMapper">
      ?
         <select id="selectRowBounds" parameterType="map" resultType="User">
            select * from mybatistest
         </select>
      </mapper>
    • 测试

          public static void selectRowBounds(){
             SqlSession sqlSession = MybatisUtils.getSqlSession();
      ?
             /*
             RowBounds(int offset, int limit)
             RowBounds(开始位置, 查询个数)
             */
             RowBounds rowBounds = new RowBounds(1, 2);
             
             /*
             selectList(String var1, Object var2, RowBounds var3)
             selectList(接口类的全限定名.方法名,一般写null即可, RowBounds对象)
             */
             List<User> userList = sqlSession.selectList("com.hmx.mapper.UserMapper.selectRowBounds", "null", rowBounds);
             
             for (User user : userList) {
                 System.out.println(user);
            }
      ?
             sqlSession.close();
        }
  • 方式三:使用分页插件PageHelper进行分页

    如需使用,可参考以下链接:

    如何使用分页插件PageHelper

 

10、ResultMap结果集映射

  • 作用

    • MyBatis 中最重要最强大的元素

    • 解决字段名和属性名不一致的问题

    • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系

  • ResultMap配置详解

    <!--
    id为这个resultMap的标识
    type为映射的类型,一个Java类的全限定名,或一个类型别名
    -->
    <resultMap id="" type="">
       <!--
           id和result都将一个列的值映射到一个简单数据类型的属性或字段
          column:对应数据库中的列
          property:对应实体类中的属性
          javaType:一个Java类的全限定名,或一个类型别名,结果集映射返回的结果
          jdbcType:所支持的JDBC类型
          typeHandler:类型处理器
       -->
       <id column="" property="" javaType="" jdbcType="" typeHandler=""></id>
       <result column="" property="" javaType="" jdbcType="" typeHandler=""></result>
       <!--
           association处理“有一个”类型的关系
           collection处理“有多个”类型的关系
               column:对应数据库中的列
               property:对应实体类中的属性(对象)
               javaType:指定实体类中属性的类型
               ofType:指定映射到集合中的实体类类型,泛型中的约束类型
               select:用于加载复杂类型属性的映射语句的id
                       会从column属性指定的列中检索数据,作为参数传递给目标select语句
       -->
       <association column="" property="" javaType="" select=""></association>
       <collection column="" property="" javaType="" select=""></collection>
    </resultMap>
  • 1、解决数据库中的字段和实体类中属性不一致

    • 实体类

      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class User {
      ?
         private Integer id;
         private String name;
         private String password;
      }
    • 接口类

      User selectById(int id);
    • 接口类的实现

      <resultMap id="UserMap" type="user">
         <!--
      id,name属性和数据库中的字段一样,所有不需要显式写出,可省略
        <result column="id" property="id"/>
        <result column="name" property="name"/>
      -->
         <result column="pwd" property="password"/>
      </resultMap>
      ?
      <!--resultMap里的值为结果集映射<resultMap></resultMap>的id值-->
      <select id="selectById" resultMap="UserMap">
        select * from mybatistest where id = #{id};
      </select>
  • 2、多对一以及一对多问题

    • Teacher表

      id:老师id
      name:老师姓名
    • Student表

      id:学生id
      name:学生姓名
      tid:学生关联的老师id
    • 多对一:多个学生对应一个老师,查询学生的信息以及学生的老师的信息

      • 1、老师类:Teacher

        @Data
        public class Teacher {
           private int id;
           private String name;
        }
      • 2、老师的接口类:TeacherMapper

        public interface TeacherMapper {
        }
      • 3、老师接口类的实现:TeacherMapper.xml

        <?xml version="1.0" encoding="UTF8" ?>
        <!DOCTYPE mapper
               PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
               "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
        ?
        <mapper namespace="com.hmx.mapper.TeacherMapper">
        </mapper>
      • 4、学生类:Student

        @Data
        public class Student {
        ?
           private int id;
           private String name;
           private Teacher teacher;
        }
      • 5、学生的接口类:StudentMapper

        public interface StudentMapper {
           List<Student> getStudent();
        }
      • 6、学生接口类的实现:StudentMapper.xml

        • 方法一:根据查询嵌套处理,子查询

          <?xml version="1.0" encoding="UTF8" ?>
          <!DOCTYPE mapper
                 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
          ?
          <mapper namespace="com.hmx.mapper.StudentMapper">
             
             <!--查询所有的学生信息-->
             <select id="getStudent" resultMap="StudentTeacher">
                select * from student
             </select>
          ?
             <!--
          根据查询出来的学生的tid属性,查询老师的信息
          tid的值是从resultMap中传递过来的
          -->
             <select id="getTeacher" resultType="Teacher">
                select * from teacher where id = #{tid}
             </select>
             
             <resultMap id="StudentTeacher" type="Student">
                 <result property="id" column="id"/>
                 <result property="name" column="name"/>
                 
                 <!--从tid列中检索数据,作为参数传递给id为getTeacher的select语句-->
                 <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
             </resultMap>
          </mapper>
        • 方法二:根据结果嵌套处理,关联查询

          <?xml version="1.0" encoding="UTF8" ?>
          <!DOCTYPE mapper
                 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
          ?
          <mapper namespace="com.hmx.mapper.StudentMapper">
             
             <select id="getStudent" resultMap="StudentTeacher">
                select s.id as sid,s.`name` as sname,t.`name` tname
                from student s,teacher t
                where s.tid = t.id
             </select>
          ?
             <resultMap id="StudentTeacher" type="Student">
                 <result property="id" column="sid"/>
                 <result property="name" column="sname"/>
                 <association property="teacher" javaType="Teacher">
                     <result property="name" column="tname"/>
                 </association>
             </resultMap>
          ?
          </mapper>
      • 7、测试

        public static void selectAll() {
           SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
           StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
           List<Student> studentList = studentMapper.getStudent();
           for (Student student : studentList) {
               System.out.println(student);
          }
        ?
           sqlSession.close();
        }

     

    • 一对多:一个老师有多个学生,查询指定老师下的学生的信息,以及该老师的信息

      • 1、学生类:Student

        @Data
        public class Student {
        ?
           private int id;
           private String name;
           private int tid;
        }
      • 2、学生的接口类:StudentMapper

        public interface StudentMapper {
        }
      • 3、学生接口类的实现类:StudentMapper.xml

        <?xml version="1.0" encoding="UTF8" ?>
        <!DOCTYPE mapper
               PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
               "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
        ?
        <mapper namespace="com.hmx.mapper.StudentMapper">
        </mapper>
      • 4、老师类:Teacher

        @Data
        public class Teacher {
        ?
           private int id;
           private String name;
           private List<Student> studentList;
        }
      • 5、老师的接口类:TeacherMapper

        public Teacher getTeacher(int id);
      • 6、老师接口类的实现类:TeacherMapper.xml

        • 方法一:根据查询嵌套处理,子查询

          <?xml version="1.0" encoding="UTF8" ?>
          <!DOCTYPE mapper
                 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
          <mapper namespace="com.hmx.mapper.TeacherMapper">
          ?
             <!--查询老师的所有信息-->
             <select id="getTeacher" resultMap="TeacherStudent">
                select * from teacher where id = #{id}
             </select>
          ?
             <!--根据老师的id查询学生的所有信息-->
             <select id="getStudentByTeacherId" resultType="Student">
                select * from student where tid = #{tid}
             </select>
          ?
             <!--把老师里的id传给select查询块-->
             <resultMap id="TeacherStudent" type="Teacher">
                 <collection property="studentList" column="id" javaType="ArrayList" ofType="student" select="getStudentByTeacherId">
                 </collection>
             </resultMap>
          ?
          </mapper>
        • 方法二:根据结果嵌套处理,关联查询

          <?xml version="1.0" encoding="UTF8" ?>
          <!DOCTYPE mapper
                 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
          <mapper namespace="com.hmx.mapper.TeacherMapper">
          ?
             <select id="getTeacher" resultMap="TeacherStudent">
                SELECT
                s.id AS sid,
                s.`name` AS sname,
                    t.id AS tid,
                t.`name` tname
                FROM
                student s,
                teacher t
                    WHERE
                s.tid = t.id AND t.id = #{tid}
             </select>
          ?
             <resultMap id="TeacherStudent" type="Teacher">
                 <result property="id" column="tid"/>
                 <result property="name" column="tname"/>
                 
                 <collection property="studentList" ofType="Student">
                     <result property="id" column="sid"/>
                     <result property="name" column="sname"/>
                     <result property="tid" column="tid"/>
                 </collection>
             </resultMap>
          ?
          </mapper>
      • 7、测试

        public static void testTeacher() {
           SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
           TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);
           Teacher teacher = teacherMapper.getTeacher(1);
           System.out.println(teacher);
        ?
           sqlSession.close();
        }

 

11、动态sql

  • 定义

    根据不同的条件生成不同的sql语句,本质还是sql语句,只是我们可以在sql层面执行逻辑代码

  • 环境搭建

    • blog表

      id
      title
      author
      creat_time
      views
    • 实体类:Blog

      @Data
      public class Blog {
      ?
         private String id;
         private String title;
         private String author;
         private Date creatTime;
         private int views;
      }
    • 配置文件

      <configuration>
      ?
         <settings>
             <setting name="logImpl" value="STDOUT_LOGGING"/>
             <!--开启驼峰命名-->
             <setting name="mapUnderscoreToCamelCase" value="true"/>
         </settings>
      ?
         <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?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                     <property name="username" value="root"/>
                     <property name="password" value="root"/>
                 </dataSource>
             </environment>
         </environments>
      ?
         <mappers>
             <mapper class="com.hmx.mapper.BlogMapper"/>
         </mappers>
      ?
      </configuration>
  • if

    • 这条语句提供了可选的查找文本功能

    • 传入哪个就按哪个查找,如果不传入,就按select里的原始sql语句查找

    • 测试

      • 接口类

        public interface BlogMapper {
           List<Blog> queryBlogIf(Map map);
        }
      • 接口类的实现类

        <mapper namespace="com.hmx.mapper.BlogMapper">
        ?
           <select id="queryBlogIf" parameterType="map" resultType="Blog">
              select * from blog
               <!--
        where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句
        而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除
        -->
               <where>
                   <!--
        test里面是判断条件
        表达式为true,把if中的语句拼接到select中的sql上
        表达式为false,忽略此语句
        -->
                   <if test="title != null">
                      and title = #{title}
                   </if>
                   <if test="author !=null" >
                      and author = #{author}
                   </if>
               </where>
           </select>
        ?
        </mapper>
      • 测试实现

        public static void queryBlogIf() {
        ?
           SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
           BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
           HashMap map = new HashMap();
           //查询title为"Mybatis如此简单"的记录
           map.put("title", "Mybatis如此简单");
           
           //查询author为"Andy"的记录
           //map.put("author","Andy");
        ?
           List<Blog> blogs = blogMapper.queryBlogIf(map);
           for (Blog blog : blogs) {
               System.out.println(blog);
          }
        ?
           sqlSession.close();
        }
  • choose,when,otherwise

    • 从多个条件中选择一个使用

    • 传入了哪个就按哪个查找,第一个when优先级最高,都没有传入,就按otherwise里的查找

    • 测试

      • 接口类

        public interface BlogMapper {
           List<Blog> queryBlogChoose(Map map);
        }
      • 接口的实现类

        <mapper namespace="com.hmx.mapper.BlogMapper">
        ?
           <select id="queryBlogChoose" parameterType="map" resultType="Blog">
              select * from blog
               <where>
                   <choose>
                       <when test="title != null">
                          title = #{title}
                       </when>
                       <when test="author !=null">
                          and author = #{author}
                       </when>
                       <otherwise>
                          and views = #{views}
                       </otherwise>
                   </choose>
               </where>
           </select>
        ?
        </mapper>
      • 测试实现

        public static void queryBlogChoose() {
        ?
           SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
           BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
           
           HashMap hashMap = new HashMap();
           hashMap.put("title", "Mybatis如此简单");
           //hashMap.put("author","洪梦霞");
           //hashMap.put("views","888");
           
           List<Blog> blogs = blogMapper.queryBlogChoose(hashMap);
           for (Blog blog : blogs) {
               System.out.println(blog);
          }
        ?
           sqlSession.close();
        }
  • trim,where,set

    • where

      • where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句,

        而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

      • 加入trim定制where元素的功能

        <!--拼接sql语句时会加上WHERE,并且删除多余的AND和OR-->
        <trim prefix="WHERE" prefixOverrides="AND |OR ">
        ...
        </trim>
    • set

      • set 元素可以用于动态包含需要更新的列,忽略其它不更新的列,它会动态地在行首插入 SET 关键字,并会删掉额外的逗号,在update语句中会用到

      • 加入trim定制set元素的功能

        <!--拼接sql语句时会加上SET,并且删除多余的","-->
        <trim prefix="SET" suffixOverrides=",">
        ...
        </trim>
  • foreach

    • 对集合进行遍历(尤其是在构建 IN 条件语句的时候)

    • 测试:select * from blog where 1 = 1 and (id = 1 or id = 2 or id = 3)

      • 接口类

        public interface BlogMapper {
           List<Blog> queryBlogForEach(Map map);
        }
      • 接口的实现类

        <mapper namespace="com.hmx.mapper.BlogMapper">
           
           <select id="queryBlogForEach" parameterType="map" resultType="Blog">
              select * from blog
               <where>
                   <!--
                       从collection这个集合中遍历,是map中存在的集合
                       遍历出的每一项叫做item中的值
                       index为下标
                       开始为open里的值
                       结束为close里的值
                       separator为分隔符
                   -->
                   <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                      id = #{id}
                   </foreach>
               </where>
           </select>
        ?
        </mapper>
      • 测试

        public static void queryBlogForEach() {
        ?
           SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
           BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
           HashMap map = new HashMap();
           
           ArrayList<Integer> ids = new ArrayList<Integer>();
           ids.add(1);
           ids.add(2);
           ids.add(3);
           map.put("ids", ids);
           
           List<Blog> blogs = blogMapper.queryBlogForEach(map);
           for (Blog blog : blogs) {
               System.out.println(blog);
          }
        ?
           sqlSession.close();
        }
  • sql片段

    • 将一些公共的功能抽取出来,方便复用

    • 注意:

      • 最好基于单表来定义SQL片段

      • 不要存在where标签

    • 使用步骤

      • 1、使用sql标签抽取公共的部分

        <sql id="if-title-author">
           <if test="title != null">
              title = #{title}
           </if>
           <if test="author !=null" >
              and author = #{author}
           </if>
        </sql>
      • 2、include标签引用

        <include refid="if-title-author"></include>

 

12、日志

  • 日志工厂

    Mybatis内置了许多的日志工厂,可以在settings中设置

    <!--
    logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找
           SLF4J
           LOG4J(重点)
           LOG4J2
           JDK_LOGGING
           COMMONS_LOGGING
           STDOUT_LOGGING(重点)
           NO_LOGGING
    -->
  • STDOUT_LOGGING:标准日志输出

    • 在核心配置文件中配置标准日志输出

      <settings>
         <setting name="logImpl" value="STDOUT_LOGGING"/>
      </settings>
    • 控制台输出

      Logging initialized using ‘class org.apache.ibatis.logging.stdout.StdOutImpl‘ adapter.
      PooledDataSource forcefully closed/removed all connections.
      PooledDataSource forcefully closed/removed all connections.
      PooledDataSource forcefully closed/removed all connections.
      PooledDataSource forcefully closed/removed all connections.
      Opening JDBC Connection
      Created connection 418304857.
      Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@18eed359]
      ==>  Preparing: select * from mybatis.mybatistest
      ==> Parameters:
      <==    Columns: id, name, pwd
      <==        Row: 1, 洪梦霞, 123
      <==        Row: 2, 王禹, 222
      <==        Row: 3, 小李子, 008
      <==        Row: 4, 小叶子, 985
      <==      Total: 4
      User(id=1, name=洪梦霞, pwd=123)
      User(id=2, name=王禹, pwd=222)
      User(id=3, name=小李子, pwd=008)
      User(id=4, name=小叶子, pwd=985)
      Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@18eed359]
      Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@18eed359]
      Returned connection 418304857 to pool.
  • LOG4J

    • 什么是log4j?

      • 可以控制日志信息输送的目的地是控制台或文件或GUI组件

      • 可以控制每一条日志的输出格式

      • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程

      • 可以通过一个配置文件来灵活地进行配置

    • 导入依赖

      <dependency>
         <groupId>log4j</groupId>
         <artifactId>log4j</artifactId>
         <version>1.2.17</version>
      </dependency>
    • 编写log4j.properties配置文件

      #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
      log4j.rootLogger=DEBUG,console,file
      ?
      #控制台输出的相关设置
      log4j.appender.console = org.apache.log4j.ConsoleAppender
      log4j.appender.console.Target = System.out
      log4j.appender.console.Threshold=DEBUG
      log4j.appender.console.layout = org.apache.log4j.PatternLayout
      log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
      ?
      #文件输出的相关设置
      log4j.appender.file = org.apache.log4j.RollingFileAppender
      log4j.appender.file.File=./log/hmx.log
      log4j.appender.file.MaxFileSize=10mb
      log4j.appender.file.Threshold=DEBUG
      log4j.appender.file.layout=org.apache.log4j.PatternLayout
      log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
      ?
      #日志输出级别
      log4j.logger.org.mybatis=DEBUG
      log4j.logger.java.sql=DEBUG
      log4j.logger.java.sql.Statement=DEBUG
      log4j.logger.java.sql.ResultSet=DEBUG
      log4j.logger.java.sql.PreparedStatement=DEBUG
    • 配置log4j为日志的实现

      <settings>
         <setting name="logImpl" value="LOG4J"/>
      </settings>
    • log4j的使用

      • 1、在要使用log4j的类中导入apache下的Logger,并获得日志对象:

        import org.apache.log4j.Logger
        ?
        static Logger logger = Logger.getLogger(当前类名.class);
      • 2、Logger中的方法

        logger.info("info:进入了log4j!");
        logger.debug("debug:进入了log4j!");
        logger.error("error:进入了log4j!");
      • 3、也可以改变一些日志的设置,如生成日志文件的位置、日志格式中的时间、日期

 

13、Mybatis缓存

  • 什么是缓存?

    • 可以进行高速数据交换的存储器,它先于内存与CPU交换数据,因此速率很快

    • 存在内存中的临时数据,

  • 为什么使用缓存?

    • 将用户经除查询的数据放在缓存中,用户去查询数据就不用从磁盘上(关系型数据库)查询,从而提高查询效率,解决了高并发系统的性能问题。

    • 减少和数据库的交互次数,减少系统开销,提高系统效率。

  • 什么样的数据可以使用缓存?

    • 经常查询并且不经常改变的数据

  • Mybatis缓存

    • 定义了一级缓存和二级缓存

    • 默认情况下,一级缓存开启(sqlSession级别的缓存,也称为本地缓存)

      • 只在一次sqlSession中有效,也就是从开启连接到关闭连接这个区间有效

    • 二级缓存需要手动开启和配置,它是基于namespace级别的缓存

      • 缓存接口cache,可以来定义二级缓存

    • 缓存的查询顺序

      • 二级缓存 ---> 一级缓存 ---> 数据库

  • Mybatis一级缓存

    • 与数据库同一次会话期间查询到的数据会存在缓存中

    • 测试

      • 开启日志

      • 测试:在一个sqlSession中查询两次相同记录,查询两次id为1的用户

        public static void selectById(){
               SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
               UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
               User user = userMapper.selectById(1);
               System.out.println(user);
        ?
               System.out.println("-----------------------------");
        ?
               User user2 = userMapper.selectById(1);
               System.out.println(user2);
        ?
               sqlSession.close();
          }
      • 查看日志输出

        Opening JDBC Connection
        Created connection 110771485.
        Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
        ==>  Preparing: select * from mybatis.mybatistest where id = ?;
        ==> Parameters: 1(Integer)
        <==    Columns: id, name, pwd
        <==        Row: 1, 洪梦霞, 123
        <==      Total: 1
        User(id=1, name=洪梦霞, pwd=123)
        -----------------------------
        User(id=1, name=洪梦霞, pwd=123)
        Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
        Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
        Returned connection 110771485 to pool.
           
        /*
        从打开链接,创造连接池,关闭连接,return连接到连接池,这些操作都在一次sqlSession中
        第一次查询时执行sql语句,从数据库中查询数据,
        第二次查询时直接在缓存中取数据
        */
    • 一级缓存失效的情况

      • 查询不同的数据

      • 增删改会刷新缓存,增删改操作有可能会改变原来的数据,所以会刷新缓存,缓存失效

      • 查询不同的Mapper.xml

      • 手动请理缓存:sqlSession.clearCache();

  • Mybatis二级缓存

    • 二级缓存工作机制

      一个会话查询一条数据,这个数据会被放在当前会话的一级缓存中

      如果当前会话关闭了(会话提交或者关闭会话),一级缓存消失,一级缓存中的数据会被转存到二级缓存中

      新的会话查询信息就可以从二级缓存中获取数据

      不同的mapper查出的数据会放在自己的缓存中

    • 开启二级缓存

      • 先在mybatis的核心配置文件中显示开启全局缓存

        <settings>
           <setting name="cacheEnabled" value="true"/>
        </settings>
      • 然后在某一个XXXMapper.xml中,也就是需要使用二级缓存的地方开启缓存

        <!--2.1-->
        <cache/>
        ?
        <!--2.2、也可使用一些参数-->
        <cache
          eviction="FIFO"
          flushInterval="60000"
          size="512"
          readOnly="true"/>
    • 测试

      • 开启日志

      • 开启二级缓存

      • 测试:在两个sqlSession中查询相同的数据

        public static void selectById(){
           SqlSession sqlSession1 = MybatisUtils.getSqlSession();
           SqlSession sqlSession2 = MybatisUtils.getSqlSession();
        ?
           UserMapper UserMapper1 = sqlSession1.getMapper(UserMapper.class);
           UserMapper UserMapper2 = sqlSession1.getMapper(UserMapper.class);
        ?
           User user1 = UserMapper1.selectById(1);
           System.out.println(user1);
        ?
           System.out.println("============================");
        ?
           User user2 = UserMapper2.selectById(1);
           System.out.println(user2);
        ?
           sqlSession1.close();
           sqlSession2.close();
        }
      • 查看日志输出

        Opening JDBC Connection
        Created connection 110771485.
        Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
        ==>  Preparing: select * from mybatis.mybatistest where id = ?;
        ==> Parameters: 1(Integer)
        <==    Columns: id, name, pwd
        <==        Row: 1, 洪梦霞, 123
        <==      Total: 1
        User(id=1, name=洪梦霞, pwd=123)
        ============================
        User(id=1, name=洪梦霞, pwd=123)
        Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
        Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
        Returned connection 110771485 to pool.
           
        /*
        开启二级缓存后,在一级缓存失效了,会使用二级缓存
        */
  • 自定义缓存-ehcache

    ehcache是一种广泛使用的开源Java分布式缓存

    主要面向通用缓存、Java EE和轻量级容器

    • 引入ehcache的依赖

      <dependency>
         <groupId>org.mybatis.caches</groupId>
         <artifactId>mybatis-ehcache</artifactId>
         <version>1.1.0</version>
      </dependency>
    • 在mapper.xml中使用对应的缓存

      <mapper namespace="com.hmx.mapper.UserMapper">
         <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
      </mapper>
    • 编写ehcache.xml文件

      注:如果在加载时未找到/ehcache.xml资源或出现问题,则将使用默认配置

      <?xml version="1.0" encoding="UTF-8"?>
      <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
              updateCheck="false">
         <!--
          diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
            user.home – 用户主目录
            user.dir – 用户当前工作目录
            java.io.tmpdir – 默认临时文件路径
          -->
         <diskStore path="./tmpdir/Tmp_EhCache"/>
         
         <!--
         defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
          -->
         <defaultCache
                 eternal="false"
                 maxElementsInMemory="10000"
                 overflowToDisk="false"
                 diskPersistent="false"
                 timeToIdleSeconds="1800"
                 timeToLiveSeconds="259200"
                 memoryStoreEvictionPolicy="LRU"/>

         <cache
                 name="cloud_user"
                 eternal="false"
                 maxElementsInMemory="5000"
                 overflowToDisk="false"
                 diskPersistent="false"
                 timeToIdleSeconds="1800"
                 timeToLiveSeconds="1800"
                 memoryStoreEvictionPolicy="LRU"/>
         
         <!--
         name:缓存名称。
         maxElementsInMemory:缓存最大数目
         maxElementsOnDisk:硬盘最大缓存个数。
         eternal:对象是否永久有效,一但设置了,timeout将不起作用。
         overflowToDisk:是否保存到磁盘,当系统当机时
         timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
         timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
         diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
         diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
         diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
         memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
         clearOnFlush:内存数量最大时是否清除。
         memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
         FIFO,first in first out,这个是大家最熟的,先进先出。
         LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
         LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
        -->
      ?
      </ehcache>

------------恢复内容开始------------

Mybatis

1、什么是Mybatis?

  • 历史

    • MyBatis 本是apache的一个开源项目iBatis

    • 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis

    • 2013年11月迁移到Github

  • 定义

    • 一个基于Java的持久层框架

    • 支持定制化 SQL、存储过程以及高级映射

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

    • 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和  Java 的 Pojos(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录

  • Mybatis相关资料

    • maven仓库

      <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis</artifactId>
         <version>3.5.2</version>
      </dependency>
    • Github

      Github地址

    • 官网文档

      官网中文文档地址

 

2、持久化和持久层

  • 持久化

    • 把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)

    • 持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等

    • 持久化是将程序数据在持久状态和瞬时状态间转换的机制

    • JDBC就是一种持久化机制,文件IO也是一种持久化机制

  • 持久层

    • 完成持久化工作的代码块

 

3、第一个Mybatis程序

  • 环境搭建

    • 建数据库以及表

    • 新建Maven项目后在pom.xml中导入依赖

      <!--mysql依赖-->
      <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>5.1.47</version>
      </dependency>
      ?
      <!--mybatis依赖-->
      <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis</artifactId>
         <version>3.5.2</version>
      </dependency>
      ?
      <!--Lombok依赖-->
      <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
         <version>1.18.12</version>
      </dependency>
  • 编写Mybatis配置文件:mybatis-config.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
           PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
           "http://mybatis.org/dtd/mybatis-3-config.dtd">
    ?
    <!--configuration核心配置文件-->
    <configuration>
       
       <!--可以配置多种环境,如开发、测试,default指定用的是哪种环境-->
       <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?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                   <property name="username" value="root"/>
                   <property name="password" value="root"/>
               </dataSource>
           </environment>
       </environments>
    </configuration>
  • 编写Mybatis工具类

    • 1、构建SqlSessionFactory

      • 每个基于Mybatis的应用都是以一个SqlSessionFactory的实例为核心的

      • SqlSessionFactoryBuilder可以从XML配置文件或者一个预先定制的Configuration的实例构建出SqlSessionFactory的实例

        • 使用任意的输入流(InputStream)实例,通过字符串形式的文件路径,或者url形式的文件路径配置

        • Resources工具类可使从classpath或其他位置加载资源文件更加容易

    • 2、从SqlSessionFactory中获取SqlSession

      • SqlSession 提供了在数据库执行 SQL 命令所需的所有方法

      • SqlSession 实例来直接执行已映射的 SQL 语句

    • SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession 的生命周期和作用域

      • SqlSessionFactoryBuilder

        • 一旦创建了 SqlSessionFactory,就不再需要SqlSessionFactoryBuilder了

        • 最佳作用域是方法作用域(也就是局部方法变量)

      • SqlSessionFactory

        • 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃SqlSessionFactory或重新创建另一个实例

        • 最佳作用域是应用作用域,最简单的就是使用单例模式或者静态单例模式

      • SqlSession

        • 每个线程都应该有它自己的 SqlSession 实例

        • 最佳的作用域是请求或方法作用域

    public class MybatisUtils {
    ?
       private static SqlSessionFactory sqlSessionFactory;
    ?
       static {
           try {
               //使用mybatis第一步:获取sqlSessionFactory对象
               String resource = "mybatis-config.xml";
               InputStream inputStream = Resources.getResourceAsStream(resource);
               sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
          } catch (IOException e) {
               e.printStackTrace();
          }
      }
    ?
       //从 SqlSessionFactory 中获取 SqlSession
       public static SqlSession getSqlSession(){
           return sqlSessionFactory.openSession();
      }
    }
  • 编写代码

    • 实体类

      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class User {
      ?
         private Integer id;
         private String name;
         private String pwd;
      }
    • Dao接口,UserMapper

      public interface UserMapper {
         List<User> getUserList();
      }
    • 接口实现类,UserMapper.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">
      ?
      <!--namespace,绑定一个对应的mapper接口-->
      <mapper namespace="com.hmx.dao.UserMapper">
         
         <!--
      id:sql语句对应的执行的方法名
      resultType:返回结果,要写全限命名
      -->
         <select id="getUserList" resultType="com.hmx.pojo.User">
            select * from mybatis.mybatistest
         </select>
      </mapper>
    • 在mybatis的配置文件中给每个Mapper.xml配置mapper

      <!--每一个Mapper.xml都需要在核心配置文件中注册-->
      <mappers>
         <mapper resource="com/hmx/dao/UserMapper.xml"/>
      </mappers>
    • 测试

      public class UserMapperTest {
         public static void main(String[] args) {
      ?
             //第一步:获得SqlSession对象
             SqlSession sqlSession = MybatisUtils.getSqlSession();
      ?
             //获得mapper,执行sql语句
             UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
             List<User> userList = userMapper.getUserList();
      ?
             //输出结果
             for (User user : userList) {
                 System.out.println(user);
            }
             
             //关闭SqlSession
             sqlSession.close();
        }
      }
    • 注意点:常见错误

      • Type interface com.hmx.dao.UserDao is not known to the MapperRegistry

        原因:Mapper.xml没有在核心配置文件中注册

        解决:在mybatis的配置文件中给每个Mapper.xml配置mapper

        <!--每一个Mapper.xml都需要在核心配置文件中注册-->
        <mappers>
           <mapper resource="com/hmx/dao/UserMapper.xml"/>
        </mappers>
      • Could not find resource com/hmx/dao/UserMapper.xml

        原因:资源导入失败,Maven导出资源问题

        解决:在pom.xml中配置如下代码

        <build>
           <resources>
               <resource>
                   <directory>src/main/resources</directory>
                   <includes>
                       <include>**/*.properties</include>
                       <include>**/*.xml</include>
                   </includes>
                   <filtering>true</filtering>
               </resource>
               <resource>
                   <directory>src/main/java</directory>
                   <includes>
                       <include>**/*.properties</include>
                       <include>**/*.xml</include>
                   </includes>
                   <filtering>true</filtering>
               </resource>
           </resources>
        </build>
      • com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 1 字节的 UTF-8 序列的字节 1 无效

        原因:xml编码问题

        解决:

        1、在pom.xml中配置如下代码

        <properties>
           <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>

        若方法1没用,参考

        2、把每个xml文件的文件头

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

        改为:

        <?xml version="1.0" encoding="UTF8" ?>

 

4、增删改查

  • 实体类:User

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
    ?
       private Integer id;
       private String name;
       private String pwd;
    }
  • 接口类:UserMapper

    public interface UserMapper {
    ?
       //查询所有数据
       List<User> getUserList();
    //根据id查询数据
       User selectById(int id);
    //增加数据
       int insertUser(User user);
    //更新数据
       int updateUser(User user);
    //删除数据
       int deleteUser(int id);
    }
  • 接口类的实现UserMapper.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">
    <!--namespace绑定一个mapper接口-->
    <mapper namespace="com.hmx.dao.UserMapper">
    ?
       <select id="getUserList" resultType="com.hmx.model.User">
          select * from mybatis.mybatistest;
       </select>
    ?
       <select id="selectById" parameterType="int" resultType="com.hmx.model.User">
          select * from mybatis.mybatistest where id = #{id};
       </select>
    ?
       <insert id="insertUser" parameterType="com.hmx.model.User">
          insert into mybatis.mybatistest (id,name,pwd) values (#{id},#{name},#{pwd});
       </insert>
    ?
       <update id="updateUser" parameterType="com.hmx.model.User">
          update mybatis.mybatistest set name = #{name},pwd = #{pwd} where id = #{id};
       </update>
    ?
       <delete id="deleteUser">
          delete from mybatis.mybatistest where id = #{id};
       </delete>
    </mapper>
  • 测试

    //查询所有数据
    public static void selectAll(){
    ?
       //第一步:获得SqlSession对象
       SqlSession sqlSession = MybatisUtils.getSqlSession();
       //执行sal语句
       UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
       List<User> userList = userMapper.getUserList();
       for (User user : userList) {
           System.out.println(user);
      }
       //关闭SqlSession
       sqlSession.close();
    }
    ?
    //根据id查询数据
    public static void selectById(){
       SqlSession sqlSession = MybatisUtils.getSqlSession();
    ?
       UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
       User user = userMapper.selectById(1);
       System.out.println(user);
    ?
       sqlSession.close();
    }
    ?
    //添加数据
    public static void insertUser(){
       SqlSession sqlSession = MybatisUtils.getSqlSession();
    ?
       UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
       userMapper.insertUser(new User(9,"大黄","123"));
       sqlSession.commit();
    ?
       sqlSession.close();
    }
    ?
    //修改数据
    public static void updateUser(){
       SqlSession sqlSession = MybatisUtils.getSqlSession();
    ?
       UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
       userMapper.updateUser(new User(4,"大黑子","333"));
       sqlSession.commit();
    ?
       sqlSession.close();
    }
    ?
    //删除数据
    public static void deleteUser(){
       SqlSession sqlSession = MybatisUtils.getSqlSession();
    ?
       UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
       userMapper.deleteUser(9);
       sqlSession.commit();
    ?
       sqlSession.close();
    }

 

5、注解实现增删改查

  • Mybatis注解作用:

    • 使用注解来映射简单语句会使代码显得更加简洁

    • 但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪

    • 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句

  • mybatis注解本质

    • 本质:反射机制

    • 底层:动态代理模式

  • 注意点:

    • 注解写在接口类中的方法上

    • 使用注解开发不需要写接口的实现类(XXXMapper.xml)文件,所以在配置文件中需要绑定XXXMapper.java

      <mappers>
         <mapper class="com.hmx.mapper.UserMapper"/>
      </mappers>
  • @Param()注解

    • 方法中只有一个参数,可加可不加,最好加,多个参数必须加

    • 基本类型和String类型需要加上

    • 和sql语句中引用的#{}相匹配

  • 增删改查

    public interface UserMapper {
    ?
       //查询所有数据
       @Select("select * from mybatis.mybatistest")
       List<User> getUserList();
       
       //根据id查询数据
       @Select("select * from mybatis.mybatistest where id = #{id}")
       User selectById(@Param("id") int id);
       
       //增加数据
       @Insert("insert into mybatis.mybatistest (id,name,pwd) values (#{id},#{name},#{pwd})")
       int insertUser(User user);
       
       //更新数据
       @Update("update mybatis.mybatistest set name = #{name},pwd = #{pwd} where id = #{id}")
       int updateUser(User user);
       
       //删除数据
       @Delete("delete from mybatis.mybatistest where id = #{id};")
       int deleteUser(@Param("id") int id);
    }

 

6、Mybatis配置文件解析

configuration核心配置文件中包含了会深深影响 MyBatis 行为的设置和属性信息,可以配置如下配置:

  • properties(属性)

    • 可以直接引入外部文件

      <properties resource="db.properties"/>
      #db.properties
      driver=com.mysql.jdbc.Driver
      url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
      username=root
      password=root
    • 如果db.properties中没有配置username和password,则可以可以在properties中增加一些属性配置

      <properties resource="db.properties">
         <property name="username" value="root"/>
         <property name="password" value="root"/>
      </properties>
    • 如果两个配置文件和properties中存在同一字段,如:都含有username这个字段但是值不一样,优先使用外部配置文件

  • settings(设置)

    • 一个配置完整的settings元素的示例如下:

      <settings>
      1、<!--全局地开启或关闭所有映射器配置文件中已配置的任何缓存,有效值true|false,默认true-->
       <setting name="cacheEnabled" value="true"/>
         
      2、<!--
            延迟加载的全局开关。
            当开启时,所有关联对象都会延迟加载。
            特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。
            有效值true|false,默认false
      -->
       <setting name="lazyLoadingEnabled" value="true"/>
         
      3、<!--是否允许单个语句返回多结果集(需要数据库驱动支持),有效值true|false,默认true-->
       <setting name="multipleResultSetsEnabled" value="true"/>
         
      4、<!--
         使用列标签代替列名
      实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察
      有效值true|false,默认true
      -->
       <setting name="useColumnLabel" value="true"/>
         
      5、<!--
      允许 JDBC 支持自动生成主键,需要数据库驱动支持
      如果设置为 true,将强制使用自动生成主键
      尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)
      有效值true|false,默认false
      -->
       <setting name="useGeneratedKeys" value="false"/>
         
      6、<!--
      指定 MyBatis 应如何自动映射列到字段或属性。
      NONE 表示关闭自动映射
      PARTIAL 只会自动映射没有定义嵌套结果映射的字段
      FULL 会自动映射任何复杂的结果集(无论是否嵌套)
      默认值为PARTIAL
      -->
       <setting name="autoMappingBehavior" value="PARTIAL"/>
         
      7、<!--
      指定发现自动映射目标未知列(或未知属性类型)的行为。
      NONE: 不做任何反应
      WARNING: 输出警告日志
      FAILING: 映射失败 (抛出 SqlSessionException)
      默认值为NONE
      -->
       <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
         
      8、<!--
      配置默认的执行器
      SIMPLE 就是普通的执行器
      REUSE 执行器会重用预处理语句(PreparedStatement)
      BATCH 执行器不仅重用语句还会执行批量更新
      默认值为SIMPLE-->
       <setting name="defaultExecutorType" value="SIMPLE"/>
         
      9、<!--
      设置超时时间,它决定数据库驱动等待数据库响应的秒数
      有效值为任意正整数 默认未设置 (null)
      -->
       <setting name="defaultStatementTimeout" value="25"/>
         
      10、<!--
      为驱动的结果集获取数量(fetchSize)设置一个建议值,此参数只可以在查询设置中被覆盖。
      有效值为任意正整数 默认未设置 (null)
      -->
       <setting name="defaultFetchSize" value="100"/>
         
      11、<!--
      是否允许在嵌套语句中使用分页(RowBounds),如果允许使用则设置为 false
      有效值true|false,默认false
      -->
       <setting name="safeRowBoundsEnabled" value="false"/>
         
      12、<!--
      是否开启驼峰命名自动映射,即从经典数据库列名A_COLUMN映射到经典Java属性名aColumn
      有效值true|false,默认false
      -->
       <setting name="mapUnderscoreToCamelCase" value="false"/>
       
      13、<!--
      MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询
      SESSION,会缓存一个会话中执行的所有查询
      STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存 默认值为SESSION
      -->
       <setting name="localCacheScope" value="SESSION"/>
         
      14、<!--
      当没有为参数指定特定的JDBC类型时,空值的默认JDBC类型
      某些数据库驱动需要指定列的JDBC类型,多数情况直接用一般类型即可
      有效值为JdbcType常量,常用值:NULL、VARCHAR或OTHER,默认OTHER
      -->
       <setting name="jdbcTypeForNull" value="OTHER"/>
         
      15、<!--
      指定对象的哪些方法触发一次延迟加载。
      有效值为用逗号分隔的方法列表:equals,clone,hashCode,toString
      -->
       <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
      </settings>
    • 具体可以查看mybatis官网中关于settings的配置解释

      mybatis中文文档 - settings

  • typeAliases(类型别名)

    • 降低冗余的全限定类名书写

    • 给具体某个类设置别名

      适合实体类不是特别多的情况

      <typeAliases>
         <typeAlias type="com.hmx.model.User" alias="User"/>
      </typeAliases>
      ?
      <!--设置别名后可以直接在resultType里写别名即可-->
      <select id="" resultType="User">
      </select>
    • 给包下所有类设置别名,默认类名首字母小写为别名,可在类上加@Alise("aaa")注解指定别名

      适合实体类特别多的情况

      <typeAliases>
         <package name="com.hmx.model"/>
      </typeAliases>
  • environments(环境配置)

    • MyBatis 可以配置成适应多种环境,每个 SqlSessionFactory 实例只能选择一种环境

    • transactionManager(事务管理器),type="[JDBC|MANAGED]"

    • dataSource(数据源),type="[UNPOOLED|POOLED|JNDI]

      <environments default="development">
         <environment id="development">
             <transactionManager type="JDBC"/>
             <dataSource type="POOLED">
             </dataSource>
         </environment>
      </environments>
  • mappers(映射)

    • 方式一:

      <mappers>
         <mapper resource="com/hmx/dao/UserMapper.xml"/>
      </mappers>
    • 方式二:mapper接口和mapper.xml名字必须相同,而且必须放在一个包下

      <mappers>
         <mapper class="com.hmx.dao.UserMapper"/>
      </mappers>
    • 方式三:mapper接口和mapper.xml名字必须相同,而且必须放在一个包下

      <mappers>
         <package name="com.hmx.dao"/>
      </mappers>
  • typeHandlers(类型处理器)

  • objectFactory(对象工厂)

  • plugins(插件)

  • databaseIdProvider(数据库厂商标识)

 

注:本文往后都使用了别名配置

 

7、Map妙用

  • 通过map可以不用知道数据库中表有几列,写sql语句时也不用把对象中所有的属性都写一遍

  • 假设我们的数据库中的表的字段过多,可以考虑使用map

    • 示例一:Map查询数据

      • 接口类

        User selectByIdAndName(Map<String,Object> map);
      • 接口类的实现

        <select id="selectByIdAndName" parameterType="map"                  resultType="User">
          select * from mybatistest where id = #{Id} and name = #{Name};
        </select>
      • 测试

        public static void selectByIdAndName(){
           SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
           UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
           Map<String,Object> map = new HashMap<String, Object>();
           map.put("Id",1);
           map.put("Name","洪梦霞");
           User user = userMapper.selectByIdAndName(map);
           System.out.println(user);
        ?
           sqlSession.close();
        }
    • 示例二:Map插入数据

      • 接口类

        int insertUser(Map<String,Object> map);
      • 接口类的实现

        <insert id="insertUser" parameterType="map">
          insert into mybatistest (id,name,pwd) values (#{mapId},#{mapName},#{mapPwd});
        </insert>
      • 测试

        public static void insertUser(){
           SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
           UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
           Map<String, Object> map = new HashMap<String, Object>();
           map.put("mapId",4);
           map.put("mapName","小叶子");
           map.put("mapPwd","985");
           userMapper.insertUser(map);
           sqlSession.commit();
        ?
           sqlSession.close();
        }

 

8、模糊查询

  • 接口类

    List<User> selectByParameter(String value);
  • 实现方式一

    • 接口类的实现

      <select id="selectByParameter" resultType="User">
        select * from mybatistest where name like #{value};
      </select>
    • 测试

      public static void selectByParameter(){
         SqlSession sqlSession = MybatisUtils.getSqlSession();
      ?
         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
         List<User> userList = userMapper.selectByParameter("%小%");
         for (User user : userList) {
             System.out.println(user);
        }
      ?
         sqlSession.close();
      }
  • 实现方式二

    • 接口类的实现

      <select id="selectByParameter" resultType="User">
        select * from mybatistest where name like "%"#{value}"%";
      </select>
    • 测试

      public static void selectByParameter(){
         SqlSession sqlSession = MybatisUtils.getSqlSession();
      ?
         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
         List<User> userList = userMapper.selectByParameter("小");
         for (User user : userList) {
             System.out.println(user);
        }
      ?
         sqlSession.close();
      }

 

9、分页

  • 实体类:User

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
    ?
       private Integer id;
       private String name;
       private String pwd;
    }
  • 方式一:使用limit实现分页

    • 接口类:UserMapper

      public interface UserMapper {
         List<User> selectLimit(Map<String,Integer> map);
      }
    • 接口的实现类:UserMapper.xml

      <?xml version="1.0" encoding="UTF8" ?>
      <!DOCTYPE mapper
             PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <mapper namespace="com.hmx.mapper.UserMapper">
      ?
         <select id="selectLimit" parameterType="map" resultType="User">
            select * from mybatistest limit #{startIndex},#{pageSize};
         </select>
      </mapper>
    • 测试

      public static void selectLimit(){
         
         SqlSession sqlSession = MybatisUtils.getSqlSession();
         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
         
         HashMap<String, Integer> map = new HashMap<String, Integer>();
         map.put("startIndex",1);
         map.put("pageSize",3);
         List<User> userList = userMapper.selectLimit(map);
         
         for (User user : userList) {
             System.out.println(user);
        }
      ?
         sqlSession.close();
      }
  • 方式二:使用RowBounds实现分页

    • 接口类:UserMapper

      public interface UserMapper {
         List<User> selectRowBounds();
      }
    • 接口的实现类:UserMapper.xml

      <?xml version="1.0" encoding="UTF8" ?>
      <!DOCTYPE mapper
             PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <mapper namespace="com.hmx.mapper.UserMapper">
      ?
         <select id="selectRowBounds" parameterType="map" resultType="User">
            select * from mybatistest
         </select>
      </mapper>
    • 测试

          public static void selectRowBounds(){
             SqlSession sqlSession = MybatisUtils.getSqlSession();
      ?
             /*
             RowBounds(int offset, int limit)
             RowBounds(开始位置, 查询个数)
             */
             RowBounds rowBounds = new RowBounds(1, 2);
             
             /*
             selectList(String var1, Object var2, RowBounds var3)
             selectList(接口类的全限定名.方法名,一般写null即可, RowBounds对象)
             */
             List<User> userList = sqlSession.selectList("com.hmx.mapper.UserMapper.selectRowBounds", "null", rowBounds);
             
             for (User user : userList) {
                 System.out.println(user);
            }
      ?
             sqlSession.close();
        }
  • 方式三:使用分页插件PageHelper进行分页

    如需使用,可参考以下链接:

    如何使用分页插件PageHelper

 

10、ResultMap结果集映射

  • 作用

    • MyBatis 中最重要最强大的元素

    • 解决字段名和属性名不一致的问题

    • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系

  • ResultMap配置详解

    <!--
    id为这个resultMap的标识
    type为映射的类型,一个Java类的全限定名,或一个类型别名
    -->
    <resultMap id="" type="">
       <!--
           id和result都将一个列的值映射到一个简单数据类型的属性或字段
          column:对应数据库中的列
          property:对应实体类中的属性
          javaType:一个Java类的全限定名,或一个类型别名,结果集映射返回的结果
          jdbcType:所支持的JDBC类型
          typeHandler:类型处理器
       -->
       <id column="" property="" javaType="" jdbcType="" typeHandler=""></id>
       <result column="" property="" javaType="" jdbcType="" typeHandler=""></result>
       <!--
           association处理“有一个”类型的关系
           collection处理“有多个”类型的关系
               column:对应数据库中的列
               property:对应实体类中的属性(对象)
               javaType:指定实体类中属性的类型
               ofType:指定映射到集合中的实体类类型,泛型中的约束类型
               select:用于加载复杂类型属性的映射语句的id
                       会从column属性指定的列中检索数据,作为参数传递给目标select语句
       -->
       <association column="" property="" javaType="" select=""></association>
       <collection column="" property="" javaType="" select=""></collection>
    </resultMap>
  • 1、解决数据库中的字段和实体类中属性不一致

    • 实体类

      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class User {
      ?
         private Integer id;
         private String name;
         private String password;
      }
    • 接口类

      User selectById(int id);
    • 接口类的实现

      <resultMap id="UserMap" type="user">
         <!--
      id,name属性和数据库中的字段一样,所有不需要显式写出,可省略
        <result column="id" property="id"/>
        <result column="name" property="name"/>
      -->
         <result column="pwd" property="password"/>
      </resultMap>
      ?
      <!--resultMap里的值为结果集映射<resultMap></resultMap>的id值-->
      <select id="selectById" resultMap="UserMap">
        select * from mybatistest where id = #{id};
      </select>
  • 2、多对一以及一对多问题

    • Teacher表

      id:老师id
      name:老师姓名
    • Student表

      id:学生id
      name:学生姓名
      tid:学生关联的老师id
    • 多对一:多个学生对应一个老师,查询学生的信息以及学生的老师的信息

      • 1、老师类:Teacher

        @Data
        public class Teacher {
           private int id;
           private String name;
        }
      • 2、老师的接口类:TeacherMapper

        public interface TeacherMapper {
        }
      • 3、老师接口类的实现:TeacherMapper.xml

        <?xml version="1.0" encoding="UTF8" ?>
        <!DOCTYPE mapper
               PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
               "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
        ?
        <mapper namespace="com.hmx.mapper.TeacherMapper">
        </mapper>
      • 4、学生类:Student

        @Data
        public class Student {
        ?
           private int id;
           private String name;
           private Teacher teacher;
        }
      • 5、学生的接口类:StudentMapper

        public interface StudentMapper {
           List<Student> getStudent();
        }
      • 6、学生接口类的实现:StudentMapper.xml

        • 方法一:根据查询嵌套处理,子查询

          <?xml version="1.0" encoding="UTF8" ?>
          <!DOCTYPE mapper
                 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
          ?
          <mapper namespace="com.hmx.mapper.StudentMapper">
             
             <!--查询所有的学生信息-->
             <select id="getStudent" resultMap="StudentTeacher">
                select * from student
             </select>
          ?
             <!--
          根据查询出来的学生的tid属性,查询老师的信息
          tid的值是从resultMap中传递过来的
          -->
             <select id="getTeacher" resultType="Teacher">
                select * from teacher where id = #{tid}
             </select>
             
             <resultMap id="StudentTeacher" type="Student">
                 <result property="id" column="id"/>
                 <result property="name" column="name"/>
                 
                 <!--从tid列中检索数据,作为参数传递给id为getTeacher的select语句-->
                 <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
             </resultMap>
          </mapper>
        • 方法二:根据结果嵌套处理,关联查询

          <?xml version="1.0" encoding="UTF8" ?>
          <!DOCTYPE mapper
                 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
          ?
          <mapper namespace="com.hmx.mapper.StudentMapper">
             
             <select id="getStudent" resultMap="StudentTeacher">
                select s.id as sid,s.`name` as sname,t.`name` tname
                from student s,teacher t
                where s.tid = t.id
             </select>
          ?
             <resultMap id="StudentTeacher" type="Student">
                 <result property="id" column="sid"/>
                 <result property="name" column="sname"/>
                 <association property="teacher" javaType="Teacher">
                     <result property="name" column="tname"/>
                 </association>
             </resultMap>
          ?
          </mapper>
      • 7、测试

        public static void selectAll() {
           SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
           StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
           List<Student> studentList = studentMapper.getStudent();
           for (Student student : studentList) {
               System.out.println(student);
          }
        ?
           sqlSession.close();
        }

     

    • 一对多:一个老师有多个学生,查询指定老师下的学生的信息,以及该老师的信息

      • 1、学生类:Student

        @Data
        public class Student {
        ?
           private int id;
           private String name;
           private int tid;
        }
      • 2、学生的接口类:StudentMapper

        public interface StudentMapper {
        }
      • 3、学生接口类的实现类:StudentMapper.xml

        <?xml version="1.0" encoding="UTF8" ?>
        <!DOCTYPE mapper
               PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
               "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
        ?
        <mapper namespace="com.hmx.mapper.StudentMapper">
        </mapper>
      • 4、老师类:Teacher

        @Data
        public class Teacher {
        ?
           private int id;
           private String name;
           private List<Student> studentList;
        }
      • 5、老师的接口类:TeacherMapper

        public Teacher getTeacher(int id);
      • 6、老师接口类的实现类:TeacherMapper.xml

        • 方法一:根据查询嵌套处理,子查询

          <?xml version="1.0" encoding="UTF8" ?>
          <!DOCTYPE mapper
                 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
          <mapper namespace="com.hmx.mapper.TeacherMapper">
          ?
             <!--查询老师的所有信息-->
             <select id="getTeacher" resultMap="TeacherStudent">
                select * from teacher where id = #{id}
             </select>
          ?
             <!--根据老师的id查询学生的所有信息-->
             <select id="getStudentByTeacherId" resultType="Student">
                select * from student where tid = #{tid}
             </select>
          ?
             <!--把老师里的id传给select查询块-->
             <resultMap id="TeacherStudent" type="Teacher">
                 <collection property="studentList" column="id" javaType="ArrayList" ofType="student" select="getStudentByTeacherId">
                 </collection>
             </resultMap>
          ?
          </mapper>
        • 方法二:根据结果嵌套处理,关联查询

          <?xml version="1.0" encoding="UTF8" ?>
          <!DOCTYPE mapper
                 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
          <mapper namespace="com.hmx.mapper.TeacherMapper">
          ?
             <select id="getTeacher" resultMap="TeacherStudent">
                SELECT
                s.id AS sid,
                s.`name` AS sname,
                    t.id AS tid,
                t.`name` tname
                FROM
                student s,
                teacher t
                    WHERE
                s.tid = t.id AND t.id = #{tid}
             </select>
          ?
             <resultMap id="TeacherStudent" type="Teacher">
                 <result property="id" column="tid"/>
                 <result property="name" column="tname"/>
                 
                 <collection property="studentList" ofType="Student">
                     <result property="id" column="sid"/>
                     <result property="name" column="sname"/>
                     <result property="tid" column="tid"/>
                 </collection>
             </resultMap>
          ?
          </mapper>
      • 7、测试

        public static void testTeacher() {
           SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
           TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);
           Teacher teacher = teacherMapper.getTeacher(1);
           System.out.println(teacher);
        ?
           sqlSession.close();
        }

 

11、动态sql

  • 定义

    根据不同的条件生成不同的sql语句,本质还是sql语句,只是我们可以在sql层面执行逻辑代码

  • 环境搭建

    • blog表

      id
      title
      author
      creat_time
      views
    • 实体类:Blog

      @Data
      public class Blog {
      ?
         private String id;
         private String title;
         private String author;
         private Date creatTime;
         private int views;
      }
    • 配置文件

      <configuration>
      ?
         <settings>
             <setting name="logImpl" value="STDOUT_LOGGING"/>
             <!--开启驼峰命名-->
             <setting name="mapUnderscoreToCamelCase" value="true"/>
         </settings>
      ?
         <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?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                     <property name="username" value="root"/>
                     <property name="password" value="root"/>
                 </dataSource>
             </environment>
         </environments>
      ?
         <mappers>
             <mapper class="com.hmx.mapper.BlogMapper"/>
         </mappers>
      ?
      </configuration>
  • if

    • 这条语句提供了可选的查找文本功能

    • 传入哪个就按哪个查找,如果不传入,就按select里的原始sql语句查找

    • 测试

      • 接口类

        public interface BlogMapper {
           List<Blog> queryBlogIf(Map map);
        }
      • 接口类的实现类

        <mapper namespace="com.hmx.mapper.BlogMapper">
        ?
           <select id="queryBlogIf" parameterType="map" resultType="Blog">
              select * from blog
               <!--
        where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句
        而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除
        -->
               <where>
                   <!--
        test里面是判断条件
        表达式为true,把if中的语句拼接到select中的sql上
        表达式为false,忽略此语句
        -->
                   <if test="title != null">
                      and title = #{title}
                   </if>
                   <if test="author !=null" >
                      and author = #{author}
                   </if>
               </where>
           </select>
        ?
        </mapper>
      • 测试实现

        public static void queryBlogIf() {
        ?
           SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
           BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
           HashMap map = new HashMap();
           //查询title为"Mybatis如此简单"的记录
           map.put("title", "Mybatis如此简单");
           
           //查询author为"Andy"的记录
           //map.put("author","Andy");
        ?
           List<Blog> blogs = blogMapper.queryBlogIf(map);
           for (Blog blog : blogs) {
               System.out.println(blog);
          }
        ?
           sqlSession.close();
        }
  • choose,when,otherwise

    • 从多个条件中选择一个使用

    • 传入了哪个就按哪个查找,第一个when优先级最高,都没有传入,就按otherwise里的查找

    • 测试

      • 接口类

        public interface BlogMapper {
           List<Blog> queryBlogChoose(Map map);
        }
      • 接口的实现类

        <mapper namespace="com.hmx.mapper.BlogMapper">
        ?
           <select id="queryBlogChoose" parameterType="map" resultType="Blog">
              select * from blog
               <where>
                   <choose>
                       <when test="title != null">
                          title = #{title}
                       </when>
                       <when test="author !=null">
                          and author = #{author}
                       </when>
                       <otherwise>
                          and views = #{views}
                       </otherwise>
                   </choose>
               </where>
           </select>
        ?
        </mapper>
      • 测试实现

        public static void queryBlogChoose() {
        ?
           SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
           BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
           
           HashMap hashMap = new HashMap();
           hashMap.put("title", "Mybatis如此简单");
           //hashMap.put("author","洪梦霞");
           //hashMap.put("views","888");
           
           List<Blog> blogs = blogMapper.queryBlogChoose(hashMap);
           for (Blog blog : blogs) {
               System.out.println(blog);
          }
        ?
           sqlSession.close();
        }
  • trim,where,set

    • where

      • where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句,

        而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

      • 加入trim定制where元素的功能

        <!--拼接sql语句时会加上WHERE,并且删除多余的AND和OR-->
        <trim prefix="WHERE" prefixOverrides="AND |OR ">
        ...
        </trim>
    • set

      • set 元素可以用于动态包含需要更新的列,忽略其它不更新的列,它会动态地在行首插入 SET 关键字,并会删掉额外的逗号,在update语句中会用到

      • 加入trim定制set元素的功能

        <!--拼接sql语句时会加上SET,并且删除多余的","-->
        <trim prefix="SET" suffixOverrides=",">
        ...
        </trim>
  • foreach

    • 对集合进行遍历(尤其是在构建 IN 条件语句的时候)

    • 测试:select * from blog where 1 = 1 and (id = 1 or id = 2 or id = 3)

      • 接口类

        public interface BlogMapper {
           List<Blog> queryBlogForEach(Map map);
        }
      • 接口的实现类

        <mapper namespace="com.hmx.mapper.BlogMapper">
           
           <select id="queryBlogForEach" parameterType="map" resultType="Blog">
              select * from blog
               <where>
                   <!--
                       从collection这个集合中遍历,是map中存在的集合
                       遍历出的每一项叫做item中的值
                       index为下标
                       开始为open里的值
                       结束为close里的值
                       separator为分隔符
                   -->
                   <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                      id = #{id}
                   </foreach>
               </where>
           </select>
        ?
        </mapper>
      • 测试

        public static void queryBlogForEach() {
        ?
           SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
           BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
           HashMap map = new HashMap();
           
           ArrayList<Integer> ids = new ArrayList<Integer>();
           ids.add(1);
           ids.add(2);
           ids.add(3);
           map.put("ids", ids);
           
           List<Blog> blogs = blogMapper.queryBlogForEach(map);
           for (Blog blog : blogs) {
               System.out.println(blog);
          }
        ?
           sqlSession.close();
        }
  • sql片段

    • 将一些公共的功能抽取出来,方便复用

    • 注意:

      • 最好基于单表来定义SQL片段

      • 不要存在where标签

    • 使用步骤

      • 1、使用sql标签抽取公共的部分

        <sql id="if-title-author">
           <if test="title != null">
              title = #{title}
           </if>
           <if test="author !=null" >
              and author = #{author}
           </if>
        </sql>
      • 2、include标签引用

        <include refid="if-title-author"></include>

 

12、日志

  • 日志工厂

    Mybatis内置了许多的日志工厂,可以在settings中设置

    <!--
    logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找
           SLF4J
           LOG4J(重点)
           LOG4J2
           JDK_LOGGING
           COMMONS_LOGGING
           STDOUT_LOGGING(重点)
           NO_LOGGING
    -->
  • STDOUT_LOGGING:标准日志输出

    • 在核心配置文件中配置标准日志输出

      <settings>
         <setting name="logImpl" value="STDOUT_LOGGING"/>
      </settings>
    • 控制台输出

      Logging initialized using ‘class org.apache.ibatis.logging.stdout.StdOutImpl‘ adapter.
      PooledDataSource forcefully closed/removed all connections.
      PooledDataSource forcefully closed/removed all connections.
      PooledDataSource forcefully closed/removed all connections.
      PooledDataSource forcefully closed/removed all connections.
      Opening JDBC Connection
      Created connection 418304857.
      Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@18eed359]
      ==>  Preparing: select * from mybatis.mybatistest
      ==> Parameters:
      <==    Columns: id, name, pwd
      <==        Row: 1, 洪梦霞, 123
      <==        Row: 2, 王禹, 222
      <==        Row: 3, 小李子, 008
      <==        Row: 4, 小叶子, 985
      <==      Total: 4
      User(id=1, name=洪梦霞, pwd=123)
      User(id=2, name=王禹, pwd=222)
      User(id=3, name=小李子, pwd=008)
      User(id=4, name=小叶子, pwd=985)
      Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@18eed359]
      Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@18eed359]
      Returned connection 418304857 to pool.
  • LOG4J

    • 什么是log4j?

      • 可以控制日志信息输送的目的地是控制台或文件或GUI组件

      • 可以控制每一条日志的输出格式

      • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程

      • 可以通过一个配置文件来灵活地进行配置

    • 导入依赖

      <dependency>
         <groupId>log4j</groupId>
         <artifactId>log4j</artifactId>
         <version>1.2.17</version>
      </dependency>
    • 编写log4j.properties配置文件

      #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
      log4j.rootLogger=DEBUG,console,file
      ?
      #控制台输出的相关设置
      log4j.appender.console = org.apache.log4j.ConsoleAppender
      log4j.appender.console.Target = System.out
      log4j.appender.console.Threshold=DEBUG
      log4j.appender.console.layout = org.apache.log4j.PatternLayout
      log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
      ?
      #文件输出的相关设置
      log4j.appender.file = org.apache.log4j.RollingFileAppender
      log4j.appender.file.File=./log/hmx.log
      log4j.appender.file.MaxFileSize=10mb
      log4j.appender.file.Threshold=DEBUG
      log4j.appender.file.layout=org.apache.log4j.PatternLayout
      log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
      ?
      #日志输出级别
      log4j.logger.org.mybatis=DEBUG
      log4j.logger.java.sql=DEBUG
      log4j.logger.java.sql.Statement=DEBUG
      log4j.logger.java.sql.ResultSet=DEBUG
      log4j.logger.java.sql.PreparedStatement=DEBUG
    • 配置log4j为日志的实现

      <settings>
         <setting name="logImpl" value="LOG4J"/>
      </settings>
    • log4j的使用

      • 1、在要使用log4j的类中导入apache下的Logger,并获得日志对象:

        import org.apache.log4j.Logger
        ?
        static Logger logger = Logger.getLogger(当前类名.class);
      • 2、Logger中的方法

        logger.info("info:进入了log4j!");
        logger.debug("debug:进入了log4j!");
        logger.error("error:进入了log4j!");
      • 3、也可以改变一些日志的设置,如生成日志文件的位置、日志格式中的时间、日期

 

13、Mybatis缓存

  • 什么是缓存?

    • 可以进行高速数据交换的存储器,它先于内存与CPU交换数据,因此速率很快

    • 存在内存中的临时数据,

  • 为什么使用缓存?

    • 将用户经除查询的数据放在缓存中,用户去查询数据就不用从磁盘上(关系型数据库)查询,从而提高查询效率,解决了高并发系统的性能问题。

    • 减少和数据库的交互次数,减少系统开销,提高系统效率。

  • 什么样的数据可以使用缓存?

    • 经常查询并且不经常改变的数据

  • Mybatis缓存

    • 定义了一级缓存和二级缓存

    • 默认情况下,一级缓存开启(sqlSession级别的缓存,也称为本地缓存)

      • 只在一次sqlSession中有效,也就是从开启连接到关闭连接这个区间有效

    • 二级缓存需要手动开启和配置,它是基于namespace级别的缓存

      • 缓存接口cache,可以来定义二级缓存

    • 缓存的查询顺序

      • 二级缓存 ---> 一级缓存 ---> 数据库

  • Mybatis一级缓存

    • 与数据库同一次会话期间查询到的数据会存在缓存中

    • 测试

      • 开启日志

      • 测试:在一个sqlSession中查询两次相同记录,查询两次id为1的用户

        public static void selectById(){
               SqlSession sqlSession = MybatisUtils.getSqlSession();
        ?
               UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
               User user = userMapper.selectById(1);
               System.out.println(user);
        ?
               System.out.println("-----------------------------");
        ?
               User user2 = userMapper.selectById(1);
               System.out.println(user2);
        ?
               sqlSession.close();
          }
      • 查看日志输出

        Opening JDBC Connection
        Created connection 110771485.
        Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
        ==>  Preparing: select * from mybatis.mybatistest where id = ?;
        ==> Parameters: 1(Integer)
        <==    Columns: id, name, pwd
        <==        Row: 1, 洪梦霞, 123
        <==      Total: 1
        User(id=1, name=洪梦霞, pwd=123)
        -----------------------------
        User(id=1, name=洪梦霞, pwd=123)
        Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
        Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
        Returned connection 110771485 to pool.
           
        /*
        从打开链接,创造连接池,关闭连接,return连接到连接池,这些操作都在一次sqlSession中
        第一次查询时执行sql语句,从数据库中查询数据,
        第二次查询时直接在缓存中取数据
        */
    • 一级缓存失效的情况

      • 查询不同的数据

      • 增删改会刷新缓存,增删改操作有可能会改变原来的数据,所以会刷新缓存,缓存失效

      • 查询不同的Mapper.xml

      • 手动请理缓存:sqlSession.clearCache();

  • Mybatis二级缓存

    • 二级缓存工作机制

      一个会话查询一条数据,这个数据会被放在当前会话的一级缓存中

      如果当前会话关闭了(会话提交或者关闭会话),一级缓存消失,一级缓存中的数据会被转存到二级缓存中

      新的会话查询信息就可以从二级缓存中获取数据

      不同的mapper查出的数据会放在自己的缓存中

    • 开启二级缓存

      • 先在mybatis的核心配置文件中显示开启全局缓存

        <settings>
           <setting name="cacheEnabled" value="true"/>
        </settings>
      • 然后在某一个XXXMapper.xml中,也就是需要使用二级缓存的地方开启缓存

        <!--2.1-->
        <cache/>
        ?
        <!--2.2、也可使用一些参数-->
        <cache
          eviction="FIFO"
          flushInterval="60000"
          size="512"
          readOnly="true"/>
    • 测试

      • 开启日志

      • 开启二级缓存

      • 测试:在两个sqlSession中查询相同的数据

        public static void selectById(){
           SqlSession sqlSession1 = MybatisUtils.getSqlSession();
           SqlSession sqlSession2 = MybatisUtils.getSqlSession();
        ?
           UserMapper UserMapper1 = sqlSession1.getMapper(UserMapper.class);
           UserMapper UserMapper2 = sqlSession1.getMapper(UserMapper.class);
        ?
           User user1 = UserMapper1.selectById(1);
           System.out.println(user1);
        ?
           System.out.println("============================");
        ?
           User user2 = UserMapper2.selectById(1);
           System.out.println(user2);
        ?
           sqlSession1.close();
           sqlSession2.close();
        }
      • 查看日志输出

        Opening JDBC Connection
        Created connection 110771485.
        Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
        ==>  Preparing: select * from mybatis.mybatistest where id = ?;
        ==> Parameters: 1(Integer)
        <==    Columns: id, name, pwd
        <==        Row: 1, 洪梦霞, 123
        <==      Total: 1
        User(id=1, name=洪梦霞, pwd=123)
        ============================
        User(id=1, name=洪梦霞, pwd=123)
        Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
        Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
        Returned connection 110771485 to pool.
           
        /*
        开启二级缓存后,在一级缓存失效了,会使用二级缓存
        */
  • 自定义缓存-ehcache

    ehcache是一种广泛使用的开源Java分布式缓存

    主要面向通用缓存、Java EE和轻量级容器

    • 引入ehcache的依赖

      <dependency>
         <groupId>org.mybatis.caches</groupId>
         <artifactId>mybatis-ehcache</artifactId>
         <version>1.1.0</version>
      </dependency>
    • 在mapper.xml中使用对应的缓存

      <mapper namespace="com.hmx.mapper.UserMapper">
         <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
      </mapper>
    • 编写ehcache.xml文件

      注:如果在加载时未找到/ehcache.xml资源或出现问题,则将使用默认配置

      <?xml version="1.0" encoding="UTF-8"?>
      <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
              updateCheck="false">
         <!--
          diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
            user.home – 用户主目录
            user.dir – 用户当前工作目录
            java.io.tmpdir – 默认临时文件路径
          -->
         <diskStore path="./tmpdir/Tmp_EhCache"/>
         
         <!--
         defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
          -->
         <defaultCache
                 eternal="false"
                 maxElementsInMemory="10000"
                 overflowToDisk="false"
                 diskPersistent="false"
                 timeToIdleSeconds="1800"
                 timeToLiveSeconds="259200"
                 memoryStoreEvictionPolicy="LRU"/>

         <cache
                 name="cloud_user"
                 eternal="false"
                 maxElementsInMemory="5000"
                 overflowToDisk="false"
                 diskPersistent="false"
                 timeToIdleSeconds="1800"
                 timeToLiveSeconds="1800"
                 memoryStoreEvictionPolicy="LRU"/>
         
         <!--
         name:缓存名称。
         maxElementsInMemory:缓存最大数目
         maxElementsOnDisk:硬盘最大缓存个数。
         eternal:对象是否永久有效,一但设置了,timeout将不起作用。
         overflowToDisk:是否保存到磁盘,当系统当机时
         timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
         timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
         diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
         diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
         diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
         memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
         clearOnFlush:内存数量最大时是否清除。
         memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
         FIFO,first in first out,这个是大家最熟的,先进先出。
         LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
         LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
        -->
      ?
      </ehcache>

------------恢复内容结束------------

以上是关于Mybatis的主要内容,如果未能解决你的问题,请参考以下文章

SSM-MyBatis-05:Mybatis中别名,sql片段和模糊查询加getMapper

mybatis动态sql片段与分页,排序,传参的使用

MyBatis动态SQL标签用法

MYBATIS05_ifwherechoosewhentrimsetforEach标签sql片段

mybatis动态sql之利用sql标签抽取可重用的sql片段

[mybatis]动态sql_sql_抽取可重用的sql片段