#{} 和 ${} 的区别是什么?
-
#{}是预编译处理,$ {}是字符串替换。
-
mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;mybatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。
-
使用 #{} 可以有效的防止SQL注入,提高系统安全性。
-
排序语句建议使用 ${}
如果想要详细了解这个问题,可以参考这篇文章——详解
Mybaits的优缺点
优点
- 基于 sql 语言编程,不会对现有的应用程序或数据库设计有任何影响;
- sql 语句都写在 xml 文件中,实现了代码和sql语句得解耦,便于统一管理 sql 语句;
- 相较于 JDBC 减少了大量冗余代码,不用手动创建释放连接;
- 数据库兼容性好(基于 JDBC 连接数据库,JDBC 支持得数据库 mybatis 都支持)
- 可以与 Spring 很好的集成。
- 提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
缺点
- 需要手动编写大量的 sql ,尤其是字段多、需要关联很多表时;
- 数据库可移植性差(sql 语言依赖于数据库,不同的数据库 sql 语言有所不同)。
逻辑分页(内存分页)和物理分页
物理分页
物理分页依赖的是某一物理实体,这个物理实体就是数据库,比如MySQL数据库提供了limit关键字,程序员只需要编写带有limit关键字的SQL语句,数据库返回的就是分页结果。
逻辑分页
逻辑分页依赖的是程序员编写的代码。数据库返回的不是分页结果,而是全部数据,然后再由程序员通过代码获取分页数据,常用的操作是一次性从数据库中查询出全部数据并存储到List集合中,因为List集合有序,再根据索引获取指定范围的数据。
对比
-
数据库负担:物理分页每次都访问数据库,逻辑分页只访问一次数据库,物理分页对数据库造成的负担大。
-
服务器负担:逻辑分页一次性将数据读取到内存,占用了较大的内容空间,物理分页每次只读取一部分数据,占用内存空间较小。
-
实时性:逻辑分页一次性将数据读取到内存,数据发生改变,数据库的最新状态不能实时反映到操作中,实时性差。物理分页每次需要数据时都访问数据库,能够获取数据库的最新状态,实时性强。
-
适用场合:逻辑分页主要用于数据量不大、数据稳定的场合,物理分页主要用于数据量较大、更新频繁的场合。
MyBatis是如何进行分页的?分页插件的原理是什么?
MyBatis 使用 Rowbounds 对象进行分页,它是根据 ResultSet 执行内存分页而非物理分页。可以在 sql 中直接书写物理分页参数来完成物理分页功能。也可以使用分页插件实现物理分页功能。
分页插件的基本原理就是根据 mybatis 提供的插件接口,自定义分页插件,在插件的拦截方法内拦截执行的 sql,然后重写 sql,根据 dialect 方言, 添加对应的物理分页语句和物理分页参数。
MyBatis 使用 RowBounds 实现的分页是逻辑分页,也就是先把数据记录全部查询出来,然在再根据 offset 和 limit 截断记录返回(数据量大的时候会造成内存溢出),不过可以用插件或其他方式能达到物理分页效果。
为什么说 MyBatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?
Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 MyBatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具。
Mybatis 缓存
如果想要详细了解这个问题,可以参考这篇文章——mybatis的一级缓存和二级缓存
Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
第一种是使用标签,逐一定义数据库列名和对象属性名之间的映射关系。
第二种是使用sql列的别名功能,将列的别名书写为对象属性名。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
Mybatis动态sql有什么用?执行原理?有哪些动态sql?
Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断 并动态拼接sql的功能。
Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。
使用MyBatis的mapper接口调用时有哪些要求?
Mapper接口方法名和mapper.xml中定义的每个sql的id相同;
Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同;
Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;
Mapper.xml文件中的namespace即是mapper接口的类路径。
i++ 和 ++i
-
i++是先赋值,然后再自增;++i是先自增,后赋值。
-
i++ 不能作为左值,而++i 可以。左值是对应内存中有确定存储地址的对象的表达式的值,而右值是所有不是左值的表达式的值。一般来说,左值是可以放到赋值符号左边的变量。
i++ 是一个复合操作,分为如下三步:
- 读取 i 的值。
- 对 i 加 1。
- 将 i 的值写回内存。
List Set Map的使用场景
说说 List,Set, Queue, Map 四者的区别?
- List:有序可重复;
- Set:无序不可重复;
- Queue:有序可重复;
- Map:无需,key 不可重复,value 可重复
如果你经常会使用索引来对容器中的元素进行访问,那么List是你的正确的选择。如果你已经知道索引了的话,那么List的实现类比如ArrayList可以提供更快速的访问,如果经常添加删除元素的,那么肯定要选择LinkedList。
如果你想容器中的元素能够按照它们插入的次序进行有序存储,那么还是List,因为List是一个有序容器,它按照插入顺序进行存储。
如果你想保证插入元素的唯一性,也就是你不想有重复值的出现,那么可以选择一个Set的实现类,比如 HashSet、LinkedHashSet或者TreeSet。所有Set的实现类都遵循了统一约束比如唯一性,而且还提供了额外的特性:比如TreeSet还是一个SortedSet,所有存储于TreeSet中的元素可以使用Java里的Comparator或者Comparable进行排序。LinkedHashSet也按照元素的插入顺序对它们进行存储。
如果你以键和值的形式进行数据存储那么 Map 是你正确的选择。你可以根据你的后续需要从 Hashtable、HashMap、TreeMap 中进行选择。
总之要根据集合特性进行合适选择。
两层循环内层break会咋样
只会跳出内层循环。
如何跳出两层循环?
使用 OUT 标识符
public static void main(String[] args) {
test();
}
public static void test(){
for(int i = 0; i < 3; ++i){
for(int j = 0; j < 1; ++j){
if(i == 1) break ;
System.out.println(i + ":" + j);
}
}
}
Output:
0:0
2:0
public static void main(String[] args) {
test();
}
public static void test(){
OUT:
for(int i = 0; i < 3; ++i){
for(int j = 0; j < 1; ++j){
if(i == 1) break OUT;
System.out.println(i + ":" + j);
}
}
}
Output:
0:0