Mybatis

MyBatis概述

  1. MyBatis简介:

    MyBatis时apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis,2013年11月迁移到GitHub。 Mybatis是一个实现了数据持久化的开源框架,简单理解就是对JDBC进行封装。

  2. MyBatis的优点:

    • 与JDBC相比,减少了50%以上的代码量

    • MyBatis是最简单的持久化框架,小巧并且简单易学

    • MyBatis相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL写在XML里,从程序代码中彻底分离,降低耦合度,便于统一管理和优化,并可重用

    • 提供XML标签,支持编写动态SQL语句

    • 提供映射标签,支持对象与数据库的ORM字段关系映射

  3. MyBatis的缺点:

    • SQL语句的编写工作量比较大,尤其是字段多、关联表多时,更是如此,对开发人员编写SQL语句的功底有一定要求

    • SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库

搭建MyBatis基础工程

  1. 创建Maven工程,导入相关依赖,做静态资源导出的配置

         <!-- 导入相关依赖 -->
      <dependencies>
             <!-- mysql相关依赖 -->
             <dependency>
                 <groupId>mysql</groupId>
                 <artifactId>mysql-connector-java</artifactId>
                 <version>8.0.15</version>
             </dependency>
             <!-- mybatis相关依赖 -->
             <dependency>
                 <groupId>org.mybatis</groupId>
                 <artifactId>mybatis</artifactId>
                 <version>3.5.2</version>
             </dependency>
             <!-- log4j日志相关依赖 -->
             <dependency>
                 <groupId>log4j</groupId>
                 <artifactId>log4j</artifactId>
                 <version>1.2.12</version>
             </dependency>
             <!-- junit测试工具相关依赖 -->
             <dependency>
                 <groupId>junit</groupId>
                 <artifactId>junit</artifactId>
                 <version>4.12</version>
             </dependency>
             <!-- lombok工具相关依赖 -->
             <dependency>
                 <groupId>org.projectlombok</groupId>
                 <artifactId>lombok</artifactId>
                 <version>1.18.16</version>
             </dependency>
         </dependencies>
     
         <!-- 静态资源导出 -->
        <build>
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>false</filtering>
                </resource>
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>false</filtering>
                </resource>
            </resources>
        </build>

    注意:mysql相关依赖和mybatis相关依赖是必须的,其余依赖可酌情导入

  2. 编写配置文件(db.properties、log4j.properties、Mybatis-config.xml)

    • db.properties

       driver=com.mysql.cj.jdbc.Driver
       url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
       username=root
       password=123456
    • 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/kuang.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  
    • 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>
           <properties resource="db.properties"></properties>
       
           <settings>
               <setting name="logImpl" value="LOG4J"/>
           </settings>
       
           <typeAliases>
       <!--       <typeAlias type="com.jarreet.pojo.User" alias="User"/>-->
               <!-- MyBatis会在包名下面搜索需要的Java Bean-->
               <package name="com.jarreet.pojo"/>
           </typeAliases>
       
           <!-- 配置mybatis运行环境 -->
           <environments default="development">
               <environment id="development">
                   <!-- 配置JDBC事物管理 -->
                   <transactionManager type="JDBC"></transactionManager>
                   <!-- POOLED配置JDBC数据源连接池 -->
                   <dataSource type="POOLED">
                       <property name="driver" value="${driver}"/>
                       <property name="url" value="${url}"/>
                       <property name="username" value="${username}"/>
                       <property name="password" value="${password}"/>
                   </dataSource>
               </environment>
           </environments>
       
           <!-- 注册UserMapper.xml -->
           <mappers>
               <mapper resource="com/jarreet/dao/UserMapper.xml"/>
           </mappers>
       </configuration>
  3. 编写MybatisUtils工具类

     package com.jarreet.utils;
     
     import org.apache.ibatis.io.Resources;
     import org.apache.ibatis.session.SqlSession;
     import org.apache.ibatis.session.SqlSessionFactory;
     import org.apache.ibatis.session.SqlSessionFactoryBuilder;
     
     import java.io.IOException;
     import java.io.InputStream;
     
     public class MybatisUtils {
     
         private static SqlSessionFactory sqlSessionFactory;
     
         static {
             try{
                 // 加载mybatis配置文件
                 String resource = "mybatis-config.xml";
                 InputStream inputStream = Resources.getResourceAsStream(resource);
                 // 实例化 SqlSessionFactoryBuilder构造器,并通过其build()方法实例化 sqlSessionFactory
                 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                 e.printStackTrace();
            }
        }
     
             // 通过sqlSessionFactory.openSession()创建sqlSession
             public static SqlSession getSession(){
                 return sqlSessionFactory.openSession();
            }
     }
  4. 编写实体类

     package com.jarreet.pojo;
     
     import lombok.AllArgsConstructor;
     import lombok.Data;
     import lombok.NoArgsConstructor;
     
     @Data
     @AllArgsConstructor
     @NoArgsConstructor
     public class User {
     
         private int id;
         private String name;
         private String pwd;
     
     }
  5. 编写实体类对应的Mapper和Mapper.xml文件

    • Mapper

       import com.jarreet.pojo.User;
       
       import java.util.List;
       import java.util.Map;
       
       public interface UserMapper {
           // 查询全部用户
           List<User> selectUser();
       
           // 根据id查询用户
           User selectUserById(int id);
       
           // insert一个用户
           int addUser(User user);
       
           // update一个用户
           int updateUser(User user);
       
           // delete一个用户
           int deleteUser(int id);
       
           // 分页
           List<User> getUserBtLimit(Map<String,Integer> map);
       }
    • Mapper.xml

     <?xml version="1.0" encoding="UTF-8" ?>
     <!DOCTYPE mapper
             PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
     <mapper namespace="com.jarreet.dao.UserMapper">
         
        // 查询全部用户
         <select id="selectUser" resultType="com.kuang.pojo.user">
            select * from user
         </select>
         
        // 根据id查询用户
         <select id="selectUserById" parameterType="int" resultType="com.kuang.pojo.user">
            select * from user where id = #{id}
         </select>
         
        // insert一个用户
         <insert id="addUser" parameterType="com.kuang.pojo.user" >
            insert into user(id, name, pwd) values (#{id},#{name},#{pwd})
         </insert>
         
        // update一个用户
         <update id="updateUser" parameterType="com.kuang.pojo.user">
            update user set name=#{name},pwd=#{pwd} where id = #{id}
         </update>
         
        // delete一个用户
         <delete id="deleteUser" parameterType="int">
            delete from user where id = #{id}
         </delete>
         
        //分页
          <select id="getUserBtLimit" parameterType="map" resultType="com.kuang.pojo.user">
            select * from user limit #{startIndex},#{pageSize}
         </select>
         
     </mapper>

Log4j日志工厂

  1. Log4j简介:

    Log4j是Apache的一个开源项目;通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件等等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

  2. Log4j使用步骤

    • 导入log4j依赖包(见上一部分)

    • 编写配置文件log4j.properties(见上一部分)

    • 在mybatis-config.xml中开启日志

       <settings> <setting name="logImpl" value="LOG4J"/> </settings>
    • 在程序中使用log4j进行输出

       static Logger logger = Logger.getLogger(MyTest.class);
       
       @Test
       public void selectUser() {
        logger.info("info:进入selectUser方法");
           logger.debug("debug:进入selectUser方法");
           logger.error("error: 进入selectUser方法");
           
           SqlSession session = MybatisUtils.getSession();
           UserMapper mapper = session.getMapper(UserMapper.class);
           
           List<User> users = mapper.selectUser();
           for (User user: users){
          System.out.println(user);
          }
           session.close();
       }

MyBayis配置解析

  1. 所有能配置的内容如下

     configuration(配置)
      properties(属性)
      settings(设置)
      typeAliases(类型别名)
      typeHandlers(类型处理器)
      objectFactory(对象工厂)
      plugins(插件)
      environments(环境配置)
      environment(环境变量)
      transactionManager(事务管理器)
      dataSource(数据源)
      databaseIdProvider(数据库厂商标识)
      mappers(映射器)
     <!-- 注意元素节点的顺序!顺序不对会报错 -->
  2. environments元素:可以对配置MyBatis的多套运行环境,将SQL映射到多个不同的数据库上,必须指定其中一个为默认运行

    环境(通过default指定)

  3. mappers元素:定义映射SQL语句文件,引入资源方式主要有如下两种

     <!-- 使用相对于类路径的资源引用 --> 
     <mappers>
         <mapper resource="org/mybatis/builder/PostMapper.xml"/>
     </mappers>
     <!-- 使用映射器接口实现类的完全限定类名 需要配置文件名称和接口名称一致,并且位于同一目录下 --> 
     <mappers>
         <mapper class="org.mybatis.builder.AuthorMapper"/>
     </mappers>
  4. properties元素:帮助导入一些properties配置文件

     <properties resource="db.properties"/>
  5. typeAliases元素:帮助起别名

     <!-- 配置别名,注意顺序 --> 
     <!-- 当这样配置时, User 可以用在任何使用 com.jarreet.pojo.User 的地方 -->
     <typeAliases>
         <typeAlias type="com.jarreet.pojo.User" alias="User"/>
     </typeAliases>
     <!-- 指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean --> 
     <!-- 每一个在包 com.kuang.pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的
     非限定类名来作为它的别名。-->
     <typeAliases>
         <package name="com.jarreet.pojo"/>
     </typeAliases>
  6. settings元素:设置一些属性,主要有懒加载、日志实现、缓存开启等

MyBatis分页

MyBatis执行流程

  1. 简易执行流程与作用域

    • SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。

    • SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为MyBatis 的本质就是 Jav对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。通常SqlSessionFactory 作为一个单例在应用中被共享,所以说 SqlSessionFactory 的最佳作用域是应用作用域。

    • SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try...catch...fifinally... 语句来保证其正确关闭。所以 SqlSession 的最佳的作用域是请求或方法作用域。

  2. 具体执行流程

    image-20210416173646548

Mapper.xml详解

  • statement标签:select、update、delete、insert 对应CRUD操作

  • parameterType:参数数据类型

    • 单个参数:基本数据类型、String类型、包装类

    • 多个参数:不写parameterType,sql语句中使用下标

      select * from user where id=#{param1} and name=#{param2}

    • Java Bean

  • resultType:结果数据类型

    • 单个参数:基本数据类型、String类型、包装类

    • Java Bean

级联查询:

接下来的一对多和多对一都为同一个示例:老师与学生,一个老师有多名学生,多名学生有共同一个老师

  • 数据表:

    • 学生表

    • 教师表

  • 实体类

    • 学生实体类

       package com.jarreet.pojo;
       
       import lombok.Data;
       
       @Data
       public class Student {
           private int id;
           private String name;
       
           // 多个学生有一个共同的老师
           private Teacher teacher;
       }
    • 教师实体类

       package jarreet.kuang.pojo;
       
       import lombok.Data;
       import java.util.List;
       
       @Data
       public class Teacher {
           private int id;
           private String name;
       
           // 一个老师有多个学生
           private List<Student> students;
       }
  1. 多对一(association)

    • 按结果嵌套处理

      • StudentMapper

         package com.jarreet.dao;
         
         import com.jarreet.pojo.Student;
         
         public interface StudentMapper {
             public Student findById(int id);
         }
      • StudentMapper.xml

         <?xml version="1.0" encoding="UTF-8" ?>
         <!DOCTYPE mapper
                 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
         <mapper namespace="com.jarreet.dao.StudentMapper">
             
             <resultMap id="StudentMap" type="com.jarreet.pojo.Student">
                 <id column="sid" property="id"></id>
                 <result column="sname" property="name"></result>
                 <!--关联对象property 关联对象在Student实体类中的属性-->
                 <association property="teacher" javaType="com.jarreet.pojo.Teacher">
                     <id column="id" property="id"></id>
                     <result property="name" column="tname"></result>
                 </association>
             </resultMap>
             
             <select id="findById" resultMap="StudentMap">
                SELECT s.id sid,s.name sname,t.name tname FROM student s, teacher t WHERE s.id=#{id} AND s.tid=t.`id`;
             </select>
         </mapper>
      • 测试

             @Test
             public void testStudentTeacher(){
                 SqlSession session = MybatisUtils.getSession();
                 StudentMapper mapper = session.getMapper(StudentMapper.class);
         
                 System.out.println(mapper.findById(1));
                 session.close();
            }

        测试结果:

    • 按查询嵌套处理

      • StudentMapper

         package com.jarreet.dao;
         
         import com.jarreet.pojo.Student;
         
         public interface StudentMapper {
             public Student findById(int id);
         }
      • StudentMapper.xml

         <?xml version="1.0" encoding="UTF-8" ?>
         <!DOCTYPE mapper
                 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
         <mapper namespace="com.jarreet.dao.StudentMapper">
             
             <resultMap id="StudentMap" type="com.kuang.pojo.Student">
                 <id column="id" property="id"></id>
                 <result column="name" property="name"></result>
                 <!--association关联属性 property属性名 javaType属性类型 column在多 的一方的表中的列名-->
                 <association property="teacher" column="tid" javaType="com.jarreet.pojo.Teacher" select="getTeacher"></association>
             </resultMap>
             <select id="getTeacher" resultType="Teacher">
                select name from teacher where id = #{tid}
             </select>
         
             <select id="findById" resultMap="StudentMap">
                SELECT * FROM student WHERE id=#{id};
             </select>
         </mapper>
      • 测试

             @Test
             public void testStudentTeacher(){
                 SqlSession session = MybatisUtils.getSession();
                 StudentMapper mapper = session.getMapper(StudentMapper.class);
         
                 System.out.println(mapper.findById(1));
                 session.close();
            }

        测试结果:

  2. 一对多

    • 按结果嵌套处理

      • TeacherMapper

         package com.jarreet.dao;
         
         import com.jarreet.pojo.Teacher;
         
         public interface TeacherMapper {
         
             public Teacher findById(int id);
         }
      • TeacherMapper.xml

         <?xml version="1.0" encoding="UTF-8" ?>
         <!DOCTYPE mapper
                 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
         <mapper namespace="com.jarreet.dao.TeacherMapper">
         
             <resultMap id="TeacherMap" type="com.jarreet.pojo.Teacher">
                 <id column="tid" property="id"></id>
                 <result column="tname" property="name"></result>
                 <!-- 使用collection映射集合,ofType指定的是映射到list集合属性中pojo的类型 -->
                 <collection property="students" ofType="com.jarreet.pojo.Student">
                     <id column="sid" property="id"></id>
                     <result column="sname" property="name"></result>
                     <result column="tid" property="id"></result>
                 </collection>
             </resultMap>
         
             <select id="findById" resultMap="TeacherMap">
                SELECT t.id tid,t.name tname,s.id sid,s.name sname FROM teacher t,student s WHERE t.id = #{id} AND t.id = s.tid;
             </select>
         </mapper>
      • 测试

            @Test
        public void testTeacherStudent(){
        SqlSession session = MybatisUtils.getSession();
        TeacherMapper mapper = session.getMapper(TeacherMapper.class);

        System.out.println(mapper.findById(1));
        session.close();
        }

        测试结果:

    • 按查询嵌套处理

      • TeacherMapper

        package com.jarreet.dao;

        import com.jarreet.pojo.Teacher;

        public interface TeacherMapper {

        public Teacher findById(int id);
        }
      • TeacherMapper.xml

        <?xml version="1.0" encoding="UTF-8" ?>
        <!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
        <mapper namespace="com.jarreet.dao.TeacherMapper">

        <select id="findById" resultMap="TeacherMap">
        select * from teacher where id=#{id}
        </select>

        <resultMap id="TeacherMap" type="com.jarreet.pojo.Teacher">
        <id column="id" property="id"></id>
        <result column="name" property="name"></result>
        <!--column是一对多的外键 , 写的是一的主键的列名-->
        <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId"></collection>
        </resultMap>
        <select id="getStudentByTeacherId" resultType="com.jarreet.pojo.Student">
        select id,name from student where tid = #{id};
        </select>
        </mapper>
      • 测试

            @Test
        public void testTeacherStudent(){
        SqlSession session = MybatisUtils.getSession();
        TeacherMapper mapper = session.getMapper(TeacherMapper.class);

        System.out.println(mapper.findById(1));
        session.close();
        }

        测试结果:

  3. 多对多

    示例:顾客买电器,一个顾客可能购买多种电器,一种电器也有可能被多个顾客购买

    • 数据表

      • 顾客表

      • 商品表

      • 顾客商品购买表

    • 实体类

      • 顾客实体类

        package com.jarreet.pojo;

        import lombok.Data;

        import java.util.List;

        @Data
        public class Customer {

        private int id;
        private String name;
        private List<Goods> goods;
        }
      • 商品实体类

        package com.jarreet.pojo;

        import lombok.Data;

        import java.util.List;

        @Data
        public class Goods {

        private int id;
        private String name;
        private List<Customer> customers;
        }
    • 查询一(通过顾客id查询他所购买的商品):Mapper和对应的Mapper.xml

      • CustomerMapper

        package com.jarreet.dao;

        import com.jarreet.pojo.Customer;

        public interface CustomerMapper {

        public Customer findById(int id);
        }
      • CustomerMapper.xml

        <?xml version="1.0" encoding="UTF-8" ?>
        <!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
        <mapper namespace="com.jarreet.dao.CustomerMapper">
        <resultMap id="CustomerGoods" type="com.kuang.pojo.Customer">
        <id column="cid" property="id"></id>
        <result column="cname" property="name"></result>
        <collection property="goods" ofType="com.jarreet.pojo.Goods">
        <id column="gid" property="id"></id>
        <result column="gname" property="name"></result>
        </collection>
        </resultMap>

        <select id="findById" parameterType="int" resultMap="CustomerGoods">
        SELECT c.id cid,c.name cname,g.id gid,g.name gname FROM customer c,goods g,customer_goods cg WHERE c.`id`=#{id} AND cg.cid=c.id AND cg.gid=g.id;
        </select>
        </mapper>
    • 查询二(通过商品id查询购买它的顾客):Mapper和对应的Mapper.xml

      • GoodsMapper

        package com.jarreet.dao;

        import com.jarreet.pojo.Goods;

        public interface GoodsMapper {

        public Goods findById(int id);
        }
      • GoodsMapper.xml

         <?xml version="1.0" encoding="UTF-8" ?>
         <!DOCTYPE mapper
                 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
         <mapper namespace="com.jarreet.dao.GoodsMapper">
             <resultMap id="GoodsCustomer" type="com.jarreet.pojo.Goods">
                 <id column="gid" property="id"></id>
                 <result column="gname" property="name"></result>
                 <collection property="customers" ofType="com.jarreet.pojo.Customer">
                     <id column="cid" property="id"></id>
                     <result column="cname" property="name"></result>
                 </collection>
             </resultMap>
             
             <select id="findById" parameterType="int" resultMap="GoodsCustomer">
                SELECT c.id cid,c.name cname,g.id gid,g.name gname FROM customer c,goods g,customer_goods cg WHERE g.`id`=#{id} AND cg.cid=c.id AND cg.gid=g.id;
             </select>
         </mapper>
    • 测试

      • 查询一测试

             @Test
             public void testCustomerGoods(){
                 SqlSession session = MybatisUtils.getSession();
                 CustomerMapper mapper = session.getMapper(CustomerMapper.class);
         
                 System.out.println(mapper.findById(1));
            }

        测试结果:

      • 查询二测试

             @Test
             public void testGoodsCustomer(){
                 SqlSession session = MybatisUtils.getSession();
                 GoodsMapper mapper = session.getMapper(GoodsMapper.class);
         
                 System.out.println(mapper.findById(3));
            }

        测试结果:

MyBatis延迟加载(懒加载)

  1. 什么是延迟加载?

    延迟加载也叫懒加载、惰性加载,使用延迟加载可以提高程序的运行效率,针对于数据持久层的操作,在某些特定的情况下去访问特定的数据库,在其他情况下可以不访问某些表,从一定程度上减少了java应用与数据库的交互次数

  2. 如何使用延迟加载?

    • 在mybatis-config.xml中开启延迟加载

           <settings>
               <!-- 开启延迟加载 -->
               <setting name="lazyLoadingEnabled" value="true"/>
           </settings>
    • 将多表关联查询拆分成多个单表查询(即使用上一部分的按查询嵌套处理),以上一部分StudentMapper为例

      • StudentMapper

         package com.jarreet.dao;
         
         import com.jarreet.pojo.Student;
         
         public interface StudentMapper {
             public Student findById(int id);
         }
      • StudentMapper.xml

         <?xml version="1.0" encoding="UTF-8" ?>
         <!DOCTYPE mapper
                 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
         <mapper namespace="com.jarreet.dao.StudentMapper">
             
             <resultMap id="StudentMap" type="com.kuang.pojo.Student">
                 <id column="id" property="id"></id>
                 <result column="name" property="name"></result>
                 <!--association关联属性 property属性名 javaType属性类型 column在多 的一方的表中的列名-->
                 <association property="teacher" column="tid" javaType="com.jarreet.pojo.Teacher" select="getTeacher"></association>
             </resultMap>
             <select id="getTeacher" resultType="Teacher">
                select name from teacher where id = #{tid}
             </select>
         
             <select id="findById" resultMap="StudentMap">
                SELECT * FROM student WHERE id=#{id};
             </select>
         </mapper>
      • 测试

        当只需查询student表时

             @Test
             public void testStudentTeacherLazy(){
                 SqlSession session = MybatisUtils.getSession();
                 StudentMapper mapper = session.getMapper(StudentMapper.class);
         
                 Student student = mapper.findById(1);
                 System.out.println(student.getName());
         
                 session.close();
            }

        查询结果:

        image-20210416003819957

        当需要查询两张表时

             @Test
             public void testStudentTeacherLazy(){
                 SqlSession session = MybatisUtils.getSession();
                 StudentMapper mapper = session.getMapper(StudentMapper.class);
         
                 Student student = mapper.findById(1);
                 System.out.println(student);
         
                 session.close();
            }

        查询结果:

        image-20210416003718988

动态SQL

使用动态sql可简化代码的开发,减少开发者的工作量,程序可以自动根据业务参数来决定SQL的组成

  • if标签

    if标签可以自动根据表达式的结果来决定是否将对应的语句添加到SQL中,如果条件不成立则不添加,如果条件成立则添加

     <select id="findByUser" parameterType="com.jarreet.pojo.User" resultType="com.jarreet.pojo.User">
        select * from user where
         <if test="id!=0">
            id = #{id}
         </if>
         <if test="username!=null">
            and username = #{username}
         </if>
          <if test="password!=null">
            and password = #{password}
         </if>  
           <if test="age!=0">
            and age = #{age}
         </if>  
     </select>
  • where标签

    where标签可以自动判断是否要删除语句块的and关键字,如果检测到where直接跟and拼接,则自动删除and,通常情况下if和where结合起来使用

     <select id="findByUser" parameterType="com.jarreet.pojo.User" resultType="com.jarreet.pojo.User">
        select * from user
         <where>
             <if test="id!=0">
                id = #{id}
             </if>
             <if test="username!=null">
                and username = #{username}
             </if>
             <if test="password!=null">
                and password = #{passwordd}
             </if>
             <if test="age!=0">
                and age = #{age}
             </if>        
         </where>
     </select>
  • choose、when标签

     <select id="findByUser" parameterType="com.jarreet.pojo.User" resultType="com.jarreet.pojo.User">
        select * from user
         <where>
             <choose>
                 <when test="id!=0">
                    id = #{id}
                 </when>
                 <when test="username!=null">
                    and username = #{username}
                 </when>
                 <when test="password!=null">
                    and password = #{password}
                 </when>
                 <when test="age!=0">
                    and age = #{age}
                 </when>            
             </choose>
         </where>
     </select>
  • trim标签

    trim标签中的prefix和suffix属性会被用于生成实际的SQL语句,会和标签内部的语句进行拼接,如果语句前后出现了prefeixOverrides或者suffixOverrides属性中指定的值,MyBatis框架会自动将其删除

     <select id="findByUser" parameterType="com.jarreet.pojo.User" resultType="com.jarreet.pojo.User">
        select * from user
         <trim prfix="where" prefixOverrides="and">
             <if test="id!=0">
                id = #{id}
             </if>
             <if test="username!=null">
                and username = #{username}
             </if>
             <if test="password!=null">
                and password = #{passwordd}
             </if>
             <if test="age!=0">
                and age = #{age}
             </if>        
         </where>
     </select>
  • set标签

    set标签用于update操作,会自动根据参数选择生成SQL语句

     <update id="update" parameterType="com.jarreet.pojo.User">
        update user
         <!--注意set是用的逗号隔开-->
         <set>
             <if test="username!=null">
                username = #{username},
             </if>
             <if test="password!=null">
                password = #{passwordd},
             </if>
             <if test="age!=0">
                age = #{age}
             </if>        
         </set>
        where id = #{id}
     </select>
  • foreach标签

    foreach标签可以迭代生成一系列值,这个标签主要用于SQL的in语句

     <select id="findByIds" parameterType="com.jarreet.pojo.User" resultType="com.jarreet.pojo.User">
        select * from user
         <where>
             <!--
      collection:指定输入对象中的集合属性
      item:每次遍历生成的对象
      open:开始遍历时的拼接字符串
      close:结束时拼接的字符串
      separator:遍历对象之间需要拼接的字符串
      select * from user where id in (1,2,3)
      -->
             <foreach collection="ids" open="id in (" close=")" item="id" separator=",">
                #{id}
             </foreach>    
         </where>
     </select>
  • SQL片段

    有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。

    提取SQL片段:

     <sql id="if-user">
         <if test="id!=0">
            id = #{id}
         </if>
         <if test="username != null">
            and username = #{username}
         </if>
         <if test="password != password">
            and password = #{password}
         </if>
         <if test="age!=0">
            and age = #{age}
         </if>
     </sql>

    引用SQL片段:

     <select id="findByUser" parameterType="com.jarreet.pojo.User" resultType="com.jarreet.pojo.User">
        select * from user
         <where>
             <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
      <include refid="if-user"></include>    
         </where>
     </select>

MyBatis缓存

  • 什么是MyBatis缓存

    使用缓存可以减少java应用与数据库交互的次数,从而提升程序的运行效率。比如查询出id=1的对象,第一次查询出之后会自动将该对象保存到缓存中,当下一次查询时,直接从缓存中取出对象即可,无需再次访问数据库。

  • MyBatis缓存分类

    1. 一级缓存:SqlSession级别,默认开启,并且不能关闭。

      操作数据库时需要创建SqlSession对象,在对象中有一个HashMap用于存储缓存数据,不同的SqlSession之间缓存数据区域是互不影响的。

      一级缓存的作用域是SqlSession范围的,当在同一个SqlSession中执行两次相同的SQL语句时,第一次执行完毕将结果保存到缓存中,第二次查询时直接从缓存中获取。

      需要注意的是,如果SqlSession执行了DML操作(insert、update、delete),MyBatis必须将缓存清空以保证数据的准确性。

    2. 二级缓存:Mapper缓存,默认关闭,可以开启。

      使用二级缓存时,多个SqlSession使用同一个Mapper的SQL语句操作数据库,得到的数据会存在二级缓存区,同样是使用HashMap进行数据存储,相比较于一级缓存,二级缓存的范围更大,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

      二级缓存是多个SqlSession共享的,其作用域是Mapper的同一个namespace,不同的SqlSession两次执行相同额namespace下的SQL语句,参数也相等,则第一次执行成功后会将数据保存到二级缓存中,第二次可直接从二级缓存中取出数据。

  • 一级缓存的使用:自动启用

    • 一级缓存失效的情况:sqlSession不同;sqlSession相同,查询条件不同;sqlSession相同,两次查询之间执行了增删改操作;sqlSession相同,手动清除一级缓存(session.clearCache()

  • 二级缓存的使用:

    1. MyBatis自带的二级缓存

      • config.xml中配置开启二级缓存

         <settings>
             <!-- 开启二级缓存 -->
             <setting name="cacheEnabled" value="true"/>
         </settings>
      • Mapper.xml中配置二级缓存

         <cache/>
      • 实体类实现序列化接口

    2. eacache二级缓存

      • pom.xml添加相关以来

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

         <mapper namespace = “org.acme.FooMapper” >
             <cache type = “org.mybatis.caches.ehcache.EhcacheCache” />
         </mapper>
      • 添加ehache.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
           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"/>
         </ehcache>

MyBatis逆向工程

MyBatis Generator,建成MBG,是一个专门为MyBatis框架开发者定制的代码生成器,可自动生成MyBatis框架所需要的实体类、Mapper接口、Mapper.xml,支持基本的CRUD操作,但是一些相对复杂的SQL需要开发者自己完成。

使用MBG的步骤:

  1. 导入MBG相关的依赖

  2. 创建MBG配置文件generatorConfig.xml

    • jdbcConnection 配置数据库连接信息

    • javaModelGenerator 配置JavaBean的生成策略

    • sqlMapGenerator 配置SQL映射文件生成策略

    • javaClientGenerator 配置Mapper接口的生成策略

    • table配置目标数据表(tableName:表名,domainObjectName:javaBean 类名)

       <?xml version="1.0" encoding="UTF-8"?>
       <!DOCTYPE generatorConfiguration
               PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
               "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
       
       <generatorConfiguration>
           <context id="testTables" targetRuntime="MyBatis3">
               <!-- 1. jdbcConnection 配置数据库连接信息 -->
               <jdbcConnection
                       driverClass="com.mysql.cj.jdbc.Driver"
                       connectionURL="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=GMT%2B8"
                       userId="root"
                       password="558597"
               ></jdbcConnection>
               <!-- 2. javaModelGenerator 配置JavaBean的生成策略 -->
               <javaModelGenerator targetPackage="com.kuang.pojo" targetProject="./src/main/java"></javaModelGenerator>
               <!-- 3. sqlMapGenerator 配置SQL映射文件生成策略 -->
               <sqlMapGenerator targetPackage="com.kuang.dao" targetProject="./src/main/java"></sqlMapGenerator>
               <!-- 4. javaClientGenerator 配置Mapper接口的生成策略 -->
               <javaClientGenerator type="XMLMAPPER" targetPackage="com.kuang.dao" targetProject="./src/main/java"></javaClientGenerator>
               <!-- 5. table配置目标数据表(tableName:表名,domainObjectName:javaBean 类名) -->
               <table tableName="user" domainObjectName="User"></table>
           </context>
       </generatorConfiguration>
  3. 创建Generator执行类

     public class MyTest {
         public static void main(String[] args) {
             List<String> warings = new ArrayList<String>();
             boolean overwrite = true;
             String genCig = "/generatorConfig.xml";
             File configFile = new File(MyTest.class.getResource(genCig).getFile());
             ConfigurationParser configurationParser = new ConfigurationParser(warings);
             Configuration configuration = null;
             try {
                 configuration = configurationParser.parseConfiguration(configFile);
            } catch (IOException e) {
                 e.printStackTrace();
            } catch (XMLParserException e) {
                 e.printStackTrace();
            }
             DefaultShellCallback callback = new DefaultShellCallback(overwrite);
             MyBatisGenerator myBatisGenerator = null;
             try {
                 myBatisGenerator = new MyBatisGenerator(configuration,callback,warings);
            } catch (InvalidConfigurationException e) {
                 e.printStackTrace();
            }
             try {
                 myBatisGenerator.generate(null);
            } catch (SQLException throwables) {
                 throwables.printStackTrace();
            } catch (IOException e) {
                 e.printStackTrace();
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
        }
     }



posted @ 2021-05-06 00:56  离渊灬  阅读(120)  评论(0)    收藏  举报