Mybatis整理

一、Mybatis入门

一、什么是mybatis框架?及其工作原理

  1. mybatis支持SQL操作以及高级映射的持久层框架,也被称为ORM框架(mybatis框架、hibernate框架);解决面向对象和关系数据库中数据类型不匹配的问题,通过描述java对象和数据库表之间的映射关系,自动将java应用程序中的对象持久化到关系型数据库的表中;使用ORM框架后,应用程序不在直接访问数据库,而是以面向对象的方式来操作持久化对象,ORM框架会通过映射关系将这些面向对象的操作转化成SQL操作。

  2. Hibernate和Mybatis的区别:

    • Hibernate是全表映射框架,开发者只需要定义映射关系,不需要掌握SQL语句的编写,开发效率高于Mybatis;但在多表关联时对SQL的支持较差,更新数据时需要发送所有的字段,不支持存储过程,不能通过SQL进行性能优化
    • mybatis是半自动映射框架,要手动匹配SQL、POJO和映射关系,支持存储过程,可通过SQL进行性能优化
  3. mybatis的工作原理流程图
    在这里插入图片描述

二.环境搭建

  1. 创建Maven工程并导入坐标

  2. 创建实体类和Dao接口

  3. 创建mybatis的主配置文件(mybatis-config.xml或applicationContext.xml)

  4. 创建映射配置文件 (mybatis-mapper-config.xml 在mapper源文件夹中)

    注意事项:

    1. mybatis的映射配置文件位置必须和Dao接口的包结构相同
    2. 映射配置文件的mapper标签namespace属性的取值必须是Dao接口的全限定类名
    3. 映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名
      • 这样我们不需要再写dao的实现类

三、入门案例

  1. 基于XML

    • mapper配置文件
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.liu.dao.UserDao">
        <!-- resultType 指定返回到哪个实体类中(domain中)-->
        <select id="findAll" resultType="com.liu.domain.User">
            select * from user
        </select>
    </mapper>
    
    • java测试类
    public class MybatisTest {
    
        @Test
        public void findAllTest() throws Exception{
            //读取配置文件
            InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
            //创建sqlSessionFactory工厂
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();  //使用了构建者模式,将细节隐藏,
            SqlSessionFactory factory = builder.build(in);					//使使用者直接调用方法即可拿到对象
            //使用工厂生产sqlSession对象
            SqlSession sqlSession = factory.openSession();		//使用了工厂模式,解耦降低类之间的联系
            //使用SqlSession创建dao接口的代理对象
            UserDao userDao = sqlSession.getMapper(UserDao.class);//使用了代理模式,不修改源码的基础上
            //使用代理对象执行方法									//对已有方法的增强
            List<User> users = userDao.findAll();
            for (User user: users) {
                System.out.println(user);
            }
            //关闭资源
            sqlSession.close();
            in.close();
        }
    }
    

    注意事项:在映射配置文件中一定要告知封装mybatis在了哪个实体类(全限定类名)中

  2. 基于注解

    在dao接口方法上使用注解,并指定SQL语句

    <!--在mybatis-config.xml配置文件中指定实体类的位置-->
    <mappers>
            <mapper class="com.liu.dao.UserDao" />
    </mappers>
    

    在接口中添加注解信息

    public interface UserDao {
        @Select("select * from user")
        //查询所有数据
        List<User> findAll();
    }
    

四、自定义mybatis框架

  1. mybatis在使用代理dao的方式实现CRUD
    • 创建代理对象
    • 在代理对象中调用selectList

二、Mybatis的基本使用

