mybatise全接触-面试宝典-知识大全

1 . 简述什么是Mybatis和原理 ?

Mybatis工作原理:

(1)Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,加载驱动、创建连接、创建statement等繁杂的过程,开发者开发时只需要关注如何编写SQL语句,可以严格控制sql执行性能,灵活度高。

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

称Mybatis是半自动ORM映射工具,是因为在查询关联对象或关联集合对象时,需要手动编写sql来完成。不像Hibernate这种全自动ORM映射工具,Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取。

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

(4)由于MyBatis专注于SQL本身,灵活度高,所以比较适合对性能的要求很高,或者需求变化较多的项目,如互联网项目。

2 . 请简述Mybaits的优缺点 ?

【Mybaits优点】

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

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

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

④ 能够与Spring很好的集成;

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

【Mybaits缺点】

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

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

3 . Mybatis是如何将SQL执行结果封装为目标对象并返回的?

方式一、

select * from user where id = #{id}

方式二

方式三

@Select("select * from user")

List selectAllUsers();

4 . Mybatis动态SQL作用 ?如何封装 ?有哪些动态SQL?

Mybatis动态SQL可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断 并动态拼接sql的功能。Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。

映射方式

1、当列名和封装查询结果的类的属性名一一对应时:

这时MyBatis 有自动映射功能,将查询的记录封装到resultType 指定的类的对象中去

2、当列名和封装查询结果的类的属性名不对应时:

使用resultMap 标签,在标签中配置属性名和列名的映射关系

5 . MyBatis #{}和${}的区别是什么?

KaTeX parse error: Expected 'EOF', got '#' at position 10: {}是字符串替换,#̲{}是预处理;使用#{}可以有...{}时,就是把${}直接替换成变量的值。而Mybatis在处理#{}时,会对sql语句进行预处理,将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值

6 . Mybatis是如何进行分页的?

Mybatis 内部使用 RowBounds 对象进行分页,需要注意的是,它是针对 ResultSet 结果集执行的内存分页,而非数据库分页。

所以,生产环境中,不适合直接使用 MyBatis 原有的 RowBounds 对象进行分页。而是使用如下两种方案:

在 SQL 内手动书写数据库分页的参数来完成分页功能,样例代码如下:

select * from t_user limit #{start}, #{pageSize}

也可使用开源的分页插件来完成数据库分页, 如:

Mybatis-PageHelper

MyBatis-Plus ():

7 . 通常一个mapper.xml文件,都会对应一个Dao接口,这个Dao接口的工作原理是什么?

Mapper 接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象 MappedProxy,代理对象会拦截接口方法,根据类的全限定名+方法名,唯一定位到一个MapperStatement并调用执行器执行所代表的sql,然后将sql执行结果返回。

Mapper接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。

(1)Dao接口,就是Mapper接口。

(2)接口的全限名,就是映射文件中的namespace的值;

(3)接口的方法名,就是映射文件中Mapper的Statement的id值;

(4)接口方法内的参数,就是传递给sql的参数。

当调用接口方法时,通过 "接口全限名+方法名"拼接字符串作为key值,可唯一定位一个MapperStatement,因为在Mybatis中,每一个SQL标签,都会被解析为一个MapperStatement对象。

举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace为com.mybatis3.mappers.StudentDao下面 id 为 findStudentById 的 MapperStatement。

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

不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复;原因就是namespace+id是作为Map的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。

备注:在旧版本的Mybatis中,namespace是可选的,不过新版本的namespace已经是必填项

9 . 简述MyBatis 编程一般步骤( 重要 )?

第一步,创建 SqlSessionFactory 对象。

通过 SqlSessionFactory 获取 SqlSession 对象。

通过 SqlSession 获得 Mapper 代理对象。

通过 Mapper 代理对象,执行数据库操作。

执行成功,则使用 SqlSession 提交事务。

执行失败,则使用 SqlSession 回滚事务。

最终,关闭 session 会话。

10 . 简述MyBatis XML 映射文件中,都有哪些常见的标签?

