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进行绑定(基本数据类型默认可不写)。
- 但是你如果是返回一个复杂的对象,就可以使用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 执行的结果与方法的返回值进行转换匹配。
接口绑定有两种实现方式 :
- 通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;
- 通过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,不过可以原生和增强一起使用