• 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>

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

  • 相关阅读:
    [HTML] IE=edge,chrome=1的META标签详解
    [FFmpeg] ffmpeg 常用命令
    rsyncd 配置使用
    httpd配置
    ftp利用脚本添加本地用户
    zabbix监控概念
    搭建本地yum源并定时同步
    linux添加lvm磁盘大小,命令行创建swap
    500 OOPS: vsftpd: refusing to run with writable root inside chroot()
    利用saltstack批量安装clamav杀毒软件
  • 原文地址:https://www.cnblogs.com/LittleSkinny/p/13125704.html
Copyright © 2020-2023  润新知