秋招—Mybatis

1. Mybatis介绍

Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。

MyBatis 可以使用 XML 或注解来配置和映射原生信息将 POJO映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

通过xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过java对象和 statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。(从执行sql到返回result的过程)。

2. Mybaits的优点和缺点

优点:

基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。

与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接

很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)

能够与Spring很好的集成

提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。

缺点:

SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。

SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

3. MyBatis与Hibernate的不同

相同点:基本上工作流程是一样的。

Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句

Mybatis直接编写原生态sql,可以严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需求变化要求迅速输出成果。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件,则需要自定义多套sql映射文件,工作量大。

Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用hibernate开发可以节省很多代码,提高效率。不需要手动写SQL语句,只需要操作对象。

4. MyBatis中的工作原理

  1. 系统启动的时候会加载解析全局配置文件和对应的mapper映射文件。加载解析的相关信息会存储在Configuration对象中。
  2. 通过建造者模式的sqlSessionFactoryBuilder中的build方法来完成sqlSession工厂的创建。Configuration对象会跟sqlSessionFactory绑定,这个工厂是单例存在的。
  3. 通过上面的工厂对象获取sqlSession对象。sqlSession底层是通过jdbc实现的。
  4. 通过两个方法操作数据库
    • 通过sqlSession提供的方法操作
    • 通过getMapper获取自己实现的sql语句。sqlSession.getMapper(UserMapper.class);
  5. 关闭

5. 介绍MyBatis的缓存

1.缓存的作用

减低数据源的访问频率。从而提高数据源的处理能力。或者提高服务器的响应速度。

2.缓存的设计

MyBatis中的缓存的架构

MyBatis提供了catch接口和实现来进行缓存处理,同时又使用了装饰者模式对其进行增强。比如阻塞的,先进先出的,日志的,调度的一些增强策略。

MyBatis中一级缓存和二级缓存

默认开启一级缓存。

  • 一级缓存:session级别的。通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就
    会从缓存中直接获取,不会从数据库重新访问。
  • 二级缓存:sqlSessionFactory级别的。通过同一个SqlSessionFactory创建的SqlSession查询的结果会被
    缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。

使一级缓存失效的四种情况:

  • 不同的SqlSession对应不同的一级缓存
  • 同一个SqlSession但是查询条件不同
  • 同一个SqlSession两次查询期间执行了任何一次增删改操作
  • 同一个SqlSession两次查询期间手动清空了缓存

一级缓存的关闭

在配置文件中 设置localCacheScope为STATEMENT

二级缓存的开启

全局配置属性cacheEnabled=“true”,默认为true

在mapper映射文件中设置cache/标签

二级缓存必须在SqlSession关闭或提交之后有效

查询的数据所转换的实体类类型必须实现序列化的接口

使二级缓存失效的情况:
两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效

MyBatis缓存查询的顺序

先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。

如果二级缓存没有命中,再查询一级缓存

如果一级缓存也没有命中,则查询数据库

SqlSession关闭之后,一级缓存中的数据会写入二级缓存

因为二级缓存的作用域sqlSessionFactory级别 比较广 百分之90都能找到。

一级缓存的作用域是sqlSession级别的,比较窄 百分之5能找到。

假设先走一级,很大概率找不到,还要走二级缓存,效率就不高!

如果让某个映射文件不走二级缓存,则在查询的标签设置useCache=“false”

6. #和$的区别

#{}是预编译处理,${}是字符串替换。

Mybatis在处理#{}时,会将sql中的**#{}替换为?号**,调用PreparedStatement的set方法来赋值

Mybatis在处理${}时,就是把**​${}替换成变量的值**。有sql注入的风险。

使用**#{}可以有效的防止SQL注入**,提高系统安全性。

7. 实体类中的属性名和表中的字段名不一致

第1种:通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。

<select id=”selectorder” parametertype=”int” resultetype=”me.gacl.domain.order”>         
    select order_id id, order_no orderno ,order_price price   
    form orders   
    where order_id=#{id};      
</select> 

第2种:通过来映射字段名和实体类属性名的一一对应的关系。(resultMap)