一、mybatis的单表crud操作

  1. 查询数据

    • 创建持久化类User

      public class User /*implements Serializable*/ {
      
          private int id;
          private String username;
         // private Date birthday;
          private String sex;
          //private String address;
      
          public int getId() {
              return id;
          }
      
          public void setId(int id) {
              this.id = id;
          }
      
          public String getUsername() {
              return username;
          }
      
          public void setUsername(String username) {
              this.username = username;
          }
      /*
          public Date getBirthday() {
              return birthday;
          }
      
          public void setBirthday(Date birthday) {
              this.birthday = birthday;
          }
      */
          public String getSex() {
              return sex;
          }
      
          public void setSex(String sex) {
              this.sex = sex;
          }
      /*
          public String getAddress() {
              return address;
          }
      
          public void setAddress(String address) {
              this.address = address;
          }
      */
          @Override
          public String toString() {
              return "User{" +
                      "id=" + id +
                      ", username='" + username + '\'' +
                      //", birthday=" + birthday +
                      ", sex='" + sex + '\'' +
                     // ", address='" + address + '\'' +
                      '}';
          }
      }
      

      接口部分就不写了

    • 创建映射文件(user-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.liu.dao.UserDao">
          <!-- resultType 指定返回到哪个实体类中(domain中)-->
          <select id="findAll" resultType="com.liu.domain.User">
              select * from user
          </select>
      </mapper>
      
    • 创建测试类

      public class MybatisTest {
      
          @Test
          public void findAllTest() throws Exception{
              //读取配置文件
              InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
              //创建sqlSessionFactory工厂
              SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
              SqlSessionFactory factory = builder.build(in);
              //使用工厂生产sqlSession对象
              SqlSession sqlSession = factory.openSession();
              //使用SqlSession创建dao接口的代理对象
              UserDao userDao = sqlSession.getMapper(UserDao.class);
              //使用代理对象执行方法
              List<User> users = userDao.findAll();
              for (User user: users) {
                  System.out.println(user);
              }
              //关闭资源
              sqlSession.close();
              in.close();
          }
      }
      
  2. 添加数据

    • 创建持久化类(同上)

    • 在映射文件中添加新的映射关系

      <!-- 插入数据,主键自增 -->
      <insert id="insertUser" useGeneratedKeys="true" keyProperty="id" 
              parameterType="com.liu.domain.User" >
          insert into user(username,sex) values (#{username},#{sex})
      </insert>
      
    • 测试类测试

      //将代理对象执行方法改为insertUser()
          @Test
          public void insertUser(){
              User user = new User();
              user.setUsername("zz");
              user.setSex("女");
              userDao.insertUser(user);
      
              //提交事务
              sqlSession.commit();
              System.out.println();
          }
      
  3. 删除数据

    • 创建持久化类(同上)

    • 在映射文件中添加新的映射关系

      	<!--  通过id删除数据  -->
          <delete id="deleteUserById" parameterType="com.liu.domain.User">
              delete from user
              where id = #{id}
          </delete>
          <!--  通过用户名删除数据  -->
          <delete id="deleteUserByName" parameterType="com.liu.domain.User">
              delete from user
              where username = #{username}
          </delete>
      
    • 测试类测试

      	@Test
          public void deleteUserById(){
              User user = new User();
              user.setId(3);
              userDao.deleteUserById(user);
              sqlSession.commit();
              System.out.println();
          }
      
          @Test
          public void deleteUserByName(){
              User user = new User();
              user.setUsername("zz");
              userDao.deleteUserByName(user);
              sqlSession.commit();
              System.out.println();
          }
      
  4. 修改数据

    • 创建持久化类(同上)

    • 在映射文件中添加新的映射关系

      <!-- 修改数据  -->
      <update id="updateUser" parameterType="com.liu.domain.User">
          update user set
          username = #{username},
          sex = #{sex}
          where id = #{id}
      </update>
      
    • 测试类测试

          @Test
          public void updateUser(){
              User user = new User();
              user.setId(3);
              user.setUsername("xw");
              user.setSex("男");
              userDao.updateUser(user);
              sqlSession.commit();
              System.out.println();
          }
      

二、mybatis的核心对象

  1. SqlSessionFactory是单个数据库的映射关系经过编译后的内存镜像,用来创建SqlSession;SqlSessionFactory对象的实例通过SqlSessionFactoryBuilder对象来创建;而SqlSessionFactoryBuilder通过XML配置文件或预定好的Configration实例构建出SqlSessionFactory实例;

     //读取配置文件
     InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    //根据配置文件构建SqlSessionFactory工厂
     SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    

    SqlSessionFactory是线程安全,整个执行期间都会存在,建议只创建一个,否则创建过多会造成数据库资源浪费

  2. SqlSession是应用程序和持久层之间进行交互的单线程对象,负责执行持久化操作;SqlSession对象支持所有SQL操作,可以直接使用实例来操作已经映射的SQL语句;每个线程都应有独立的SqlSession实例,不被共享;线程不安全,应用范围最好在一次请求或方法中,绝不能放在静态字段,使用后及时关闭

三、mybatis的参数和返回值

  1. mybatis的参数
    • parameterType(输入类型)
    • 传递简单类型
    • 传递pojo类型
    • 传递pojo包装对象
  2. mybatis的返回值
    • 输出简单类型
    • 输出pojo对象
    • 输出pojo列表

四、mybatis配置细节(configuration的几个标签的使用)

  1. properties标签:配置属性的元素,将外部的配置动态的替换内部定义的属性;

    <!-- 引入数据库连接配置文件 -->
    <properties resource="db.properties" />
    
  2. settings标签:用于改变mybatis运行时的行为,如开启延时缓存、开启延时加载

    <settings>
      <setting name="cacheEnabled" value="true"/>
      <setting name="lazyLoadingEnabled" value="true"/>
      <setting name="multipleResultSetsEnabled" value="true"/>
      <setting name="useColumnLabel" value="true"/>
      <setting name="useGeneratedKeys" value="false"/>
      <setting name="autoMappingBehavior" value="PARTIAL"/>
      <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
      <setting name="defaultExecutorType" value="SIMPLE"/>
      <setting name="defaultStatementTimeout" value="25"/>
      <setting name="defaultFetchSize" value="100"/>
      <setting name="safeRowBoundsEnabled" value="false"/>
      <setting name="mapUnderscoreToCamelCase" value="false"/>
      <setting name="localCacheScope" value="SESSION"/>
      <setting name="jdbcTypeForNull" value="OTHER"/>
      <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
    </settings>
    
  3. typeAliases标签:为配置文件中java类型设置别名

    <typeAliases>
        <!-- 包较少时,自定义包的别名为user-->
    	<typeAlias type="user" alias="com.liu.domain.User" />
        <!-- 包比较多时,自动扫描包来定义别名,默认为实体类对应的包名(基于注解时包名为注解指定的值)-->
        <package name="com.liu.domain"/>
        <!-- 不能同时使用-->
    </typeAliases>
    
  4. typeHandler标签:将预处理语句()中传入的参数从java类型转换为JDBC类型,或从数据库中取出结果时将JDBC类型转化为java类型

    <!-- 自定义类型处理器 -->
    <typeHandlers>
        <!-- 以单个类的形式配置 -->
        <typeHandler handler="com.liu.type.handler" />
        <!-- 注册一个包中所有的类型处理器,系统会在启动时自动扫描包下的所有文件 -->
        <package name="com.liu.type"/>
    </typeHandlers>
    
  5. objectFactory标签:不常用了解即可

  6. plugins标签:不常用了解即可

  7. environments标签:对环境进行配置,主要是数据源的配置

    <!-- 配置环境 -->
        <environments default="mysql">
            <!-- 配置mysql的环境-->
            <environment id="mysql">
                <!-- 配置事务的类型-->
                <transactionManager type="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>
    
  8. mappers标签:指定映射文件的位置

    • 使用类路径引用

      <mappers>
          <mapper resource="mapper/UserDao.xml"/>
      </mappers>
      
    • 使用本地文件路径引用

      <mapper url="file:///c:/com/liu/mapper/UserDao.xml" />
      
    • 使用接口类引用

      <mapper class="com.liu.dao.UserDao" />
      
    • 使用包名引用

      <mapper name="com.liu.dao" />
      

六、映射文件(crud部分见上,此处重点讲sql标签和resultMap标签)

  1. sql标签:定义可重用的SQL代码片段,在其他语句中引用这些片段

    <sql id="UserColumns" >id, username, sex</sql>
    <select id="findUserById" parameterType="Integer" resultType="com.liu.domain.User">
        select <include refid="UserColumns" />
        from user
        where id = #{id}
    </select>
    
  2. resultMap标签:表示结果映射集,定义映射的规则,级联的更新和定义类型转换器等;解决数据表中的列和对象的属性不完全一致

    <!--  resultMap的元素结构  -->
        <resultMap id="" type="">
            <constructor>    <!-- 类在实例化是用来注入结果到构造方法中-->
                <idArg/>    <!-- ID参数,标记结果作为ID-->
                <arg/>      <!-- 注入到构造方法的一个普通结果-->
            </constructor>
            <id/>          <!-- 用于表示哪个列是主键-->
            <result/>       <!-- 注入到字段或JavaBean的普通结果-->
            <association property=""/>      <!-- 用于一对一关联-->
            <collection property=""/>       <!-- 用于一对多关联-->
            <discriminator javaType="">     <!-- 使用结果值来决定使用哪个结果映射-->
                <case value=""></case>      <!-- 基于某些值的结果映射-->
            </discriminator>
        </resultMap>
    

映射文件的动态SQL

  1. 动态SQL中的元素
    在这里插入图片描述

  2. 元素:相当于if···else语句

    <select id="findUserByNameAndSex" resultType="com.liu.domain.User" parameterType="com.liu.domain.User">
        select * from user where
        <if test="username != null and username != ''">
            username = #{username}
        </if>
        <if test="sex != null and sex != ''">
            and sex = #{sex}
        </if>
    </select>
    
  3. ( 、) 元素:相当于switch···case···default语句

    	<select id="findUserByNameOrSex" resultType="com.liu.domain.User" parameterType="com.liu.domain.User">
            select * from user where
            <choose>
                <when test="username != null and username != ''">
                    username = #{username}
                </when>
                <when test="sex != null and sex != ''">
                    and sex = #{sex}
                </when>
                <otherwise>
                    and birthday is not null
                </otherwise>
            </choose>
        </select>
    
  4. 、、元素

    <!-- 将where用<where>标签替换,会自动将多余的and或or删除 -->
    <select id="findUserByNameAndSex" resultType="com.liu.domain.User" parameterType="com.liu.domain.User">
        select * from user
        <where>
            <if test="username != null and username != ''">
                and username = #{username}
            </if>
            <if test="sex != null and sex != ''">
                and sex = #{sex}
            </if>
        </where>
    </select>
    <!-- 将where用<trim>标签替换,prefix属性表示语句的前缀,prefixOverrides表示需要去除的特殊字符串(and) -->
    <select id="findUserByNameAndSex" resultType="com.liu.domain.User" parameterType="com.liu.domain.User">	    	select * from user
        <trim prefix="where" prefixOverrides="and">
            <if test="username != null and username != ''">
                and username = #{username}
            </if>
            <if test="sex != null and sex != ''">
                and sex = #{sex}
            </if>
        </trim>
    </select>
    <!-- set标签主要用于更新操作,并将最后一个多余的逗号去除 -->
    <select id="updateUser" parameterType="com.liu.domain.User">
        update user
        <set>
            <if test="username != null and username != ''">
                and username = #{username},
            </if>
            <if test="sex != null and sex != ''">
                and sex = #{sex},
            </if>
            where id = #{id}
        </set>
    </select>
    
  5. 元素

    <select id="findUserByIds" parameterType="list" resultType="com.liu.domain.User">
        select * from user where id in 
        <foreach item="id" collection="list" index="index" open="(" separator="," close=")" >
            #{id}
        </foreach>
    </select>
    

    item:配置的是循环中当前的元素

    index:当前元素在集合中位置的下标

    collection:list传过来的参数类型(array、list、collection、Map集合的键、POJO包装类中的数组或集合类型的属性名)

    open和close:以什么符号将集合元素包裹起来

    separate:各个元素的间隔符

  6. 元素:模糊查询时实现字符串拼接,${}可能会有SQL注入,因此要用#{},但#{}又只支持mysql;

    <select id="findUserByName" parameterType="com.liu.domain.User" resultType="com.liu.domain.User">
        <bind name="pattern_username" value="'%'+_parameter.getUsername()+'%'"/>
        select * from user
        where username like #{pattern_username}
    </select>
    

三、Mybatis的深入和多表

一、mybatis的连接池

  1. 三种数据源类型:
    • UNPOOLED:在每次请求时都会打开和关闭连接,用于简单的操作,如果有较多的连接而不释放资源时不推荐使用,容易造成服务器故障
    • POOLED:利用连接池将将JDBC对象组织起来,在建立新连接是避免初始化时间和认证时间,快速响应Web请求;防止在过多的连接中造成服务器故障
    • JNDI:在EJB和应用服务器等容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用

二、mybatis的事务管理

  1. 配置两种类型的事务管理器:如果是SSM框架,则事务交由Spring管理

    • JDBC:直接使用JDBC的提交和回滚设置,依赖于从数据源得到的连接来管理事务的作用域
    • MANAGED:不主动提交和回滚一个连接,而是有容器管理事务的整个生命周期;默认关闭连接,用closeConnection=“false”来阻止关闭连接

三、mybatis的多表查询(关联映射:一对多、多对一、多对多)

通过标签中的子元素来处理关联关系

  1. 一对一:

    • 创建两个实体类
    • 编写配置文件(两个)
    • 创建测试类
    <!-- 实体类A的Amapper.xml文件 -->
    <select id="findUserById" parameterType="Integer" resultType="实体类B的一个属性b类型">
        select * from where id = #{id}
    </select>
    <!-- 实体类B的Bmapper.xml文件 -->
    <!--  嵌套查询:通过执行另一条SQL映射语句来返回预期的特殊类型  -->
    <select id="findUserById" parameterType="Integer" resultMap="map1">
        select * from user WHERE id = #{id}
    </select>
    <resultMap id="map1" type="User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="sex" column="sex"/>
        <!-- 一对一:引入另一条SQL语句-->
        <association property="card" column="card_id" javaType="实体类B的一个属性b类型" 				select="mapper.Amapper.findUserById"/>
    </resultMap>
    <!--  嵌套结果:处理重复的联合结果的子集 -->
    ···········
    

    property:映射到实体类对象属性,和数据表字段一一对应

    column:数据表中对应的字段

    javaType:映射到实体对象属性的类型

    select:指定引入的SQL语句,用于关联映射中的嵌套查询

    fetchType:是否启用延迟加载,有lazy(延迟加载)和eager(积极加载)两种,默认lazy延迟加载

  2. 一对多:使用collection元素来实现一对多的关系,将javaType属性替换成ofType属性(其他同上),两种处理方式:嵌套查询、嵌套结果

  3. 多对多:使用collection元素来实现多对多的关系,将javaType属性替换成ofType属性(其他同上),两种处理方式:嵌套查询、嵌套结果

四、Mybatis的缓存和注解开发

一、mybatis中的加载时机(查询时机)

  1. 延迟加载(懒加载):在需要使用数据时才查询,不用时就不查询(加载),适用于一对多和多对多
  2. 立即加载:不论是否使用数据,一旦调用立即加载(查询),适用于多对一和一对一

二、mybatis的一级缓存和二级缓存

  1. 缓存模式

    • 缓存的作用是将数据保存在内存中,一旦用户查询数据,优先从缓存容器中获取数据,不用频繁的查询数据库,这提高了查询性能;一级缓存是SqlSession的缓存,每个SqlSession类的实体对象都有一个HashMap集合来缓存数据,他们之间互不影响;二级缓存是基于mapper的缓存,跨SqlSession的,多个SqlSession的实例对象共同操作同一个Mapper.xml文件中的SQL语句
      在这里插入图片描述
  2. 一级缓存

    • 作用域是SqlSession范围内的,在SQL和参数完全一样的情况下,使用同一个SqlSession对象调用同一个mapper方法,只执行一次SQL,数据会放在缓存中,如果没有刷新缓存且缓存没超时,那么就会取出缓存数据,而不和数据库进行交互;如果SqlSession执行了DML操作并提交到数据库,那么就会清空一级缓存(为了保证缓存中是最新信息,避免脏读),基于id的缓存,使用HashMap时,对象的id作为key,对象作为value
    • 一级缓存的生命周期
      • 如果SqlSession调用了close()方法,会释放一级缓存perpetualChache对象,一级缓存不可用
      • 如果SqlSession调用了clearChache()方法,会清空perpetualChache对象中的数据,对象仍然可用
      • SqlSession执行了任何DML操作,会清空perpetualChache对象中的数据,对象仍然可用
  3. 二级缓存

    二级缓存概述

    1. 二级缓存是基于mapper的缓存,跨SqlSession的,多个SqlSession的实例对象共同操作同一个Mapper.xml文件中的SQL语句,当执行了DML操作时,会清空二级缓存;mybatis默认没有开启二级缓存,因此需要我们主动开启;

    2. 在mybatis的配置文件mybatis-config.xml文件中配置

      <!-- 开启二级缓存 -->
      <settings>
      	<setting name="cacheEnabled" value="true" />
      </settings>
      
    3. 在mapper文件中配置

      <!-- 开启二级缓存 -->
      <cache />
      

    二级缓存的特点

    1. 缓存是以namespace为单位的,每个namespace下的操作互不影响
    2. CRUD会清空二级缓存

    cache-ref共享缓存

    1. 每个mapper分配一个Cache缓存对象(配置)
    2. 多个mapper共享一个Cache缓存对象(配置)
    <!-- mapper1.xml中开启二级缓存 -->
    <mapper namespace="">
        <!-- 必须写,要与和其共享缓存的mapper文件交互 -->
        <cache />
    </mapper>
    <!-- mapper2.xml中开启二级缓存,并指定共享哪个mapper文件的缓存 -->
    <mapper namespace="">
    	<!-- 共享mapper1的二级缓存,要求mapper1.xml中必须有<cache/> -->
        <cache-ref namespace="" />
    </mapper>
    
  4. mybatis缓存的工作原理图
    一级缓存
    在这里插入图片描述

三、mybatis的注解开发(单表CRUD、多表查询)

在这里插入图片描述
在这里插入图片描述
@Select注解的例子

@Select("select * from user")
List<User> findAll();
发布了6 篇原创文章 · 获赞 1 · 访问量 407

猜你喜欢

转载自blog.csdn.net/qq_43544596/article/details/104045093
今日推荐