Mybatis的相关

1.Mybatis的介绍

Mybatis是针对持久层的框架,能力只能和数据库进行交互

Mybatis的底层也并没有其他新奇的东西,只是对jdbc的封装

Mybatis是一个半自动化的ORM框架

  1. ORM(Object,Relation,Mapping)
  2. Java中的实体类通过映射文件和关系表映射到一起,以面向对象的方式和实体类进行交互,操作会持久化到数据库的关系表

 由于Mybatis是半自动化的ORM框架,Mybatis是将sql语句的返回结果和实体类进行映射

我所了解的自动化的ORM框架有hibernate,他可以实现包括连sql语句都为用户生成,但是由于我们在开发的过程中,sql语句的编写具体是依赖于具体的业务需求,这一点他无法帮我们实现,所以逐渐被Mybatis替代

2.Mybatis基于mapper接口实现

Mybatis提供了基于动态代理的方式实现与数据库的交互

提供好mapper层的接口,提供好接口层对应的映射文件,Mybatis会基于接口的映射文件自动的创建一个代理对象(接口的实现类)

代理对象内部会自动的调用Sqlsession的select,insert等方法与数据库交互

当前这种实现的方式需要满足4个要求

  1. 接口名与映射文件名称保持一致
  2. 接口与映射文件放在同一目录下(后期可以不满足)
  3. 映射文件的namespace属性写接口的全路径
  4. 接口中的抽象方法和映射文件的sql一一对应(方法名&id,返回结果&resultType,方法参数&parameterType )

 

 

 PS:如果出现org.apache.ibatis.binding.BindingException异常,说明四个要求没有满足……

3.Mybatis的核心类

  1. Resources:是用于加载配置文件生成流,反射也可以实现
  2. SqlSessionFactoryBuilder:构建SqlSession工厂,由于工厂一个足矣,所以SqlSessionFactoryBuilder生命周期用过即丢
  3. SqlSessionFactory:应当保证SqlSessionFactory全局唯一
  4. SqlSession:与数据库交互的核心对象
  5. Executor:SimpleExecutor底层调用了原生的JDBC,与数据库交互

 以下是Mybatis中的executor(执行器):

 默认使用的是SimpleExecutor,最简单的执行器,直接根据sql执行,不会进行额外的操作

ReuseExecutor:可重用的执行器,重用的对象是Statement,也就是说该执行器会缓存同一个sql的Statement,省去Statement的重新创建,优化性能

BatchExecutor,执行批量操作的执行器

CachingExecutor:启用于二级缓存时的执行器.

4.Mybatis的核心配置文件

4.1 properties

可以将连接数据库的信息保存在外部的properties文件中,通过properties标签引入外部的properties文件,通过${key}的方式引入properties文件中的value

4.2 settings 

设置Mybatis的一些运行时的信息

具体可以查看官网的配置信息:mybatis – MyBatis 3 | 配置

介绍一下常用的驼峰映射的开启:

4.3  typeAliases(了解)

基本不会使用,了解型的知识

别名,在映射文件编写SQL时,传入参数或者返回结果是Entity实体类,需要编写实体类的全路径。

由于全路径太长,可以通过typeAliases配置别名,编写映射文件时,更方便

 4.4  plugins

4.5  environments

环境

4.5.1 transactionManager标签

 事务的话后期我们都是交给spring去管理的,这里的话不需要太多的关注

4.5.2 dataSource标签

 "POOLED"这是mybatis内置的一款数据源,实际的开发中我们一般使用的都是阿里的德鲁伊连接池,这里为了方便测试,使用的是内置的数据源

4.6 mappers

加载外部的映射文件

<mappers>

这个是扫描使用逆向工程生成的xml文件:
    <!--        resource: 编写类路径
                url: 编写配置文件绝对路径
                class:基于Mapper接口实现时的Mapper接口
                如果后期项目表较多,需要引入大量的mapper标签,成本太高
    -->
    <!--        <mapper resource="com/项目名/mapper/UserMapper.xml"/>-->

这个扫描的是使用逆向工程生成的mapper接口:
    <!--        采用package标签,扫描包的形式找映射文件-->
    <package name="com.项目名.mapper"/>
