MyBatis技术总结

  • 什么是MyBatis?

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

  • MyBatis中的主要组成部分

    • Resources:用于读取配置文件,并返回输入流。
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    
    • SqlSessionFactoryBuilder(工厂构造器):根据配置文件来生成SqlSessionFactory

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

    • SqlSessionFactory(SqlSession工厂):用于创建SqlSession对象。

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

      private static SqlSessionFactory sqlSessionFactory;
      sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
    
    • SqlSession(会话):用于完成对数据库的连接、操作以及对结果集的处理等。

    生命周期:每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 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)

    主要作用:设置别名(为实体类设置别名)

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

    主要作用:配置事务管理和连接池

    环境配置(environments)代码示例:
    <environments default="development">
          <!-- 环境1(开发) -->
          <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)

    主要作用:配置映射器,这些映射器的 XML 映射文件包含了SQL代码和映射定义信息

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

    1.常用节点作用总结

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

    2.常用属性作用总结

    • id 属性:为当前映射的SQL语句进行命名
    • resultType 属性:设置查询结果使用的类型名称
    • parameterType 属性:设置参数使用的类型名称
    • useGeneratedKeys属性:开启主键回填
    • keyProperty属性:设置用于保存主键值的属性名称

    3.常见SQL映射示例

    示例1:普通增加

    <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:批量增加

    <!-- 批量添加新答题记录 -->
    <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:普通删除

    <delete id="deleteAnswerRecord" >
              delete from answer_record
              where record_id = #{id}
    </delete>
    
    // 接口方法定义
    int deleteAnswerRecord(int recordId);
    

    示例4:批量删除

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

    示例5:动态修改

      <!-- set节点用于动态处理update语句中的set -->
      <!-- if节点用于条件判断 -->
      <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:动态查询

    <!-- where节点:处理where子句和条件之间的关系 -->
    <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 ar);
    

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

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