Mybatis基础实践

Mybatis特点之——动态SQL

mybatis的动态sql 是基于强大的OGNL表达式来实现的,主要是用来解决 SQL 语句生成的问题。

动态标签的类型:

if、choose (when, otherwise)、trim (where, set)、foreach

if
需要判断的时候,条件写在 test 中进行使用
eg:

<select id="findActiveBlogWithTitleLike" resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

choose (when, otherwise)
需要选择一个条件的时候使用。
eg:

<select id="findActiveBlogLike" resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

trim (where, set)
需要去掉 where、and、逗号之类的符号的时候使用。
eg:

<insert  id" ="insert"  parameterType ="com.vincent.bean.Employee">
    insert into tbl_emp
    <trim  prefix" ="("  suffix" =")"  suffixOverrides =",">
        <if  testd ="empId = !=  null">
            emp_id,
        </if>
        <if  teste ="empName = !=  null">
            emp_name,
        <if>
        <if  testd ="dId = !=  null">
            d_id,
        </if>
    </trim>
    <trim  prefixs ="values " ("  suffix" =")"  suffixOverrides =",">
        <if  testd ="empId = !=  null">
            #{empId,jdbcType=INTEGER},
        </if>
        <if  teste ="empName = !=  null">
            #{empName,jdbcType=VARCHAR},
        </if>
        <if  testd ="dId = !=  null">
            #{dId,jdbcType=INTEGER},
        </if>
    </trim>
</insert>

foreach
需要遍历集合的时候使用
eg:

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

批量操作

批量插入

eg:
Java代码中,直接传入一个List类型参数
在Mapper文件里面,使用foreach标签拼接values部分语句

<!-- 批量插入 -->
<insert  id" ="batchInsert"  parameterType ="java.util.List" useGeneratedKeys ="true">
    <selectKey  resultType" ="long"  keyProperty" ="id"  order ="AFTER">
        SELECT LAST_INSERT_ID()
    </selectKey>
        insert into tbl_emp (emp_id, emp_name, gender,email, d_id)
        values
    <foreach  collection" ="list"  item" ="emps"  index ="index" separator =",">
    ( #{emps.empId},#{emps.empName},#{emps.gender},#{emps.email},#{emps.dId} )
    </foreach>
</insert>

这种做法效率要比循环发送 SQL 执行要高得多。最重要的就在于减少了跟数据库交互的次数,并且避免了开启和结束事务的时间消耗。

批量更新

Mapper 文件里面最关键的就是 case when 和 where 的配置。
eg:

<update  id ="updateBatch">
    update tbl_emp set emp_name =
    <foreach  collection" ="list"  item" ="emps"  index" ="index"  separator" =" " "  opene ="case  emp_id" close ="end">
        when #{emps.empId} then #{emps.empName}
    </foreach>
        ,gender =
    <foreach  collection" ="list"  item" ="emps"  index" ="index"  separator" =" " "  opene ="case  emp_id" close ="end">
        when #{emps.empId} then #{emps.gender}
    </foreach>
        ,email =
    <foreach  collection" ="list"  item" ="emps"  index" ="index"  separator" =" " "  opene ="case  emp_id" close ="end">
        when #{emps.empId} then #{emps.email}
    </foreach>
        where emp_id in
    <foreach  collection" ="list"  item" ="emps"  index" ="index"  separator" =","  open ="(" close =")">
        #{emps.empId}
    </foreach>
</update>

batch Executor

对于批量操作,数据量大的时候,拼接出来的sql语句会过大而报错,
MySQL 的服务端对于接收的数据包有大小限制,max_allowed_packet 默认是4M,需要修改默认配置才可以解决这个问题。

在我们的全局配置文件中,可以配置默认的 Executor 的类型。其中有一种BatchExecutor。
<setting name" ="defaultExecutorType" value" ="BATCH" />

也可以在创建会话的时候指定执行器类型:
SqlSession session = sqlSessionFactory.openSession(ExecutorType. BATCH );

嵌套(关联)查询/ N+1 / 延迟加载

映射结果的2个标签:resultType、resultMap
都是属于select标签的一个属性

用法:
结果集的列和实体类的属性可以直接映射的用resultType

返回的字段无法直接映射的用resultMap来建立映射关系

关联查询2种配置方式(1对1)

1、 嵌套结果
eg:

<!-- 根据文章查询作者,一对一查询的结果,嵌套查询 -->
<resultMap  id ="BlogWithAuthorResultMap"
type ="com.vincent.associate.BlogAndAuthor">
    <id  column" ="bid"  property" ="bid"  jdbcType ="INTEGER"/>
    <result  column" ="name"  property" ="name"  jdbcType ="VARCHAR"/>
    <!-- 联合查询,将 author 的属性映射到 ResultMap -->
    <association  property" ="author"  javaType ="com.vincent.domain.Author">
        <id  column" ="author_id"  property ="authorId"/>
        <result  column" ="author_name"  property ="authorName"/>
    </association>
</resultMap>

2.嵌套查询
eg:

<!-- 联合查询 (一对一)的实现,但是这种方式有“N+1”的问题 -->
<resultMap  id" ="BlogWithAuthorQueryMap"  type ="com.vincent.associate.BlogAndAuthor">
    <id  column" ="bid"  property" ="bid"  jdbcType ="INTEGER"/>
    <result  column" ="name"  property" ="name"  jdbcType ="VARCHAR"/>
    <association  property" ="author"  javaType ="com.vincent.domain.Author"
    column" ="author_id"  select ="selectAuthor"/> <!-- selectAuthor 定义在下面-->
</ resultMap>
<!-- 嵌套查询 -->
<select  id" ="selectAuthor"  parameterType" ="int"  resultType ="com.vincent.domain.Author">
    select author_id authorId, author_name authorName
    from author where author_id = #{authorId}
</ select>

由于是分2次查询,查询完员工信息之后,会再发送一条sql去查询部门信息,如果只执行了一次查询员工信息的 SQL(所谓的 1),如果返回了 N 条记录,就会再发送 N 条到数据库查询部门信息(所谓的 N),这个就是我们所说的 N+1 的问题。这样会白白地浪费应用和数据库的性能。

解决办法:利用延迟加载(懒加载)

在setting 标签里面可以配置:

<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false -->
<setting  name" ="lazyLoadingEnabled"  value ="true"/>
<!--当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过 select 标签的fetchType 来覆盖-->
<setting  name" ="aggressiveLazyLoading"  value ="false"/>
<!-- Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认 JAVASSIST -->
<setting  name" ="proxyFactory"  value" ="CGLIB" />

lazyLoadingEnabled 决定了是否延迟加载。
aggressiveLazyLoading 决定了是不是对象的所有方法都会触发查询。

数据源(DataSource)和连接池(Connection Pool)

数据源就是数据的来源,一个数据源就对应一个数据库。在 Java 里面,它是对数据库连接的一个抽象,一般的数据源都会包括连接池管理的功能

用连接池的好处
提升性能,提高资源利用率

常用的连接池有:
c3p0:Hibernate 内置的数据库连接池
dbcp:Apache 项目,Tomcat 自带
Druid:阿里开源产品,为监控而生
HikariCP:Springboot 2.0 默认数据库连接池

Mybatis的连接池:
MyBatis 内部的连接池,在 settings 里面配置 type="POOLED"就可以了。

可以自定义连接池,通过实现DataSource接口来自定义

Druid连接池
阿里开源的 Druid 是一个号称“为监控而生”的数据源

1.核心配置

<bean  id="dataSource"  class ="com.alibaba.druid.pool.DruidDataSource"  init-method ="init"
destroy-method ="close">
    <!-- 基本属性 url、user、password -->
    <property  name="driverClassName"  value ="${druid.driverClassName}" />
    <property  name="url"  value="${druid.url}" />
    <property  name="username"  value="${druid.username}" />
    <property  name="password"  value="${druid.password}" />
    <!-- 配置初始化大小、最小、最大 -->
    <property  name="maxActive"  value="${druid.maxActive}" />
    <property  name="initialSize"  value="${druid.initialSize}" />
    <property  name="minIdle"  value="${druid.minIdle}" />
    <!-- 配置获取连接等待超时的时间 -->
    <property  name="maxWait"  value="${druid.maxWait}" />
    <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
    <property name ="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}"/>
    <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
    <property  name ="minEvictableIdleTimeMillis" value="${druid.minEvictableIdleTimeMillis}" />
    <property  name="testWhileIdle"  value="${druid.testWhileIdle}" />
    <property  name="testOnBorrow"  value="${druid.testOnBorrow}" />
    <property  name="testOnReturn"  value="${druid.testOnReturn}" />
    <!-- 打开 PSCache,并且指定每个连接上 PSCache 的大小 -->
    <property  name="poolPreparedStatements"value="${druid.poolPreparedStatements}" />
    <property  name="maxOpenPreparedStatements" value ="${druid.maxOpenPreparedStatements}"/>
    <!-- 配置监控统计拦截的 filters -->
    <property  name ="filters"  value="${druid.filters}" />
    <property  name ="connectionProperties" value="config.decrypt=true;config.decrypt.key=${druid.public.key}" />
</ bean>

2.密码加密配置
Druid 也提供了加密工具,可以产生一对公钥和私钥。我们用私钥来对密码进行加密,把加密后的密码和私钥配置在配置文件中,应用在使用的时候会自动还原成明文。可以用 jar 包或者工具类的方式对密码进行加密。

翻页

逻辑翻页
逻辑翻页是属于假分页,是把所有数据查出来,然后在内存中删选数据

物理翻页
物理翻页是真正的分页,如 MySQL 使用 limit 语句,Oracle 使用 rownum 语句,SQLServer 使用 top 语句。

翻页插件——PageHelper
PageHelper 是通过 MyBatis 的拦截器实现的,它会根据 PageHelper 的参数,改写我们的 SQL 语句。如 MySQL会生成 limit 语句,Oracle 会生成 rownum 语句,SQL Server 会生成 top 语句。

Mybatis-Plus

MyBatis-Plus是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

核心功能:
通用 CRUD:定义好 Mapper 接口后,只需要继承 BaseMapper<T> 接口即可获得通用的增删改查功能,无需编写任何接口方法与配置文件。

条件构造器:通过 EntityWrapper<T>(实体包装类),可以用于拼接 SQL 语句,并且支持排序、分组查询等复杂的 SQL。

代码生成器:支持一系列的策略配置与全局配置,比 MyBatis 的代码生成更好用。

分页插件

<!-- spring xml 方式 -->
<plugins>
    <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
        <property name="sqlParser" ref="自定义解析类、可以没有" />
        <property name="dialectClazz" value="自定义方言类、可以没有" />
    </plugin>
</plugins>

<!--Spring boot方式-->
@EnableTransactionManagement
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {
    /**
     * 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // paginationInterceptor.setLimit(你的最大单页限制数量,默认 500 条,小于 0 如 -1 不受限制);
        return paginationInterceptor;
    }
}

Sequence主键

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

推荐阅读更多精彩内容