</mappers>

5.Mybatis的映射配制文件(mapper.xml文件)_重要

5.1 select(引入参数,传入多个参数)

     在编写select查询语句时,需要将语句放到select标签内

select标签常用的属性:

       id:与抽象方法的方法名一致

       resultType:与抽象方法的返回值一致,如果返回的结果是List或者Set,需要写泛型内的类型

       parameterType:与抽象方法的参数类型一致,如果传入一个参数可以使用parameterType,多个参数后面建议使用注解的方式

穿插的小问题:

①为什么#{}可以防止SQL注入,为什么${}不能防止SQL注入,什么时候用${}

#{}:可以防止SQL注入,采用的是预编译的方式,使用的是PreparedStatement,而且使用了占位符

${}:不可以防止SQL注入,使用的是直接将值拼接到SQL语句上,使用的是Statement,使用的场景:当我们传入的参数要作为字段名,表名等内容时,必须使用${}

②传入多个参数的处理方案(只写建议使用的)

<!--
    /**
    * 根据名字和年龄范围查询用户信息
    * @return
    */
    List<User> findByNameAndAgeLessThan(@Param(value = "name") String name,@Param("lessAge") Integer age);
-->


<!--<select id="findByNameAndAgeLessThan" resultType="User">
        select * from user where name = #{name}  and age <![CDATA[ < ]]> #{lessAge}
    </select>-->
<select id="findByNameAndAgeLessThan" resultType="User">
    select * from user where name = #{name}  and age &lt;= #{lessAge}
</select>

在mapper接口中使用@Param()注解指定,mapper.xml文件中无法使用一些常用的特殊符号,只能使用转义字符,比如上面&lt相当于<;建议使用<![CDATA[ < ]]>,这个符号里写啥代表啥

③采用实体类传参,传入实体类后,在映射文件通过parameterType设置类型,通过属性名获取属性值

④采用Map传参 ,传入参数比较复杂,比较多时,可以采用Map传参  

 5.2 insert

增删改操作的返回结果都是int类型,代表几行受影响

如果添加的数据表主键生成的策略是自增,要获取自增后的主键值,Mybatis提供了两种方案(主键回填)

在<insert>标签中,添加两个属性useGeneratedKeys="true"keyProperty="实体类的oid名称"

在<insert>标签体内,追加额外的标签,采用MySQL的函数获取自增后的主键值

具体实现:

 5.3 update和delete

      正常的修改和删除,没有什么特别之处

 5.4 resultMap

编写<select>标签时,需要编写resultType属性,目的是将ResultSet结果集封装到resultType类型中。

因为select语句的返回结果的列名与实体类的属性名一致,所以可以自动映射到实体类中……

如果SQL语句返回结果的列名与实体类的属性名不一致,需要手动映射结果集和实体类……

MyBatis提供了<resultMap>标签,来做手动映射操作。

默认情况下,MyBatis的autoMappingBehavior属性为Partial,代表没有嵌套结果可以自动映射,但是,即便提供了自动映射,只要用了<resultMap>就全部手动映射。

6. Mybatis的多表查询

6.1多对一关系

关系表:

实体类:

 映射文件:

PS:resultMap中,这里的映射为一个实体类,所以使用的是association,与下面的集合映射区分开

 6.2 一对多关系

关系表:

实体类: 

 映射文件:

 PS:这里的映射是一个集合,所以使用的是collection,注意区分

6.3 多对多关系

关系表

 实体类

 映射文件:

7.动态sql

7.1 if+where

if标签解决了逻辑判断问题,可以在判断非空后,再拼接到SQL中

where标签解决了条件前多一个and/or,或者where中没有查询条件时的问题

 比如上述的sql语句,如果传入的实体类中的brand字段为空,因为我们加了if标签,brand为null的情况下,动态sql就不会为我们拼接,假如下面的两个字段不为空的话,如果没有where标签,此时的sql语句就是  select * from shoe where  and color="" and style="",很显然,这样的语句不满足sql的语法就会执行错误,where标签就可以帮助我们解决这个问题.

7.2 if+set

if标签解决了逻辑判断问题,可以判断非空后,再拼接到SQL中

