云趣科技面试

一、Mybatis中${}和#{}区别

${}的本质是字符串拼接,#{}的本质是占位符赋值

  1. 若mapper接口方法的参数是单个的字面量类型,此时可以通过#{}和${}以任意的内容获取参数值,但一定要注意${}的单引号问题
  2. 若mapper接口方法的参数是多个的字面量类型,此时mybatis会将参数放在map集合中,以两种方式存储数据(a.以args0,args1...为键,以参数为值  b.以param1,param2...为键,以参数为值)因此只需要通过#{}和${}访问map集合的键就可以获取相对应的值,但一定要注意${}的单引号问题
  3. 若mapper接口方法的参数是map集合类型的参数,只需要通过#{}和${}访问map集合的键就可以获取相对应的值,但一定要注意${}的单引号问题
  4. 若mapper接口方法的参数是实体类类型的参数,只需要通过#{}和${}访问实体类中的属性名就可以获取相对应的值,但一定要注意${}的单引号问题
  5. 若mapper接口方法的参数上设置@Param注解,此时mybatis会将参数放在map集合中,以两种方式存储数据(a.以@Param注解value的属性值为键,以参数为值  b.以param1,param2...为键,以参数为值)因此只需要通过#{}和${}访问map集合的键就可以获取相对应的值,但一定要注意${}的单引号问题

解法:首先Mybatis里面提供了#号和$号两种占位符,都是去实现动态SQL的一种方式,通过这两种方式,可以把参数传递到XML里面,那么在传递以后呢,在执行操作之前,Mybatis会对这两个占位符进行动态的解析(#{}等同于jdbc里面一个?号占位符,它相当于向PreparedStatement的里面的预处理语句设置参数,而PreparedStatement里面的SQL语句呢它是预编译的,那么SQL语句里使用了占位符规定的SQL语句的一个结构,并且setstring方法在设置参数的时候,如果有特殊字符会自动进行转义,所以#号占位符可以防止SQL注入,而使用$的方式传参呢相当于直接把参数拼接到了原始的SQL里面,Mybatis不会对它进行任何的特殊处理,所以${}和#{}的最大区别就是前者是动态参数后者是占位符,动态参数无法防止SQL注入的一个问题,所以在实际应用里面呢,尽可能的应该去使用#号占位符,另外${}的动态传参可以适合应用在一些动态SQL场景里面比如动态传递表名或者动态设置排序字段等

模糊查询三种情况处理

select * from t_user where username = '%${username}%'