以下列举 Mybatis 中常见的标签:

  • 映射查询语句。
  • 映射插入语句。
  • 映射更新语句。
  • 映射删除语句。
  • 对给定命名空间的缓存配置。
  • 对其他命名空间缓存配置的引用。。
  • 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
    已被废弃!老式风格的参数映射。更好的办法是使用内联参数,此元素可能在将来被移除。文档中不会介绍此元素。
    可被其他语句引用的可重用语句块。
    标签,引用 标签的语句。
    标签,不支持自增的主键生成策略标签。
    PS: 关于 Mybatis 标签,可参见《MyBatis 官方文档 ------ Mapper XML 文件》
    以下关于动态 SQL 相关的标签:

、、

、、

:集合遍历

:bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文。

11 . MyBatis 是如何将 sql 执行结果转换为目标对象并返回的?有哪些映射形式?

方式一、

select * from user where id = #{id}

方式二

方式三

@Select("select * from user")

List selectAllUsers();

12 . 简述使用MyBatis的mapper接口调用时有哪些要求?

1)Mapper接口方法名和mapper.xml中定义的每个sql的id相同

2)Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同

3)Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

4)Mapper.xml文件中的namespace即是mapper接口的类路径。

13 . MyBatis如何在Mapper中如何传递多个参数?

(1)第一种:

//DAO层的函数

Public UserselectUser(String name,String area);

//对应的xml,#{0}代表接收的是dao层中的第一个参数,#{1}代表dao层中第二参数,更多参数一致往后加即可。

(2)第二种: 使用 @param 注解:

public interface usermapper {

user selectuser(@param("username") string username,@param("hashedpassword") string hashedpassword);

}

然后,就可以在xml像下面这样使用(推荐封装为一个map,作为单个参数传递给mapper):

(3)第三种:多个参数封装成map

try{

//映射文件的命名空间.SQL片段的ID,就可以调用对应的映射文件中的SQL

//由于我们的参数超过了两个,而方法中只有一个Object参数收集,因此我们使用Map集合来装载我们的参数

Map map = new HashMap();

map.put("start", start);

map.put("end", end);

return sqlSession.selectList("StudentID.pagination", map);

}catch(Exception e){

e.printStackTrace();

sqlSession.rollback();

throw e; }

finally{

MybatisUtil.closeSqlSession();

}

14 . MyBatis实现一对一有几种方式?具体怎么操作的?

有联合查询和嵌套查询:

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

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

15 . MyBatis实现一对多有几种方式,怎么操作的?

有联合查询和嵌套查询。联合查询是几个表联合查询,只查询一次,通过在resultMap里面的collection节点配置一对多的类就可以完成;嵌套查询是先查一个表,根据这个表里面的 结果的外键id,去再另外一个表里面查询数据,也是通过配置collection,但另外一个表的查询通过select节点配置

16 . 简述Mapper编写有哪几种方式?

第一种:接口实现类继承SqlSessionDaoSupport:使用此种方法需要编写mapper接口,mapper接口实现类、mapper.xml文件。

(1)在sqlMapConfig.xml中配置mapper.xml的位置:

(2)定义mapper接口:

(3)实现类集成SqlSessionDaoSupport:mapper方法中可以this.getSqlSession()进行数据增删改查。

(4)spring 配置:

第二种:使用org.mybatis.spring.mapper.MapperFactoryBean:

(1)在sqlMapConfig.xml中配置mapper.xml的位置,如果mapper.xml和mappre接口的名称相同且在同一个目录,这里可以不用配置

(2)定义mapper接口:

① mapper.xml中的namespace为mapper接口的地址

② mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致

③ Spring中定义:

第三种:使用mapper扫描器:

(1)mapper.xml文件编写:

mapper.xml中的namespace为mapper接口的地址;

mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致;

如果将mapper.xml和mapper接口的名称保持一致则不用在sqlMapConfig.xml中进行配置。

(2)定义mapper接口:

注意mapper.xml的文件名和mapper的接口名称保持一致,且放在同一个目录

(3)配置mapper扫描器:

(4)使用扫描器后从spring容器中获取mapper的实现对象。

17 . MyBatis 模糊查询like语句该怎么写?