<select id="getOrder" parameterType="int" resultMap="orderresultmap">  
    select * from orders where order_id=#{id}  
</select>  

<resultMap type="me.gacl.domain.order" id="orderresultmap">  
    <!--用id属性来映射主键字-->  
    <id property="id" column="order_id"/>  
    <!--用result属性来映射非主键字段,property为实体类属性名,column为数据表中的属性-->  
    <result property = "orderno" column ="order_no"/>  
    <result property="price" column="order_price"/>  
</resultMap>  

8.获取自增的主键

在insert标签中设置 useGeneratedKeys=“true” keyProperty=“id”

  • useGeneratedKeys:设置使用自增的主键
  • keyProperty:因为增删改有统一的返回值是受影响的行数,因此只能将获取的自增的主键放在传输的参数user对象的某个属性中

9. MyBatis用到的设计模式

  1. 缓存模块:装饰者模式
  2. 日志模块:用到了适配者模式
  3. sqlSessionFactory:用到了工厂模式
  4. Mapper接口:代理模式
  5. sqlSessionFactoryBuilder:建造者模式

10. 映射文件中的标签

sql:SQL代码片段

cache:缓存

cache-ref:缓存映射

resultMap:处理结果集

resultType:自动映射,用于属性名和表中字段名一致的情况

resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况

全局配置文件中:

properties:引入properties文件,

settings,typeHandler,interceports,elements,

mappers:引入映射文件

typeAlias:设置某个具体的类型的别名

11. MyBatis中的分页

分为逻辑分页物理分页

逻辑分页:假设数据库里有1w条数据,它会把这些数据一次性放到服务器里,假设客户端显示5条,它会从服务器里返回5条。不是真正的分页。通过RowBounds来进行分页的。

物理分页:根据客户端的要求,取出5条放到服务器,服务器相应给客户端。通过拦截器来实现的。

通过插件

将sql语句进行拼接,重写sql

select * from... ---->  select * from ... limit 0,10

12. MyBatis中的执行器

SimpleExecutor:简单的执行器,每次执行操作都会开启一个新的Statement对象,用完就会立刻关闭。

ReuseExecutor:重复使用的执行器,实现了对Statement对象复用。

BatchExecutor:可执行批处理任务的执行器。

所有的执行器都必须在SqlSession的生命周期内。

13.使用Mapper接口需要注意的操作

  1. mapper接口的接口名要和mapper.xml文件名称保持一致。
  2. mapper接口的方法名要在mapper.xml中存在相同的id。
  3. mapper接口的返回类型要和mapper.xml中定义的sql标签的返回类型一致。
  4. Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同;
  5. mapper.xml的namespace必须要和mapper接口的全类路径名保持一致。

14. Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

第一种是使用resultMap标签,逐一定义数据库列名和对象属性名之间的映射关系。

第二种是使用sql列的别名功能,将列的别名书写为对象属性名。

有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

15. 在mapper中传递多个参数

第一种:MyBatis会自动将这些参数放在一个map集合中,以arg0,arg1…为键,以参数为值
param1,param2…为键,以参数为值;因此只需要通过${}#{}访问map集合的键就可以获取相对应的
值,注意​${}需要手动加单引号。

第二种:使用 @param 注解

此时,会将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;以
**param1,param2…为键,以参数为值;**只需要通过${}#{}访问map集合的键就可以获取相对应的值,
注意${}需要手动加单引号

public interface UserMapper {  
    User selectUser(@param("username") String username,@param(“password”) String password);  
}
<!-- 然后,就可以在xml像下面这样使用(推荐封装为一个map,作为单个参数传递给mapper):  -->
<select id="selectUser" resultType="user">  
    select id, username, password  
    from some_table  
    where username = #{username} and password = #{password}   
</select>  

第三种:实体类型的参数

若mapper接口中的方法参数为实体类对象时
此时可以使用${}#{},通过访问实体类对象中的属性名获取属性值,注意​${}需要手动加单引号

16. Mybatis动态sql

Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断并动态拼接sql的功能。

Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。