select * from t_user where username like concat('%',#{username},'%')

select * from t_user where username = "%"#{username}"%"

批量删除ids和动态处理表名时必须使用${}

delete from t_user where ids in (${ids})

select * from ${tablename}

查询注意事项

若sql语句查询的结果为多条时,一定不能以实体类类型作为方法的返回值,否则会抛出异常TooManyResultsException,而若sql语句查询的结果为1条时,此时可以使用实体类类型或者list集合作为方法的返回值

若查询结果使用Map集合来存储,那么查询结果如果值为null时它不会存放到map集合中

{password=123456,gender=男,id=1,age=23,[email protected],username=admin}

{password=123,id=4,username=lisi}

若查询的数据有多条时,并且要将每条数据转换为map集合,此时有两种解决方案

1.将mapper接口方法的返回值设置为泛型是map的list集合

List<Map<String,Object>> getAllUserToMap()

2. 可以将每条数据转换的map集合放在一个大的map中,但是必须要通过@MapKey注解将查询的某个字段的值作为大的map的键例如id

@MapKey("id")

Map<String,Object>>getAllUserToMap()

字段名和属性名不一样的情况下,如何处理映射关系

1.为查询的字段设置别名,和属性名保持一致

2.当字段符合mysql的要求使用_,而属性符合java的要求使用驼峰,此时可以在mybatis的核心配置文件中设置一个全局配置,可以自动将下划线映射为驼峰 

<settings>
    <!--将下划线映射为驼峰-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

二、IOC和AOP的理解

IOC:控制反转是一种设计思想,即原本在程序中需要自己手动创建对象的控制权,交由spring框架来管理。IOC容器底层就是对象工厂(BeanFactory接口)。IOC的原理是基于xml解析、工厂设计模式、反射来实现的。 IOC 容器实际上就是个Map(key,value)Map 中存放的是各种对象。

结论:之前需要我们自己手动new对象的,但是我们现在不需要反复去new对象了,而是把new对象的主动权交给IOC容器,我们什么时候用什么时候取就可以了。

AOP:就是能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

结论:就是不通过修改源代码方式,在主干功能里面添加新功能。 

1.有接口的情况,使用 JDK 动态代理,即创建接口实现类代理对象,增强类的方法。

2.没有接口的情况,使用 CGLIB 动态代理,即创建子类的代理对象,增强类的方法。

三、SpringMVC执行流程

1. 用户发起请求到前端控制器

2.前端控制器接收请求调用处理器映射器

3.处理器映射器根据请求中的url找到具体的处理器,生成处理器对象及拦截器对象,一并返回给前端控制器

4.前端控制器调用处理器适配器执行处理器

5.处理器适配器执行处理器

6.处理器执行完向处理器适配器返回ModelAndView

7.处理器适配器向前端控制器返回ModelAndView

8.前端控制器请求视图解析器进行视图解析

9.视图解析器解析完后向前端控制器返回视图View

10.前端控制器对视图View进行渲染

11.前端控制器给用户响应结果

四、数据库内外连接区别

内连接:指连接结果仅包含符合连接条件的行,参与连接的两个表都应该符合连接条件。

外连接:连接结果不仅包含符合连接条件的行同时也包含自身不符合条件的行。包括左外连接、右外连接和全外连接。

左外连接:左边表数据行全部保留,右边表保留符合连接条件的行。

右外连接:右边表数据行全部保留,左边表保留符合连接条件的行。

全外连接:左外连接 union 右外连接。

五、数据库隔离级别

读未提交、读已提交、可重复读和串行化

六、Oracle数据库truncate和delete区别

1、truncate 是DDL 语句,delete是DML语句。

2、truncate 的速度远快于delete。当执行delete操作时所有表数据先被COPY到回滚表空间,数据量不同花费时间长短不一。而truncate 是直接删除数据,不进回滚表空间。

3、delete 数据可以运行rollback 进行数据回滚。而truncate 则是永久删除,不能回滚。

4、truncate 操作不会触发表上的delete触发器,而delete 会正常触发。

5、truncate 语句不能带 where 条件,意味着只能进行全部数据删除,而delete可带 where 条件进行局部删除数据。

6、truncate 操作会重置表的高水位线(High Water Mark),而delete 不会。

7、delete可以操作视图,truncate不能操作视图。

七、适配器模式

定义:将一个接口转换成客户所希望的另一个接口,使接口不兼容的那些类可以一起工作。

八、 索引失效

1.不满足最左匹配原则

2.使用了select *

3.索引列上有计算,如explain select name from user where id+1=2;

4.索引列上用了函数

5.字段类型不同

6.模糊查询like左边包含%

7.列进行对比 id=height

8.如果使用了or关键字,那么它前面和后面的字段都要加索引,不然所有的索引都会失效,这是一个大坑。

9.主键字段中使用not in关键字查询数据范围,仍然可以走索引。而普通索引字段使用了not in关键字查询数据范围,索引会失效。

10.如果order by语句中没有加where或limit关键字,该sql语句将不会走索引。

11.如果对多个索引进行order by,索引也失效了

12. 不同的排序

九、Linux命令chown和chmod功能描述 

chown用法:用来更改某个目录或文件的用户名和用户组。

chmod用法:用来修改某个目录或文件的访问权限。

十、多线程的实现方式及其区别

  1. 继承Thread类:可以通过继承Thread类并重写run()方法来创建线程。需要注意的是,Java中不能直接继承多个类,因此这种方式不能同时继承其他类。
  2. 实现Runnable接口:可以通过实现Runnable接口并实现run()方法来创建线程。这种方式可以避免由于Java的单继承限制而带来的问题,并且能够更好地支持多个线程共享同一个资源。
  3. 实现Callable接口:与Runnable接口类似,但是Callable接口的call()方法可以返回执行结果或抛出异常,且必须通过FutureTask类或ExecutorService.submit()方法等方式来执行。
  4. 使用线程池:Java提供了Executor框架,它封装了线程池的创建和管理,使得开发者可以更方便地管理多个线程,避免线程过多或线程资源浪费等问题,它可以管理多个线程并且可以复用已经创建的线程,避免了频繁地创建和销毁线程的开销。
     

submit() 和 invokeAll() 都是 ExecutorService 接口中定义的方法,用于向线程池提交任务。它们的主要区别在于返回值和异常处理:

submit() 方法用于提交一个 Callable 或 Runnable 任务,并返回一个表示该任务待处理结果的 Future 对象。通过 Future 对象可以判断任务是否完成、取消任务、获取任务的执行结果等。

invokeAll() 方法用于同时提交多个 Callable 任务,并返回一个 Future 对象列表。这个方法会阻塞当前线程,直到所有任务都执行完成或者超时,然后返回所有任务的执行结果列表。

在性能方面,submit() 和 invokeAll() 的差别不大,因为它们都是基于线程池实现的。但是,它们的使用场景不同,如果需要同时提交多个任务,并等待所有任务完成后再进行处理,使用 invokeAll() 会更加方便和高效;如果只需要提交单个任务,并处理任务的执行结果,那么使用 submit() 更为适合。

猜你喜欢

转载自blog.csdn.net/bubbleJessica/article/details/130647586
今日推荐