第1种:在Java代码中添加sql通配符。

string wildcardname = "%smi%";

list names = mapper.selectlike(wildcardname);

第2种:在sql语句中拼接通配符,会引起sql注入

string wildcardname = "smi";

list names = mapper.selectlike(wildcardname);

18 . 简述MyBatis的一级、二级缓存?

(1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。

(2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置 ;

(3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear 掉并重新更新,如果开启了二级缓存,则只根据配置判断是否刷新

19 . MyBatis是否支持延迟加载?如果支持,它的实现原理是什么?

Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。

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

当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的

20 . 简述 Mybatis 的 Xml 映射文件和 Mybatis 内部数据结构之间的映射关系?

Mybatis 将所有 Xml 配置信息都封装到 All-In-One 重量级对象 Configuration 内部。在

Xml 映射文件中,标签会被解析为 ParameterMap 对象,其每个子元素会

被解析为 ParameterMapping 对象。标签会被解析为 ResultMap 对象,其每个子

元素会被解析为 ResultMapping 对象。每一个

21 . MyBatis动态SQL语句(OGNL语法) ?

1、if

resultType="Blog">

SELECT * FROM BLOG

WHERE state = 'ACTIVE'

AND title like #{title}

AND name like #{title}

2、where

像上面的那种情况,如果where后面没有条件,然后需要直接写if判断(开头如果是 and / or 的话,会去除掉)

resultType="Blog">

SELECT * FROM BLOG

AND title like #{title}

AND name like #{title}

3、choose(when、otherwise)

choose 相当于 java 里面的 switch 语句。otherwise(其他情况)

resultType="Blog">

SELECT * FROM BLOG WHERE state = 'ACTIVE'

AND title like #{title}

AND author_name like #{author.name}

AND featured = 1

4、trim

prefix:前缀prefixoverride:去掉第一个and或者是or

select * from test

AND a=#{a}

AND a=#{a}

5、set

set 元素主要是用在更新操作的时候,如果包含的语句是以逗号结束的话将会把该逗号忽略,如果set包含的内容为空的话则会出错。

update t_blog

title = #{title},

content = #{content},

owner = #{owner}

where id = #{id}

6、foreach

foreach主要用在构建in条件中

open separator close

相当于是in (?,?,?)

如果是个map怎么办

collection对应map的键,像这样

List ids = new ArrayList();

ids.add(1);

ids.add(2);

ids.add(3);

ids.add(6);

ids.add(7);

ids.add(9);

Map params = new HashMap();

params.put("ids", ids);

22 . 解释MyBatis是否可以映射到枚举类 ?

Mybatis 可以映射枚举类。

不单可以映射枚举类,Mybatis 可以映射任何对象到表的一列上。映射方式为自定义一个 TypeHandler ,实现 TypeHandler 的 ​setParameter()​ 和 ​getResult()​ 接口方法。

TypeHandler 有两个作用,一是完成从 javaType 至 jdbcType 的转换,二是完成 jdbcType 至 javaType 的转换,体现为 ​setParameter() ​和 ​getResult()​ 两个方法,分别代表设置 sql 问号占位符参数和获取列查询结果。

23 . 简述MyBatis如何使用时间timestamp做条件进行查询 ?

首先要将条件 转换为 时间戳

long startTime = TimeUtil.parseTimestamp(start);

long endTime = TimeUtil.parseTimestamp(end);

/对应工具类 /

public static long parseTimestamp(String datetime){

try{

SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

Date date = dateformat.parse(datetime);

return date.getTime()/1000;

}catch(Exception e){

e.printStackTrace();

}

return 0;

}

然后Mapper.xml中 使用BETWEEN and 和 to_timestamp

AND tdnm.create_time BETWEEN to_timestamp(#{startDate}) AND to_timestamp(#{endDate})

24 . 简述什么情况下用注解绑定,什么情况下用 xml 绑定?

当 Sql 语句比较简单时候,用注解绑定;当 SQL 语句比较复杂时候,用 xml 绑定,一般用xml 绑定的比较多

25 . Mybatis都有哪些Executor执行器?它们之间的区别是什么?

Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。

SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。

ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。

BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。

作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内

26 . MyBatis 如何获取自动生成的主键id?

1、XML配置文件

insert into person(name,pswd) values(#{name},#{pswd})

2、Mapper中的方法

int insert(Person person);

注意在调用这个方法时,返回的int值并不是主键,而是插入的记录数。主键id会被赋值到输入的person对象里,自动赋值给person对象的id属性。比如:

Person person = new Person("name","psw");

//num是插入的记录数

int num = PersonMapper.insert(person);

//person对象的id属性会变成自生成的id

int id = person.getId();

27 . 简述MyBatis中ResultType 和 ResultMap 的区别 ?

ResultMap和ResultType都是用于设置mybatis增删改查后返回的数据类型。那么什么时候用ResultMap,什么时候用ResultType呢?

如果你搜索只是返回一个值,比如说String ,或者是int,那你直接用resultType就行了。

例如:

//dao中的接口

int addArticleThumbs(String id);

update

art_thumbs = art_thumbs+1

where

id = #{id}

该SQL返回的是int型,那么ResultType定义成int型即可直接与Java进行绑定(基本数据类型默认可不写)。

  1. 但是你如果是返回一个复杂的对象,就可以使用ResultMap(当然ResultType也是可以的)。
    例如:
    创建User 对象, 拥有两个字段id,userName。
    //dao中的接口
    User queryUser(String id);

从代码中可以清晰的看出,resultMap可以对返回的参数进行配置,mybatis默认会进行驼峰转换,当数据库字段和Java对象字段不满足驼峰(列名不匹配),可以通过这里来配置,进行字段匹配

28 . 解释MyBatis中Mapper接口中的方法支持重载么?

在MyBatis中,Mapper接口中的方法不支持重载。

在MyBatis源码中有这么几行代码,我们可以看到,在解析XML配置文件创建Mapper接口对应方法的时候,采用了【 接口全类名+方法名】的方式作为 StrictMap(MappedStatement数据存放的Map集合)的key值,而源码中对于StrictMap的put方法进行了判断,如果存入的数据key已重复,则会抛出异常,所以,Mapper接口中的方法不支持重载

29 . MyBatis中动态SQL的执行原理是什么?

在MyBatis中,动态SQL的执行原理大致可以分为三个阶段:初始化阶段、代理阶段、数据读写阶段,每个阶段都在做不同的事情。

初始化阶段:通过 XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder 来解析XML配置文件中的信息 存储到Configuration类中的。

代理阶段:首先从Configuration配置类MapperRegistry对象中获取Mapper接口和对应的代理对象工厂信息,然后再利用代理对象工厂MapperProxyFactory创建实际代理类,最后在MapperProxy类中通过MapperMethod类对象内保存的对应方法的信息,以及对应的SQL语句的信息进行分析,最终确定对应的增强方法的调用。

数据读写阶段:通过四种Executor调用四种Handler进行数据查询和数据封装返回

30 . MyBatis如何在配置多数据源 ?

先将spring boot自带的DataSourceAutoConfiguration禁掉,因为它会读取application.properties文件的spring.datasource.*属性并自动配置单数据源。在@SpringBootApplication注解中添加exclude属性即可

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

public class XxlJobExecutorApplication {

public static void main(String[] args) {

SpringApplication.run(XxlJobExecutorApplication.class, args);

}

}

连接配置。

properties

########################## 主数据源 ##################################

spring.datasource.primary.jdbc-url=jdbc:mysql://127.0.0.1:3306/oppo?characterEncoding=utf-8&serverTimezone=GMT%2B8

spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.primary.username=root

spring.datasource.primary.password=123456

########################## 第二个数据源 ###############################

spring.datasource.datasource2.jdbc-url=jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf-8&serverTimezone=GMT%2B8

spring.datasource.datasource2.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.datasource2.username=root

spring.datasource.datasource2.password=123456

#####sql打印#####

mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

logging.level.com.xxl.job.executor.mapper=debug

注意,配置中的数据源连接 url 末尾使用的是 jdbc-url.

因为使用了 Mybatis 框架,所以 Mybatis 框架的配置信息也是少不了的,指定扫描目录 mapper 下的mapper xml 配置文件。

多数据源配置

上面你应该看到了,到目前为止和 Mybatis 单数据源写法唯一的区别就是 Mapper 接口使用不同的目录分开了,那么这个不同点一定会在数据源配置中体现。

主数据源

开始配置两个数据源信息,先配置主数据源,配置扫描的 MapperScan 目录为 com.wdbyte.mapper.primary

java

@Configuration

@MapperScan(basePackages = {"com.xxl.job.executor.mapper.primary"}, sqlSessionFactoryRef = "sqlSessionFactory")

public class PrimaryDataSourceConfig {

@Bean(name = "dataSource")

@ConfigurationProperties(prefix = "spring.datasource.primary")

@Primary

public DataSource dataSource() {

return DataSourceBuilder.create().build();

}

@Bean(name = "sqlSessionFactory")

@Primary

public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {

SqlSessionFactoryBean bean = new SqlSessionFactoryBean();

bean.setDataSource(dataSource);

bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:com/xxl/job/executor/mapper/primary/*.xml"));

return bean.getObject();

}

@Bean(name = "transactionManager")

@Primary

public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource) {

return new DataSourceTransactionManager(dataSource);

}

@Bean(name = "sqlSessionTemplate")

@Primary

public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {

return new SqlSessionTemplate(sqlSessionFactory);

}

}

和单数据源不同的是这里把

dataSource

sqlSessionFactory

transactionManager

sqlSessionTemplate

都单独进行了配置,简单的 bean 创建,下面是用到的一些注解说明。

@ConfigurationProperties(prefix = "spring.datasource.primary"):使用spring.datasource.primary 开头的配置。

@Primary :声明这是一个主数据源(默认数据源),多数据源配置时必不可少。

@Qualifier:显式选择传入的 Bean。

第二个数据源

第二个数据源和主数据源唯一不同的只是 MapperScan 扫描路径和创建的 Bean 名称,同时没有 @Primary 主数据源的注解。

java

/**

  • 第二个数据源配置
    */
    @Configuration
    @MapperScan(basePackages = {"com.xxl.job.executor.mapper.second"}, sqlSessionFactoryRef = "sqlSessionFactory2")
    public class SecondDataSourceConfig {

@Bean(name = "dataSource2")

@ConfigurationProperties(prefix = "spring.datasource.datasource2")

public DataSource dataSource() {

return DataSourceBuilder.create().build();

}

@Bean(name = "sqlSessionFactory2")

public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource2") DataSource dataSource) throws Exception {

SqlSessionFactoryBean bean = new SqlSessionFactoryBean();

bean.setDataSource(dataSource);

bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:com/xxl/job/executor/mapper/second/*.xml"));

return bean.getObject();

}

@Bean(name = "transactionManager2")

public DataSourceTransactionManager transactionManager(@Qualifier("dataSource2") DataSource dataSource) {

return new DataSourceTransactionManager(dataSource);

}

@Bean(name = "sqlSessionTemplate2")

public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory2") SqlSessionFactory sqlSessionFactory) {

return new SqlSessionTemplate(sqlSessionFactory);

}

}

注意:因为已经在两个数据源中分别配置了扫描的 Mapper 路径,如果你之前在 SpringBoot 启动类中也使用了 Mapper 扫描注解,需要删掉。

31 . 简述Mybatis 和 Hibernate 持久层框架之间的区别 ?

一、Mybatis

MyBatis 支持通过 XML 或注解的方式来配置需要运行的 SQL 语句,并且,最终由框架本身将 Java 对象和 SQL 语句映射生成最终执行的 SQL ,执行后,再将结果映射成 Java 对象返回。

相较于 Hibernate, Mybatis 因为可以编写原生的 SQL ,也就是说,能够严格控制 SQL 执行性能,灵活度高。但是灵活的前提是 MyBatis 无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套 SQL 映射文件,工作量大。

PS: 现在一般借助于一些自动化的插件,来帮我们自动生成相关代码,极大地提升了开发效率。还可以使用 MyBatis-Plus 框架,内部集成了常用的 SQL 操作,也是非常不错的。

二、Hibernate

再来说说 Hibernate, 它对象/关系映射能力强,能做到数据库无关性。如果用 Hibernate 开发,无需关系 SQL 编写(不会写 SQL 的人都可以操作数据库),能节省很多代码,提高效率。但是 Hibernate 的缺点是学习门槛高,要精通门槛更高,而且怎么设计 O/R 映射,在性能和对象模型之间如何权衡,以及怎样用好 Hibernate 需要具有很强的经验和能力才行。

插一句: JPA 是基于 Hibernate 的一层封装,也就是说底层还是 Hibernate.

三、如何选型

结合公司业务,选取最适合的框架,不要为了技术而技术,否则都是耍流氓。

比如说,你所在的是相对来说较小的公司,数据量并不大,且公司开发人员的技术栈偏 Hibernate 多一些,推荐使用 JPA、Hibernate 这些无需手动编写 SQL 的持久层框架,提高开发效率、版本迭代速度。

如果说,你所在的是一家互联网公司,用户数较大,对相关 SQL 执行性能要求较为严格,则推荐使用 Mybatis。

总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。

四、总结

Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取。 Mybatis 属于半自动 ORM 映射工具,在查询关联对象或关联集合对象时,需要手动编写 SQL 来完成。

32 . MyBatis 如何获取自增主键的?

MySQL 有两种方式获取自增主键,代码如下:

// 方式一: 使用 useGeneratedKeys + keyProperty 属性,这种在项目中比较常用

INSERT INTO user(name, password)

VALUE (#{name}, #{password})

// 方式二: 使用 `` 标签

SELECT LAST_INSERT_ID()

INSERT INTO user(name, password)

VALUE (#{name}, #{password})

Oracle 也有两种方式,分别是:

序列 (推荐使用)

触发器 (不太推荐使用)。

这里仅说下推荐使用的序列方式,根据 执行的时机, 分以下两种,示例代码如下:

// 这个是创建表的自增序列

CREATE SEQUENCE student_sequence

INCREMENT BY 1

NOMAXVALUE

NOCYCLE

CACHE 10;

// 方式一,使用 `` 标签 + BEFORE

select student_sequence.nextval FROM dual

INSERT INTO student(student_id, student_name, student_age)

VALUES (#{student_id},#{student_name},#{student_age})

// 方式二,使用 `` 标签 + AFTER

SELECT SEQ_ZONE.CURRVAL AS id FROM dual

INSERT INTO TBL_ZONE (ID, NAME )

VALUES (SEQ_ZONE.NEXTVAL, #{name,jdbcType=VARCHAR})

33 . 简述Mybatis的插件运行原理,以及如何编写一个插件?

Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。

编写插件:实现Mybatis的Interceptor接口并复写intercept()方法,然后再给插件编写注解,指定要拦截哪一个接口的哪些方法即可,最后在配置文件中配置你编写的插件

34 . MyBatis当实体类中的属性名和表中的字段名不一样 ,怎么办 ?

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

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

35 . 请用MyBatis代码显示一对一、一对多的关联查询 ?

36 . 简述什么是MyBatis的接口绑定?有哪些实现方式?

接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。

接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;另外一种就是通过xml里面写SQL来绑定, 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多

37 . 详细简述什么是 MyBatis 的接口绑定 ?

Mybatis的接口绑定,就是把接口里面的方法和SQL语句绑定,以及 SQL 执行的结果与方法的返回值进行转换匹配。

接口绑定有两种实现方式 :

  1. 通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;
  2. 通过xml里面写SQL来绑定, 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名

38 . MyBatis映射文件中A标签引用B标签,如果B标签在A的后面定义,可以吗?

虽然 Mybatis 解析 Xml 映射文件是按照顺序解析的,但是,被引用的 B 标签依然可以定义在任何地方,Mybatis 都可以正确识别。

原理:

Mybatis 解析 A 标签时,发现引用了 B 标签,未解析到 B 标签,此时会把 A 标签标记为未解析状态;

继续解析下面内容,把剩下解析完之后,再解析标记为未解析的标签;

此时已解析到 B 标签,此时再解析A标签时,B标签已经存在,A 标签也就顺利解析完成。

39 . MyBatis不同映射文件中的id是否可以重复?

可以重复,但是需要映射文件的namespace不同

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

原因就是 namespace+id 是作为 Map的 key使用的,如果没有 namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。

有了 namespace,自然 id 就可以重复,namespace 不同,namespace+id 自然也就不同

40 . 简述MyBatis如何使用日志记录分析 ?

Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:

SLF4J

Apache Commons Logging

Log4j 2

Log4j

JDK logging

具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。 如果一个都未找到,日志功能就会被禁用。

不少应用服务器的classpath中已经包含Commons Logging,如Tomcat和WebShpere, 所以MyBatis会把它作为具体的日志实现。记住这点非常重要。这将意味着,在诸如 WebSphere的环境中------WebSphere提供了Commons Logging的私有实现,你的Log4J配置将被忽略。 这种做法不免让人悲催,MyBatis怎么能忽略你的配置呢?事实上,因Commons Logging已经存 在了,按照优先级顺序,Log4J自然就被忽略了!不过,如果你的应用部署在一个包含Commons Logging的环境, 而你又想用其他的日志框架,你可以根据需要调用如下的某一方法:

org.apache.ibatis.logging.LogFactory.useSlf4jLogging();

org.apache.ibatis.logging.LogFactory.useLog4JLogging();

org.apache.ibatis.logging.LogFactory.useJdkLogging();

org.apache.ibatis.logging.LogFactory.useCommonsLogging();

org.apache.ibatis.logging.LogFactory.useStdOutLogging();

如果的确需要调用以上的某个方法,请在其他所有MyBatis方法之前调用它。另外,只有在相应日志实现中存在 的前提下,调用对应的方法才是有意义的,否则MyBatis一概忽略。如你环境中并不存在Log4J,你却调用了 相应的方法,MyBatis就会忽略这一调用,代之默认的查找顺序查找日志实现。

41 . 简述Mybatis中如何指定使用哪一种Executor执行器?

1.在Mybatis配置文件中,在设置(settings)可以指定默认的ExecutorType执行器类型,也可以手动DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数,如SqlSession openSession(ExecutorType execType)。

2.配置默认的执行器。SIMPLE就是普通的执行器;REUSE执行器会重用预处理语句(preparedstatements); BATCH 执行器将重用语句并执行批量更新。

42 . 简述什么是MyBatis 流式查询 ?

流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果。流式查询的好处是能够降低内存使用。

如果没有流式查询,我们想要从数据库取 1000 万条记录而又没有足够的内存时,就不得不分页查询,而分页查询效率取决于表设计,如果设计的不好,就无法执行高效的分页查询。因此流式查询是一个数据库访问框架必须具备的功能。

流式查询的过程当中,数据库连接是保持打开状态的,因此要注意的是:执行一个流式查询后,数据库访问框架就不负责关闭数据库连接了,需要应用在取完数据后自己关闭

43 . MyBatis 如何防止 SQL 注入?

MyBatis框架作为一款半自动化的持久层框架,其SQL语句都要我们自己手动编写,这个时候当然需要防止SQL注入。其实,MyBatis的SQL是一个具有"输入+输出"的功能,类似于函数的结构,参考上面的两个例子。其中,parameterType表示了输入的参数类型,resultType表示了输出的参数类型。回应上文,如果我们想防止SQL注入,理所当然地要在输入参数上下功夫。上面代码中使用#的即输入参数在SQL中拼接的部分,传入参数后,打印出执行的SQL语句,会看到SQL是这样的:

select id, username, password, role from user where username=? and password=?

不管输入什么参数,打印出的SQL都是这样的。这是因为MyBatis启用了预编译功能,在SQL执行前,会先将上面的SQL发送给数据库进行编译;执行时,直接使用编译好的SQL,替换占位符"?"就可以了。因为SQL注入只能对编译过程起作用,所以这样的方式就很好地避免了SQL注入的问题。

【底层实现原理】MyBatis是如何做到SQL预编译的呢?其实在框架底层,是JDBC中的PreparedStatement类在起作用,PreparedStatement是我们很熟悉的Statement的子类,它的对象包含了编译好的SQL语句。这种"准备好"的方式不仅能提高安全性,而且在多次执行同一个SQL时,能够提高效率。原因是SQL已编译好,再次执行时无需再编译。

//安全的,预编译了的

Connection conn = getConn();//获得连接

String sql = "select id, username, password, role from user where id=?"; //执行sql前会预编译号该条语句

PreparedStatement pstmt = conn.prepareStatement(sql);

pstmt.setString(1, id);

ResultSet rs=pstmt.executeUpdate();

...

//不安全的,没进行预编译

private String getNameByUserId(String userId) {

Connection conn = getConn();//获得连接

String sql = "select id,username,password,role from user where id=" + id;

//当id参数为"3;drop table user;"时,执行的sql语句如下:

//select id,username,password,role from user where id=3; drop table user;

PreparedStatement pstmt = conn.prepareStatement(sql);

ResultSet rs=pstmt.executeUpdate();

...

}

【 结论:】

#{}:相当于JDBC中的PreparedStatement

${}:是输出变量的值

简单说,#{}是经过预编译的,是安全的;${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入。

如果我们order by语句后用了${},那么不做任何处理的时候是存在SQL注入危险的。你说怎么防止,那我只能悲惨的告诉你,你得手动处理过滤一下输入的内容。如判断一下输入的参数的长度是否正常(注入语句一般很长),更精确的过滤则可以查询一下输入的参数是否在预期的参数集合中。

44 . 请列举说明Mapper.xml中statement中属性含义 ?

id:sql语句唯一标识

parameterType:指定输入参数类型,mybatis通过ognl从输入对象中获取参数值拼 接在sql中。

resultType:指定输出结果类型,mybatis将sql查询结果的一行记录数据映射为resultType指定类型的对象。

resultMap:resultType可以指定pojo将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系 resultMap实质上还需要将查询结果映射到pojo对象中。

resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo 和list实现一对一查询和一对多查询。

#{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。一般能用#的就别用$.
表示拼接 s q l 串,通过 {}表示拼接sql串,通过 表示拼接sql串,通过{}可以将parameterType 传入的内容拼接在sql中且不进 行jdbc类型转换, 可以接收简单类型值或 p o j o 属性值,如果 p a r a m e t e r T y p e 传输单个简单类型值, {}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值, 可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,{}括号中只能是value。

45 . 简述 MyBatis-Plus 是什么框架?

Mybatis的增强框架,帮助开发人员更加简化开发。这些框架提供了大部分的简单SQL语句封装的API,提供了分页插件,简化了配置流程。不过只对单表有好的支持,多表关联查询支持差,一般需要手动编写多表的SQL,不过可以原生和增强一起使用

相关推荐
测试19983 小时前
2024软件测试面试热点问题
自动化测试·软件测试·python·测试工具·面试·职场和发展·压力测试
马剑威(威哥爱编程)4 小时前
MongoDB面试专题33道解析
数据库·mongodb·面试
独行soc6 小时前
#渗透测试#SRC漏洞挖掘#深入挖掘XSS漏洞02之测试流程
web安全·面试·渗透测试·xss·漏洞挖掘·1024程序员节
理想不理想v6 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
sszmvb12347 小时前
测试开发 | 电商业务性能测试: Jmeter 参数化功能实现注册登录的数据驱动
jmeter·面试·职场和发展
测试杂货铺7 小时前
外包干了2年,快要废了。。
自动化测试·软件测试·python·功能测试·测试工具·面试·职场和发展
王佑辉7 小时前
【redis】redis缓存和数据库保证一致性的方案
redis·面试
真忒修斯之船8 小时前
大模型分布式训练并行技术(三)流水线并行
面试·llm·aigc
ZL不懂前端8 小时前
Content Security Policy (CSP)
前端·javascript·面试
鹿屿二向箔8 小时前
基于SSM(Spring + Spring MVC + MyBatis)框架的汽车租赁共享平台系统
spring·mvc·mybatis