set可以代替SET关键字,并且去掉后缀多余的一个,

Ps:和上面的where基本一样,不做赘述

7.3 if+trim 

trim可以实现set标签和where标签的功能

trim提供了四个属性:

7.4 foreach  批量操作

如果SQL语句的拼接需要用到循环的方式,在映射文件中可以使用<foreach>

<foreach 
    collection="array或者list或者map-key" 
    open="以什么开始" 
    separator="间隔" 
    close="以什么结束" 
    item="遍历出来的值的别名"
    index="遍历次数">
    #{遍历出来的值的别名},  #{遍历次数}
</foreach>    

Ps:foreach的执行的本质就是sql的拼接,细品 

8.Mybatis的缓存

8.1 Mybatis的一级缓存

一级缓存的特点:

  • 默认开启
  • 作用域是sqlSession级别的(sqlSession对象中的一个hashMap)
  • 一级缓存直接缓存的是对象
  • 一级缓存基于BaseExecutor实现,直接找PerpetualCache查询map中的数据

 执行流程:userMapper.select(参数) ---- DefaultSqlSession.selectList() ----- BaseExecutor.query()查询PerpetualCache中的Map是否有数据,有数据直接返回,没有数据查询数据库,将查询结果存放到一级缓存.

8.2 二级缓存

二级缓存的特点:

  • 二级缓存有三开关,全局开关默认开启,映射文件开关默认关闭,select语句开关默认开启
  • 二级缓存的作用域是SqlSessionFactory级别
  • 二级缓存存储对象序列化的byte[]
  •  二级缓存基于CachingExecutor实现,二级缓存会结果一系列的Cache链(Synchronized,Logging,Serialized,Lru),最终找到PerpetualCache中的Map集合
  • 二级缓存优先级高于一级缓存……

 执行流程:userMapper.select(参数) ---- DefaultSqlSession.selectList() ----- CachingExecutor.query(),经历一套Cache链,然后查询PerpetualCache中的Map是否有数据,有数据直接返回,没有数据查询一级缓存,如果一级缓存没有,查询数据库,将查询结果存放到一级缓存,在commit提交事务后,将数据存储到二级缓存

 

9.Mybatis的插件 

9.1 分页插件

采用的MyBatis支持的PageHelper分页插件

9.1.1 导入依赖

<!--        分页助手-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>

9.1.2 编写核心配置文件

<!--        插件-->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

9.1.3 实现分页效果

// 设置当前页,和每页显示条数      11条
PageHelper.startPage(1,5);
List<User> userList = userMapper.findAll();
PageInfo<User> pageInfo = new PageInfo<>(userList);

 使用这样的方式实现分页,我们的返回的对象就是PageInfo类型的,然后将查询出来的数据直接放入new出来的pageInfo对象中(注意这是使用的传统分页的方式,基本的分页效果可以实现,但是存在问题)

问题的描述:

这是我们使用传统分页的语句:

 select * from studnet order by id desc limit (pageNumber-1)*pageSize,pageSize

假设查到的数据如下图:

此时假设我们前端用户每页展示的数据条数为3条,如下图:

此时假设有小编在后台添加了多条数据,传统的分页就会出现问题,如下假设小编添加了3条数据

此时为前端用户假如请求第二页的数据展示数据为: 

 

用户发现数据没有发生变化,出现了问题

Ps:解决的方法:每次将前端用户展示的数据的最后一条的数据的id传给后端

使用下面的语句可以解决传统分页存在的问题:

求下一页数据: select id,name,age from user where id<4 and id>=4-20 order

by id desc limit 2

请求第一页数据: select id,name,age from user where id<=(select max(id) from user) and id>(select convert(max(id),SIGNED)-20 from user) order by id desc limit 2

10.逆向工程

根据表自动生成实体类,mapper接口,mapper映射文件,并且编写好全部单表的增删改查。

MyBatis Generate是MyBatis官方提供的功能,IDEA中有一个款插件,更方便的使用这款插件。

10.1 下载插件

下载Free MyBatis Plugins

10.2 用IDEA的database连接数据库  

 10.3 生成实体类,Mapper接口,Mapper映射文件

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值