2022面试题,建议收藏
- 1. 简述springMVC的流程
- 2.mybatis中ORM的一对一映射,一对多映射,哪里用到
- 3.mybatis中一级缓存、二级缓存都是什么,现在springBoot有没有用到mybatis缓存
- 4.mybatis中的动态SQL是什么
- 5.Spring中的过滤器怎么实现
- 6.Spring中的拦截器怎么实现
- 7.Spring请求参数封装、方式和响应参数封装、方式
- 8.MySQL数据库引擎、SQL优化、索引概念
- 9.MySQL数据库集群方式、mycat基本理念
- 10.redis五种数据类型,以及各种应用场景
- 11.redis持久化、双检查锁防穿透,布隆过滤器防击穿,随机失效时间防雪崩
- 12.缓存穿透 缓存击穿 缓存雪崩
- 13.redis失效策略
- 14.redis集群理念
- 15.SpringSession一致性存储在redis里面的是什么
- 16.mybatis 中 #{} 和 ${} 的区别及应用场景
- 17.Spring中的数据库事务
- 18.第三方服务调用失败了如何处理
- 19.SQL语句中case函数用法(类似于判断,主要用于结果集列值根据条件判断显示什么)
- 20.线程池的作用,四个线程池有哪些,分别什么时候用
- 21.数据库连接池的作用,有哪些,你们用哪些,大概配置
- 22.springBoot启动流程
- 24.数据库事物四大特性、MySQL的隔离级别
- 25.List、Set、Map集合分别都在什么场景下使用
1. 简述springMVC的流程
2.mybatis中ORM的一对一映射,一对多映射,哪里用到
ORM就是对象关系映射
数据库中的一张表,往往对应着面向对象中的一个实体类。
在没有ORM时,我们需要手动写一些数据库中的字段与实体类中的属性的映射关系,这个操作是重复且不好维护的。
ORM出现之后,我们就省去了手动写映射关系的代码,我们只需要注重实体类以及sql语句的代码即可。
如果是一对一:
如果是直接传到对应的类中,直接使用resultType
如果类中有其他的对象,则resultMap–> association
如果类中有其他的对象的集合,则resultMap–> collection
<resultMap id="orderDetailMap" type="com.fh.entity.Order">
<id property="id" column="ID"/>
<result property="name" column="NAME"/>
<result property="number" column="NUMBER"/>
<association property="account" javaType="com.fh.entity.Account"> //对象Account
<id property="id" column="UID"/>
<result property="userName" column="USERNAME"/>
</association>
<collection property="orderDetails" ofType="com.fh.entity.OrderDetail"> //集合
<id property="id" column="DID"/>
<result property="orderId" column="ORDER_ID"/>
<result property="itemNumber" column="ITEM_NUMBER"/>
<result property="name" column="DNAME"/>
</collection>
</resultMap>
3.mybatis中一级缓存、二级缓存都是什么,现在springBoot有没有用到mybatis缓存
一级缓存:什么是一级缓存
MyBatis会在一次会话的表示----一个SqlSession对象中创建一个本地缓存(local cache),对于每一次查询,都会尝试根据查询的条件去本地缓存中查找是否在缓存中,如果在缓存中,就直接从缓存中取出,然后返回给用户;(不同的会话是不同的SqlSession,拿不到数据,基本每次都要重新擦汗讯)否则,从数据库读取数据,将查询结果存入缓存并返回给用户.对于会话(Session)级别的数据缓存,我们称之为一级数据缓存,简称一级缓存.
MyBatis的一级缓存就是使用了简单的HashMap,每次开启会话时,mybatis会自动创建一个Session对象,会话结束时,会自动释放掉该对象.
第一次查询后,MyBatis会将查询结果存储到SqlSession对象的缓存中,当后来有完全相同的查询时,直接从缓存中将结果取出(由于每次都创建SqkSession,因此可以使用数据库连接池)
二级缓存:
二级缓存需要配置cacheEnabled=true才行
二级缓存节约了[与数据库交互的时间]连接数据库的时间,查询数据的时间,返回数据的时间
一个mapper.xml对应一个缓存,即一个命名空间对应一个缓存,会出现脏读,由于作用域的区别,在其他作用域修改数据库后,是没办法更新的(跨命名空间操作(连接查询)导致数据库已经更新,缓存未更新)
4.mybatis中的动态SQL是什么
if、choose、when、otherwise、trim、where、set以及foreach
我们往往要根据各种不同的场景拼接出不同的SQL语句
<if test="ids!=null"></if>
<foreach collection="cities" index="city" open="(" separator="," close=")" item="city">
#{city}
</foreach>
5.Spring中的过滤器怎么实现
@WebFilter("/*")
public class EncodingFilter implements Filter{}
基本上适用于对编码进行修改,对请求头请求体的参数进行修改
6.Spring中的拦截器怎么实现
implements HandlerInterceptor,需要实现这个接口
` 单个拦截器
有三个方法:
public boolean preHandle//到达控制器之前进行拦截
public void postHandle
// postHandler用于对拦截信息处理完毕后进行其他操作
需要perHandler方法的返回值为true时才能执行
preHandler通过后,就会执行
public void afterCompletion
//需要perHandler方法的返回值为true时才能执行
该方法主要用于清理资源等
它将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行后,才会运行
springMVC使用时需要在spring-mvc.xml文件中配置
springboot中需要通过注册类进行注册
7.Spring请求参数封装、方式和响应参数封装、方式
地址通过 @RequestMapping注解来传递地址
以及GetMapping PostMapping 等
如果是对象(user),那么传过来的参数的name必须个set方法中的一样才能封装
如果只是参数,那么String 参数名 必须和name一致或者加上@RequestParam(value = "name"String name)
可以通过定义一个工具类,来封装前端传递的数据,并返回数据
8.MySQL数据库引擎、SQL优化、索引概念
- InnoDB存储引擎 事务型数据库 可处理巨大数据量 默认引擎
- MyISAM存储引擎 不支持事务 较高的插入、查询速度 web常用
- MEMORY存储引擎 将表中的数据存储到内存中,未查询和引用其他表数据提供快速访问 安全性差,常用于临时存储数据
如果要提供提交、回滚、崩溃恢复能力的事物安全(ACID兼容)能力,并要求实现并发控制,InnoDB是一个好的选择
如果数据表主要用来插入和查询记录,则MyISAM引擎能提供较高的处理效率
如果只是临时存放数据,数据量不大,并且不需要较高的数据安全性,可以选择将数据保存在内存中的Memory引擎,MySQL中使用该引擎作为临时表,存放查询的中间结果
如果只有INSERT和SELECT操作,可以选择Archive,Archive支持高并发的插入操作,但是本身不是事务安全的。Archive非常适合存储归档数据,如记录日志信息可以使用Archive - SQL优化
查询SQL尽量不要使用select *,而是具体字段 只取需要的字段,节省资源
避免在where子句中使用or来连接条件 使用union
使用or可能会使索引失效,从而全表扫描
使用varchar代替char varchar变长字段按数据内容实际长度存储,存储空间小,可以节省存储空间
尽量使用数值替代字符串类型
查询若无必要避免返回大量数据 通常采用分页
使用explain分析你SQL执行计划 SQL提供了explain关键字,它可以分析你的SQL执行计划,看它是否最佳 Explain主要看SQL是否使用了索引
优化like语句 like很可能让你的索引失效 第一位最好不是"%"
索引不适合建在有大量重复数据的字段 索引也不是越多越好,一般五个以内
避免在where子句中使用!=或<>操作符 引擎将放弃使用索引而进行全表扫描
批量插入性能提升 默认新增SQL有事务控制,导致每条都需要事务开启和事务提交;而批量处理是一次事务开启和提交
避免同时修改或删除过多数据,因为会造成cpu利用率过高,会造成锁表操作
伪删除设计 商品状态(state):1-上架、2-下架、3-删除
group by先过滤在分组
select job,avg(salary) from employee
where job ='president' or job = 'managent'
group by job;
索引:索引虽然会提高查询效率,但是会降低更新表的效率。
索引的数据结构有hash表 二叉树 平衡二叉树 B树
B树的结构:
B+树的结构 Mylsam以及引擎使用的就是B+树存储索引
9.MySQL数据库集群方式、mycat基本理念
我们一般应用对数据库而言都是“读多写少”,也就说对数据库读取数据的压力比较大,有一个思路就是说采用数据库集群的方案: 其中一个是主库,负责写入数据,我们称之为:写库; 其它都是从库,负责读取数据,我们称之为: 读库;
说白了就是数据库镜像
建议参考:
https://blog.csdn.net/K_520_W/article/details/117154729
- MyCat就是一个近似等于MySql的数据库库服务,你可以使用连接MySql的方式连接MyCat。绝大多数情况,你也可以使用常用的ORM框架连接MyCat,但是,对于分片的表,还是建议使用标准SQL语句,这样能够达到最佳的性能
- MyCat是一个强大的数据库中间件,不仅仅可以用作读写分离、分库分表,还可以用于容灾备份,云平台建设等,让你的架构具备很强的适应性和灵活性。
10.redis五种数据类型,以及各种应用场景
可以参考:https://blog.csdn.net/zzu_seu/article/details/106323114
redis自身是一个Map类型的存储方式,其中所有的数据都是采用key:value的形式存储
string --> String 字符串(可以为整形、浮点型和字符串,统称为元素)
hash --> Hashmap 列表(实现队列,元素不唯一,先入先出原则) 适合存储对象
list --> LinkList 集合(各不相同的元素)
set --> HashSet 散列值(hash的key必须是唯一的)
sorted_set --> TreeSet 有序集合
- Hash类型
新的存储需求:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息
需要的内存结构:一个存储空间保存多少个键值对数据
hash类型:底层使用哈希表结构实现数据存储
hash类型下的value只能存储字符串 - List类型
数据存储需求:存储多个数据,并对数据进入存储空间的顺序进行区分
需要的存储数据:一个存储空间保存多个数据,且通过数据可以体现进入顺序
list类型:保存多个数据,底层使用双向链表存储结构实现
list类型数据操作注意事项
list 中保存的数据都是string类型的,数据总容量式是有限的,最多232-1个元素(4294967295)
list具有索引的概念,但是操作数据时候通常以队列的形式进行入队出队操作,或以栈的形式进入栈出栈的操作
获取全部数据操作结束索引设置为-1
list 可以对数据进行分页操作,通过第一页的信息来自list,第2页及更多的信息通过数据库的形式加载 - Set类型
新的存储需求:存储大量的数据,在查询方面提供更高的效率
需要的存储结构:能够保存大量的数据,高效的内部存储机制,便于查询
set类型:与hash存储结构完全相同,仅存储键,不存储值(nil),并且值式不允许重复的。也就是只有键没有值的hash
业务场景-随机操作数据 例如热点歌单推荐,热点新闻推荐,热点旅游线路,应用APP推荐,大V推荐
共同好友 - sorted_set类型
业务场景- 建立排序依据
业务场景-会员短期体验之过期失效
11.redis持久化、双检查锁防穿透,布隆过滤器防击穿,随机失效时间防雪崩
redis的持久化
- Redis对数据的操作都是基于内存的,当遇到了进程退出、服务器宕机等意外情况,如果没有持久化机制,那么Redis中的数据将会丢失无法恢复
- RDB:持久化是把当前Redis中全部数据生成快照保存在硬盘上。
优点:RDB文件是一个紧凑的二进制压缩文件,是Redis在某个时间点的全部数据快照。所以使用RDB恢复数据的速度远远比AOF的快,非常适合备份、全量复制、灾难恢复等场景。
缺点:每次进行bgsave操作都要执行fork操作创建子经常,属于重量级操作,频繁执行成本过高,所以无法做到实时持久化,或者秒级持久化。
会丢失最后一次快照的数据 - AOF:每执行一次改变数据的命令时,就会存储到一个AOF文件中
更改持久化方式需要在redis.conf配置文件中进行配置
缺点:aof文件会在操作过程中变得越来越大
双检查锁防穿透
类似这样(不清楚是否正确):
update b_loan_info
set
left_product_money = left_product_money - #{bidMoney},
version = version + 1
where id =#{loanId}
and (left_product_money - #{bidMoney}) >= 0
and version = #{version}
<!--通过加上字段version,来确定是否有其他进程修改了version,如果version没有改动,
说明正确,如果version发生改动则会停止运行-->
布隆过滤器:
从容器的角度来说:
如果布隆过滤器判断元素在集合中存在,不一定存在
如果布隆过滤器判断不存在,一定不存在
从元素的角度来说:
如果元素实际存在,布隆过滤器一定判断存在
如果元素实际不存在,布隆过滤器可能判断存在
布隆过滤器不是万能的,但是它能帮我们抵挡掉大部分不存在的数据已经很不错了,已经减轻数据库很多压力了
可以把所有的user表里的关键查询字段放于Redis的bloom过滤器内。
12.缓存穿透 缓存击穿 缓存雪崩
可看:https://blog.csdn.net/kongtiao5/article/details/82771694
-
缓存穿透
描述:
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
解决方案:
接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,
缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样
可以防止攻击用户反复用同一个id暴力攻击 -
缓存击穿
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
解决方案:
设置热点数据永远不过期。
加锁:例如
Double allBidMoney = (Double) redisTemplate.opsForValue().get(Constants.ALL_BID_MONEY);
if(!ObjectUtils.allNotNull(allBidMoney)){
synchronized (this){
allBidMoney = (Double) redisTemplate.opsForValue().get(Constants.ALL_BID_MONEY);
if(!ObjectUtils.allNotNull(allBidMoney)){
allBidMoney = bidInfoMapper.selectAllBidMoney();
redisTemplate.opsForValue().set(Constants.ALL_BID_MONEY,allBidMoney);
}
}
}
return allBidMoney ;
}
//解释一下,这里第一个进程对象在redis中获取数据后,若为空,则给这个对象加锁,让他去数
//据库中取出数据并放到redis中,其他进程排队等待该对象取出数据后查询能够有效防止缓存击穿
- 缓存雪崩
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是, 缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案:
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
设置热点数据永远不过期。
13.redis失效策略
Redis在设置缓存数据时指定了过期时间,到了过期时间数据就失效了,那Redis是怎么处理这些失效的数据的呢?这就用到了Redis的过期策略——"定期删除+惰性删除
- 定期删除 (redis.conf文件中规定的)
定期删除是指Redis默认每隔 100ms 就 随机抽取 一些设置了过期时间的key,检测这些key是否过期,如果过期了就将其删除
如果Redis里面有大量key都设置了过期时间,全部都去检测一遍的话CPU负载就会很高,会浪费大量的时间在检测上面,甚至直接导致redis挂掉。所有只会抽取一部分而不会全部检查
正因为定期删除只是随机抽取部分key来检测,这样的话就会出现大量已经过期的key并没有被删除,这就是为什么有时候大量的key明明已经过了失效时间,但是redis的内存还是被大量占用的原因 ,为了解决这个问题,Redis又引入了“惰性删除策略”
- 惰性删除
惰性删除不是去主动删除,而是在你要获取某个key 的时候,redis会先去检测一下这个key是否已经过期,如果没有过期则返回给你,如果已经过期了,那么redis会删除这个key,不会返回给你
"定期删除+惰性删除"就能保证过期的key最终一定会被删掉 ,但是只能保证最终一定会被删除,要是定期删除遗漏的大量过期key,我们在很长的一段时间内也没有再访问这些key,那么这些过期key不就一直会存在于内存中吗?不就会一直占着我们的内存吗?这样不还是会导致redis内存耗尽吗?由于存在这样的问题,所以redis又引入了“内存淘汰机制”来解决
Redis内存淘汰机制 同样redis.conf文件中规定的
- LRU算法:
标准LRU算法是这样的:它把数据存放在链表中按照“最近访问”的顺序排列,当某个key被访问时就将此key移动到链表的头部,保证了最近访问过的元素在链表的头部或前面。当链表满了之后,就将"最近最久未使用"的,即链表尾部的元素删除,再将新的元素添加至链表头部。
因为标准LRU算法需要消耗大量的内存,所以Redis采用了一种近似LRU的做法:给每个key增加一个大小为24bit的属性字段,代表最后一次被访问的时间戳。然后随机采样出5个key,淘汰掉最旧的key,直到Redis占用内存小于maxmemory为止。其中随机采样的数量可以通过Redis配置文件中的 maxmemory_samples 属性来调整,默认是5,采样数量越大越接近于标准LRU算法,但也会带来性能的消耗。
LUF算法:
14.redis集群理念
- 主从复制
可以通过部署多台 redis,并在配置文件中指定这几台 redis 之间的主从关系,主负责写入数据,同时把写入的数据实时同步到从机器,这种模式叫做主从复制 redis 默认 master 用于写,slave 用于读,向 slave 写数据会导致错误 - 容灾处理
当 Master 服务出现故障,需手动将 slave 中的一个提升为 master,剩下的 slave 挂至新的master 上(冷处理:机器挂掉了,再处理) - 高可用 Sentinel 哨兵
-
- 监控:Sentinel 不断的检查主服务和从服务器是否按照预期正常工作。
-
- 提醒:被监控的 Redis 出现问题时,Sentinel 会通知管理员或其他应用程序。
自动故障转移:监控的主 Redis 不能正常工作,Sentinel 会开始进行故障迁移操作。将一个从服务器升级新的主服务器。让其他从服务器挂到新的主服务器。同时向客户端提供新的主服务器地址。
- 提醒:被监控的 Redis 出现问题时,Sentinel 会通知管理员或其他应用程序。
15.SpringSession一致性存储在redis里面的是什么
session会把用户的登录信息、用户信息存储在 session 中,以保持登录状态
就是通过redis将session存储到redis中以此来实现session的共享问题.主要原因传统session只存储在本地服务器,并不能实现session的跨域使用,session它是不共享的,不同的项目中无法获取同一个session,因此需要使用外部的redis来帮助实现session的共享.
16.mybatis 中 #{} 和 ${} 的区别及应用场景
-
#{}
#传入的参数在SQL中显示为字符串(当成一个字符串),会对自动传入的数据加一个双引号
#可以防止SQL注入的风险(语句的拼接);但$无法防止Sql注入
#{}预编译 -
${}
$传入的参数在SqL中直接显示为传入的值
$方式一般用于传入数据库对象,例如传入表名
${}必用场景动态表名、列名,但注意防SQL注入
${}字符串链接
select id,name,age from student where id =#{id}
-->select id,name,age from student where id ="1"
select id,name,age from student where id =${id}
-->select id,name,age from student where id =1
17.Spring中的数据库事务
package com.bjpowernode.app.controller;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
//1——数据库事务在业务层出现
//2——@EnableTransactionManagement作用是切换代理类型,默认是JDK,可以切换成CGLIB
//3——@Transactional默认捕获RuntimeException和Error
//4——一般情况处理事务@Transactional都会采取自定义异常,需要回滚的时候直接throw new
//5——@Transactional第一谈回滚(异常机制),第二谈隔离性isolation,第三谈传播性Propagation,第四谈@Transactional代理类型
//6——@Transactional事务一般谈到第一个就行了,后面的都需要其他知识补充
//7——@Transactional公司规范,第一自定义异常,第二不同情况根据自定义异常回滚,第三业务层使用事务绝对不会catch,第四控制catch
@Service
public class ServerAA {
public static class ExceptionA extends Exception {
}
public static class ExceptionB extends Exception {
}
public static class ExceptionC extends Exception {
}
@Transactional(rollbackFor ={ExceptionA.class,ExceptionB.class,ExceptionC.class} )
public String fun1() throws Exception {
if (1 == 2) {
throw new ExceptionA();
}
if (3 == 2) {
throw new ExceptionB();
}
if (4 == 5) {
throw new ExceptionC();
}
return "ok";
}
}
18.第三方服务调用失败了如何处理
//糊涂工具
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.4.1</version>
</dependency>
1——使用Hutool工具封装httpClient组件发起请求和响应,主要是为了获取响应对象
HttpResponse res = HttpRequest.post(url).execute();
2——判断HttpResponse响应对象的状态,不是200(200是成功)就重复请求。
3——重复请求3次,使用自定义计数器(int js=0),然后循环请求
4——重复请求3次也不行,就模拟熔断器,直接返回其它值(服务降级)
5——如果是SpringCloud则直接使用feign+hystrix即可
19.SQL语句中case函数用法(类似于判断,主要用于结果集列值根据条件判断显示什么)
表结构
id name a b c
1 hzj 56 66 78
2 11 77 155 99
3 22 89 77 34
4 33 45 66 99
4 33 45 66 99
问题:查询每个人最大的成绩
结果集效果:
name 最大成绩
hzj c=78
11 b=155
22 a=89
33 c=99
解决方案:使用case ,因为是列之间的比较,聚合是行聚合,列比较只能使用case
select
name,
CASE
WHEN a>b and a>c THEN concat(‘a=’,a)
WHEN b>a and b>c THEN concat(‘b=’,b)
WHEN c>a and c>b THEN concat(‘c=’,c)
END as 最大成绩
from stu;
20.线程池的作用,四个线程池有哪些,分别什么时候用
线程池其实就是一种多线程处理形式
使用线程池最大的原因就是可以根据系统的需求和硬件环境灵活的控制线程的数量,且可以对所有线程进行统一的管理和控制,从而提高系统的运行效率,降低系统运行运行压力
线程和任务分离,提升线程重用性;
控制线程并发数量,降低服务器压力,统一管理所有线程;
提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;
21.数据库连接池的作用,有哪些,你们用哪些,大概配置
- 数据库连接池是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的连接进行申请,使用,释放。
资源重用,避免了数据库连接频繁建立、关闭的开销 - 更快的系统响应速度,直接从连接池中获取连接,响应速度加快
- 控制资源的使用。如果不使用连接池,每次访问数据库都需要创建一个连接,这样系统的稳定性受系统连接需求影响很大,很容易产生资源浪费和高负载异常。连接池能够使性能最大化,将资源利用控制在一定的水平之下。连接池能控制池中的连接数量,增强了系统在大量用户应用时的稳定性。
常见的数据库连接池:DBCP、C3P0、Druid
我们主要使用阿里的Druid连接池
如何配置?
在StringContext文件中配置:
<!--配置数据源druid-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!--driverClassName可以不写,智能匹配-->
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!--整合MyBatis-->
<!-- 配置 SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--读取mybatis的主配置-->
<property name="configLocation" value="classpath:mybatis.xml"/>
<!--衔接数据源,目前使用的是数据库连接池-->
<property name="dataSource" ref="dataSource" />
<!--批量起别名,放在beans包中【也就是实体】-->
<property name="typeAliasesPackage" value="com.bjpowernode.beans" />
</bean>
<!-- 配置Spring整合MyBatis,扫描包 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--注册MyBatis的映射文件-->
<property name="basePackage" value="com.bjpowernode.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
主要就:
配置数据源,将数据库地址密码等配置进去
在配置mybatis的配置中配置中,将连接池添加进去即可
配置spring整合mybatis,扫描包
22.springBoot启动流程
springboot是依赖于spring的,比起spring,除了拥有spring的全部功能以外,springboot无需繁琐的xml配置,这取决于它自身强大的自动装配功能;并且自身已嵌入Tomcat、Jetty等web容器,集成了springmvc,使得springboot可以直接运行,不需要额外的容器,提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标,健康检测、外部配置等
springBoot启动器制作流程
自定义启动器:
单独配置一个项目
在单独的项目中,启动器依赖自动配置模块,然后也会扫描自动配置模块类路径下的META-INF目录下的 spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.sgl.mystarter.sglhello.config.HelloServiceAutoConfiguration
再springboot引入一个启动器依赖
<dependency>
<groupId>com.sgl.mystarter</groupId>
<artifactId>sglhello-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
然后需要注入属性则从配置文件中注入属性
@ConfigurationProperties(prefix = "sglhello.hello") //配置时候的注解
24.数据库事物四大特性、MySQL的隔离级别
- 原子性
- 一致性
- 隔离性
- 持续性
- 四种隔离级别:
-
- Read Uncommitted(读取未提交内容)
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果
就好比还没确定的消息,你却先知道了发布出去,最后又变更了,就是说瞎话了。常说的脏读,读到了还未提交的
脏读,读取到未提交事务的数据
- Read Uncommitted(读取未提交内容)
-
- Read Committed(读取提交内容)
这个事务只能看见已经提交事务所做的改变
只能读取到已经提交的事务
出现了不可重复读的问题,即事务A在两次查询的数据不一致,因为在两次查询之间事务B更新了一条数据
- Read Committed(读取提交内容)
-
- Repeatable Read(可重读) MySQL的默认事务隔离级别
它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行
但是如果修改后尚未提交,就会出现幻读
本次事务中不会读取到其他提交的事务数据,当前事务结束后再次读取会读到最新数据
- Repeatable Read(可重读) MySQL的默认事务隔离级别
-
- Serializable(可串行化)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题
- Serializable(可串行化)
25.List、Set、Map集合分别都在什么场景下使用
ArrayList:底层采用数组,非线程安全,查找快捷,增删麻烦,占用空间是连续的 把检索发挥到机制
LinkedList:底层采用链表,增删方便,查找麻烦 把随机增删发挥到极致
Vector集合是线程安全的数组,但是效率低
HashSet:将存入的数据存到HashMap的key中
TreeSet底层是treemap
sortSet:无序不可重复,但会自动排序
HashMap:底层是Hash表数据结构,根据HashCode进行计算哈希值
HashTable底层是哈希表,线程安全没人用
sortMap:底层是无序的,但是key会自动排序
List集合有序可重复,set集合无序不可重复