2020-08-23

MyBatis技术总结

分类:JavaEE框架技术原创文章
作者:幸运王

  • 什么是MyBatis?

    MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

  • MyBatis中的主要组成部分

    SqlSessionFactoryBuilder( 工厂构造器,使用了建造者模式 ) :根据配置文件来生成SqlSessionFactory

    生命周期及其作用域:这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

    SqlSessionFactory(SqlSession工厂,使用到了工厂模式):创建SqlSession。
    生命周期及其作用域:SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

    SqlSessionFactory创建代码示例:
      private static final String CONFIG = "mybatis-config.xml"; //配置文件
      private static SqlSessionFactory sqlSessionFactory; //SqlSessionFactory对象
      private static final Class<MyBatisUtils> CLASS_LOCK = MyBatisUtils.class; //类级别锁
    static {
          //初始化
      initSqlSessionFactory();
      }
    
      //私有的构造方法,保证MyBatis工具类不会被实例化
      private MyBatisUtils() {
      }
    
      private static SqlSessionFactory initSqlSessionFactory() {
          if (sqlSessionFactory == null) {
              synchronized (CLASS_LOCK) {
                  try (InputStream inputStream = Resources.getResourceAsStream(CONFIG)) {
                      sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
          return sqlSessionFactory;
      }
      public static SqlSession openSqlSession() {
          if (sqlSessionFactory == null) {
              initSqlSessionFactory();
          }
          return sqlSessionFactory.openSession();
      }
    

    SqlSession(会话):用于执行SQL并返回结果(创建数据库连接+创建数据库执行操作对象+返回结果集)
    生命周期及其作用域:每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 try 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

    SqlSession创建代码示例:
     try (SqlSession session = sqlSessionFactory.openSession()) {
      // 应用逻辑代码
     }
    
  • MyBatis配置

    • 配置文件(properties)

    主要作用:加载外部的properties文件

    配置文件(properties)代码示例:
      <properties resource="jdbc.properties"/>
    
    • 设置(settings)

    主要作用:MyBatis框架运行规则配置

    设置(settings)代码示例:
    <settings>
    <!-- 开启日志 -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    
    • 类型别名(typeAliases)

    主要作用:设置别名(为实体类设置别名),可以分别为每个实体类设置别名,也可以统一为package中的每个实体类自动设置别名

    类型别名(typeAliases)代码示例:
    <typeAliases>
    <!-- 为每一个实体设置别名 -->
    <!-- 
    <typeAlias type="com.apesource.entity.Employee" alias="Employee"/>
    <typeAlias type="com.apesource.entity.Order" alias="Order"/>
     -->
    <!-- 设置实体类包,为该package中的每个实体类自动设置别名 -->
    <package name="com.apesource.entity"/>
    </typeAliases>
    
    • 环境配置(environments)

    主要作用:元素体中包含了事务管理和连接池的配置

    环境配置(environments)代码示例:
    <environments default="development">
    <!-- 环境(开发) -->
    <environment id="development">
        <!-- 事务管理器:采用JDBC事务 -->
        <transactionManager type="JDBC" />
        <!-- 数据源(数据库连接池) -->
        <!-- type设置为pooled,启动数据库连接池 -->
        <dataSource type="POOLED">
            <!-- 数据库连接参数 -->
            <property name="driver" value="${driver_class}" />
            <property name="url" value="${jdbc_url}" />
            <property name="username" value="${db_username}" />
            <property name="password" value="${db_password}" />
        </dataSource>
    </environment>
    </environments>
    
    • 映射器(mappers)

    主要作用:包含了一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息

    映射器(mappers)代码示例:
    <mappers>
    <!-- SQL映射文件 -->
    <mapper resource="com/apesource/dao/mapper/AnswerRecordMapper.xml" />
    </mappers>
    
  • MyBatis XML 映射器

    1.常用节点作用总结

    • select:映射select查询SQL语句
    • update:映射update修改SQL语句
    • delete:映射delete删除SQL语句
    • insert:映射insert增加SQL语句

    2.常用属性作用总结

    • id 属性:为当前映射的SQL语句进行命名,id应该与xxxMapper接口下的方法名一致
    • resultType 属性:设置查询结果使用的类型名称,
    • parameterType 属性:设置执行操作时使用的参数类型
    • useGeneratedKeys属性:设置为true表示开启主键回填(默认为false)
    • keyProperty属性:设置用于保存主键值的属性名称

    3.常见SQL映射示例

    示例1:普通增加

    SQL映射配置
    <insert id="insertAnswerRecord"
          parameterType="AnswerRecord"
          useGeneratedKeys="true"
          keyProperty="recordId">
    insert into answer_record(respondent,question,right_answer,submit_answer,submit_datetime)values(#{respondent},#{question},#{rightAnswer},#{submitAnswer},now())
    </insert>
    
    接口方法定义
    int insertAnswerRecord(AnswerRecord answerRecord);
    
    

    示例2:批量增加

    SQL映射配置
    <insert id="insertAnswerRecordBatch" 
        parameterType="list"
        useGeneratedKeys="true" 
        keyProperty="recordId">
    INSERT INTO
    answer_record(respondent,question,right_answer,submit_answer,submit_datetime)VALUES
    <foreach collection="list" item="record" separator=",">
        (
            #{record.respondent},
            #{record.question},
            #{record.rightAnswer},
            #{record.submitAnswer},
            now()
        )
    </foreach>
    </insert>
    
    
    接口方法定义
    int insertAnswerRecordBatch(List<AnswerRecord> answerRecordList);
    
    

    示例3:普通删除

    SQL映射配置
    <delete id="deleteAnswerRecord">
    delete from answer_record
    where record_id = #{id}
    </delete>
    
    
    接口方法定义
    int deleteAnswerRecord(@Param("id")int recordId);
    
    

    示例4:批量删除

    SQL映射配置
    <delete id="deleteAnswerRecordBatch"
        parameterType="list">
    DELETE FROM answer_record
    WHERE record_id IN
    <foreach collection="list" item="rid" separator="," open="(" close=")">
        #{rid}
    </foreach>
    </delete>
    
    
    接口方法定义
    int deleteAnswerRecordBatch(List<Integer> recordIdList);
    
    

    示例5:动态修改

    SQL映射配置
    <update id="updateAnswerRecord" 
        parameterType="AnswerRecord">
    update answer_record
    <set>
        <if test="respondent!=null">respondent = #{respondent},</if>
        <if test="question!=null">question = #{question},</if>
        <if test="rightAnswer!=null">right_answer = #{rightAnswer},</if>
        <if test="submitAnswer!=null">submit_answer = #{submitAnswer},</if>
        submit_datetime = now()
    </set>
    where record_id = #{recordId}
    </update>
    
    
    接口方法定义
    int updateAnswerRecord(AnswerRecord answerRecord);
    
    

    示例6:动态查询

    SQL映射配置
    <select id="listAnswerRecordByCondition"
        resultType="AnswerRecord" 
        parameterType="AnswerRecord">
    SELECT record_id AS recordId,
    respondent,
    question,
    right_answer AS rightAnswer,
    submit_answer AS submitAnswer,
    submit_datetime AS submitDatetime
    FROM answer_record
    <where>
        <if test="respondent != null">AND respondent = #{respondent}</if>
        <if test="question != null">AND question LIKE concat('%',#{question},'%')</if>
        <if test="rightAnswer != null">AND right_answer = #{rightAnswer}</if>
        <if test="submitAnswer != null">AND submit_answer = #{submitAnswer}</if>
    </where>
    </select>
    
    
    接口方法定义
    List<AnswerRecord> listAnswerRecordByCondition(AnswerRecord condition);
    
    

    示例7:查询结果封装为Map

    SQL映射配置
    <select id="countAnswerRecordDataByRespondent"
        resultType="map">
    SELECT count(record_id) as total,
         (SELECT count(record_id) FROM answer_record WHERE respondent = #{name} AND right_answer = submit_answer ) as right_count,
         (SELECT count(record_id) FROM answer_record WHERE respondent = #{name} AND right_answer != submit_answer ) as fail_count
    FROM answer_record
    WHERE respondent = #{name}
    </select>
    
    
    接口方法定义
    Map<String,Integer> countAnswerRecordDataByRespondent(@Param("name") String respondent);
    
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351