where和if一般结合使用:
a>若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字
b>若where标签中的if条件满足,则where标签会自动添加where关键字,并将条件最前方多余的
and去掉
注意:where标签不能去掉条件最后多余的and

trim用于去掉或添加标签中的内容
常用属性:
prefix:在trim标签中的内容的前面添加某些内容
prefixOverrides:在trim标签中的内容的前面去掉某些内容
suffix:在trim标签中的内容的后面添加某些内容
suffixOverrides:在trim标签中的内容的后面去掉某些内容

foreach属性:
collection:设置要循环的数组或集合
item:表示集合或数组中的每一个数据
separator:设置循环体之间的分隔符
open:设置foreach标签中的内容的开始符
close:设置foreach标签中的内容的结束符

17. Mybatis不同的Xml映射文件id是否可以重复?

不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复;

原因就是namespace+id是作为Map<String, MapperStatement>的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。

18. MyBatis实现一对一的方式

有联合查询和嵌套查询。

联合查询是几个表联合查询,只查询一次,通过在resultMap里面配置association节点配置一对一的类就可以完成;

嵌套查询是先查一个表,根据这个表里面的结果的 外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。

19. Mybatis的延迟加载

**延迟加载又叫懒加载,也叫按需加载,也就是说先加载主信息,需要的时候,再去加载从信息。**代码中有查询语句,当执行到查询语句时,并不是马上去DB中查询,而是根据设置的延迟策略将查询向后推迟。

Mybatis仅支持association关联对象和collection关联集合对象的延迟加载association指的就是一对一collection指的就是一对多查询

在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=“lazy(延迟加载)|eager(立即加载)”

它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。

优点:先从单表查询,需要时再从关联表去关联查询大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

缺点:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成⽤户等待时间变长,造成用户体验下降

在多表中:
一对多,多对多:通常情况下采用延迟加载
一对一(多对一):通常情况下采用立即加载
注意:
延迟加载是基于嵌套查询来实现的

20. 为什么mybatis的mapper没有实现类?底层是咋实现的?

当我们在使用 MyBatis 的时候,通常会先定义一个包含 SQL 语句的 XML 文件(也可以使用注解方式实现),这个文件中包含了 select、update、delete、insert 等与数据库操作相关的语句。

MyBatis 通过读取这个 XML 文件,将其中定义的 SQL 语句解析成对应的 MappedStatement 对象,并存储在 Configuration 对象中。

在使用 MyBatis 进行数据库操作时,会先通过 SqlSession 对象获取到对应的 Mapper 接口,然后通过该接口上的方法调用执行相应的 SQL 语句。但实际上这些方法定义时是一个个接口方法,并没有实现,因此在执行这些方法时,需要使用动态代理(JDK动态代理)来实现这些接口方法的执行

MyBatis 通过 Java 的 Proxy 类动态生成 Mapper 接口的代理对象,并将该代理对象返回给客户端使用。Mapper 接口的代理对象会拦截所有对接口方法的调用,并把这些调用转发给 MyBatis 的 SqlSessionSqlSession 会负责执行与调用相关的一些 SQL 语句,并返回结果给代理对象,最终返回给客户端。因此,从客户端来看,就好像是直接调用了接口方法一样。

具体来说,当 MyBatis 调用 Mapper 接口方法时,会将对应的 MapperMethod 对象封装成对应的 Invocation 对象传递给代理对象的 invoke 方法,从而通过代理对象回调 MapperProxy 的 invoke 方法。MapperProxy 的 invoke 方法会获取 SqlSession 并根据调用 Mapper 接口方法的相关信息获取对应的 MappedStatement,然后再通过 Executor 和 StatementHandler 将 MappedStatement 中封装的 SQL 语句交给 JDBC 执行,最后将 JDBC 执行结果转化为 Mapper 接口方法执行结果并返回。

代理类 MapperProxy 实现了 InvocationHandler 接口,该接口有一个 invoke() 方法,代理对象在调用某个方法时,都会通过 invoke() 方法回调到具体的处理类中进行处理,从而实现动态代理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值