MyBatis与Hibernate
📋 本文节选自《Java常见面试题 8000 道》,返回 查看全部章节
138、mybatis 中 #{}和 ${}的区别是什么?
- #{}带引号,
${}不带引号; - #{}可以防止SQL注入; 3、 ${}常用于数据库表名、orderby子句;
- 一般能用#{}就不要使用
${};
139、mybatis 是否支持延迟加载?延迟加载的原理是什么?
1、 mybatis是否支持延迟加载?;
延迟加载其实就是讲数据加载时机推迟,比如推迟嵌套查询的时机。
延迟加载可以实现先查询主表,按需实时做关联查询,返回关联表结果集,一定程度上提高了效率。
mybatis仅支持关联对象association和关联集合对象collection的延迟加载,association是一对一,collection是一对多查询,在mybatis配置文件中可以配置lazyloadingEnable=true/false。
2、 延迟加载的原理是什么?;
使用CGLIB为目标对象建立代理对象,当调用目标对象的方法时进入拦截器方法。
比如调用a.getB().getName(),拦截器方法invoke()发现a.getB()为null,会单独发送事先准备好的查询关联B对象的sql语句,把B查询出来然后调用a.setB(b),也是a的对象的属性b就有值了,然后调用getName(),这就是延迟加载的原理。
140、说一下 mybatis 的一级缓存和二级缓存?
一级缓存是session级别的缓存,默认开启,当查询一次数据库时,对查询结果进行缓存,如果之后的查询在一级缓存中存在,则无需再访问数据库;
二级缓存是sessionFactory级别的缓存,需要配置才会开启。当进行sql语句查询时,先查看一级缓存,如果不存在,访问二级缓存,降低数据库访问压力。
141、mybatis 有哪些执行器(Executor)?
1、 mybatis有三种基本的Executor执行器:;
(1)、SimpleExecutor
每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
(2)、PauseExecutor
执行update或select,以sql做为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而且放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。
(3)、BatchExecutor
执行update,将所有sql通过addBatch()都添加到批处理中,等待统一执行executeBatch(),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。
2、 作用范围:;
Executor的这些特点,都严格限制在SqlSession生命周期范围内。
3、 Mybatis中如何指定使用哪一种Executor执行器?;
在mybatis的配置文件中,可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数。
142、mybatis 和 hibernate 的区别有哪些?
1、 两者最大的区别;
针对简单逻辑,都有对应的代码生成工具,可以生成简单基本的dao层方法;
针对高级查询,mybatis要手动编写sql语句和resultMap,而hibernate有良好的映射机制;
2、 开发难度对比;
hibernate > mybatis
3、 日志统计;
hibernate有自己的日志统计功能,而mybatis需要借助log4j来记录日志。
4、 数据库扩展比较;
hibernate > mybatis
5、 缓存机制比较;
因为hibernate对查询对象有良好的管理机制,用户无需关心sql,所以使用二级缓存如果出现脏数据,系统会报错。
而mybatis,如果不能获取最新数据,应该避免缓存的使用,脏数据的出现会给系统的正常运行带来很大的隐患。
6、 如何选择;
1、 mybatis需要编写sql和映射规则,工作量大于hibernate;
2、 mybatis支持的工具也有限,不能像hibernate那样有许多插件可以帮助生成映射代码和关联关系;
3、 对于性能要求不太苛刻的系统,比如管理系统、ERP等推荐hibernate;
4、 对于性能要求高、响应快、灵活的系统,比如电商系统,推荐使用mybatis;
143、myBatis查询多个id、myBatis常用属性
myBatis查询多个id(我居然回答用对象来传递...)
Page<UserPoJo> getUserListByIds(@Param("ids") List<Integer> ids);<!--根据id列表批量查询user-->
<select id="getUserListByIds" resultType="com.guor.UserPoJo">
select * from student
where id in
<foreach collection="ids" item="userid" open="(" close=")" separator=",">
{userid}
</foreach>
</select>144、mybatis一级缓存、二级缓存
1、 一级缓存:指的是mybatis中sqlSession对象的缓存,当我们执行查询以后,查询的结果会同时存入sqlSession中,再次查询的时候,先去sqlSession中查询,有的话直接拿出,当sqlSession消失时,mybatis的一级缓存也就消失了,当调用sqlSession的修改、添加、删除、commit()、close()等方法时,会清空一级缓存;
2、 二级缓存:指的是mybatis中的sqlSessionFactory对象的缓存,由同一个sqlSessionFactory对象创建的sqlSession共享其缓存,但是其中缓存的是数据而不是对象当命中二级缓存时,通过存储的数据构造成对象返回查询数据的时候,查询的流程是二级缓存>一级缓存>数据库;
3、 如果开启了二级缓存,sqlSession进行close()后,才会把sqlSession一级缓存中的数据添加到二级缓存中,为了将缓存数据取出执行反序列化,还需要将要缓存的pojo实现Serializable接口,因为二级缓存数据存储介质多种多样,不一定只存在内存中,也可能存在硬盘中;
4、 mybatis框架主要是围绕sqlSessionFactory进行的,具体的步骤:;
1、 定义一个configuration对象,其中包含数据源、事务、mapper文件资源以及影响数据库行为属性设置settings;
2、 通过配置对象,则可以创建一个sqlSessionFactoryBuilder对象;
3、 通过sqlSessionFactoryBuilder获得sqlSessionFactory实例;
4、 通过sqlSessionFactory实例创建qlSession实例,通过sqlSession对数据库进行操作;
5、 代码实例;
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 加载类路径下的属性文件 -->
<properties resource="db.properties"/>
<!-- 设置类型别名 -->
<typeAliases>
<typeAlias type="cn.itcast.javaee.mybatis.app04.Student" alias="student"/>
</typeAliases>
<!-- 设置一个默认的连接环境信息 -->
<environments default="mysql_developer">
<!-- 连接环境信息,取一个任意唯一的名字 -->
<environment id="mysql_developer">
<!-- mybatis使用jdbc事务管理方式 -->
<transactionManager type="jdbc"/>
<!-- mybatis使用连接池方式来获取连接 -->
<dataSource type="pooled">
<!-- 配置与数据库交互的4个必要属性 -->
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
<!-- 连接环境信息,取一个任意唯一的名字 -->
<environment id="oracle_developer">
<!-- mybatis使用jdbc事务管理方式 -->
<transactionManager type="jdbc"/>
<!-- mybatis使用连接池方式来获取连接 -->
<dataSource type="pooled">
<!-- 配置与数据库交互的4个必要属性 -->
<property name="driver" value="${oracle.driver}"/>
<property name="url" value="${oracle.url}"/>
<property name="username" value="${oracle.username}"/>
<property name="password" value="${oracle.password}"/>
</dataSource>
</environment>
</environments>
<!-- 加载映射文件-->
<mappers>
<mapper resource="cn/itcast/javaee/mybatis/app14/StudentMapper.xml"/>
</mappers>
</configuration>public class MyBatisTest {
public static void main(String[] args) {
try {
//读取mybatis-config.xml文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//初始化mybatis,创建SqlSessionFactory类的实例
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//创建session实例
SqlSession session = sqlSessionFactory.openSession();
/*
* 接下来在这里做很多事情,到目前为止,目的已经达到得到了SqlSession对象.通过调用SqlSession里面的方法,
* 可以测试MyBatis和Dao层接口方法之间的正确性,当然也可以做别的很多事情,在这里就不列举了
*/
//插入数据
User user = new User();
user.setC_password("123");
user.setC_username("123");
user.setC_salt("123");
//第一个参数为方法的完全限定名:位置信息+映射文件当中的id
session.insert("com.cn.dao.UserMapping.insertUserInformation", user);
//提交事务
session.commit();
//关闭session
session.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}145、mybatis如何防止sql注入
注意:但凡是sql注入漏洞的程序,都是因为程序要接受来自客户端用户输入的变量或URL传递的参数,并且这个变量或参数是组成sql语句的一部分,对于用户输入的内容或传递的参数,我们应该要时刻保持警惕,这是安全领域里的【外部数据不可信任】的原则,纵观web安全领域的各种攻击方式,大多数都是因为开发者违反了这个原则而导致的,所以自然能想到,就是变量的检测、过滤、验证下手,确保变量是开发者所预想的。
1、 检查变量数据类型和格式;
数据类型检查,sql执行前,要进行数据类型检查,如果是邮箱,参数就必须是邮箱的格式,如果是日期,就必须是日期格式;
只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。
如果上述例子中id是int型的,效果会怎样呢?无法注入,因为输入注入参数会失败。比如上述中的name字段,我们应该在用户注册的时候,就确定一个用户名规则,比如5-20个字符,只能由大小写字母、数字以及汉字组成,不包含特殊字符。此时我们应该有一个函数来完成统一的用户名检查。不过,仍然有很多场景并不能用到这个方法,比如写博客,评论系统,弹幕系统,必须允许用户可以提交任意形式的字符才行,否则用户体验感太差了。
2、 过滤特殊符号;
3、 绑定变量,使用预编译语句;
146、 为什么要使用hibernate?;
1、 hibernate对jdbc进行了封装,简化了JDBC的重复性代码;
2、 hibernate对dao有一个封装类hibernateTemplate,可以继承它,实现简单的CRUD接口;
3、 hibernate使用注解和配置文件,可以对实体类和映射文件进行映射;
4、 hibernate有事务管理机制,保证了数据的安全性;
5、 hibernate有一级缓存和二级缓存;
146、hibernate 中如何在控制台查看打印的 sql 语句?
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true147、hibernate 有几种查询方式?
1、 导航对象图查询:根据已加载的对象,导航到其他对象;
例如,对于已经加载的Customer对象,调用它的getOrders().iterator()方法就可以导航到所有关联的Order对象,假如在关联级别使用了延迟加载检索策略,那么首次执行此方法时,hibernate会从数据库中加载关联的Order对象,否则就从缓存中获得Order对象。
2、 OID方式:按照对象的OID来检索对象;
Session的get()和load()方法提供了这种功能,如果在应用程序中先知道了OID,就可以使用这种方式检索对象。
get()和load()的用法完全一样,都需要两个参数,一个是持久化对象类名class,一个是行号OID,返回固定的某一行的数据,但是需要注意的是,当输入的OID不存在时,get()会返回一个空对象,load()则直接报错。
3、 HQL检索方式:(hibernatequerylanguage);
使用面向对象的HQL查询语言,session的find()方法用于执行HQL查询语句。此外,hibernate还提供了query接口,它是hibernate提供的专门的HQL查询接口,能够执行各种复杂的HQL查询语句。
它具备以下功能:
1、 在查询语句中设定各种查询条件;
2、 支持投影查询,即仅检索出对象的部分属性;
3、 支持分页查询;
4、 支持连接查询;
5、 支持分组查询;
6、 提供内置函数;
7、 能够调用用户自定义的SQL函数;
8、 支持子查询;
9、 支持动态绑定参数;
例如:
Query query = session.createQuery(“from UserPo”);获得一个query对象,注意参数字符串中不是一个SQL语句,from后面的是持久化对象名称;
List list = query.list();就可以获取数据库中对应表的数据集合。
4、 QBC检索方式:QueryByCriteria的API来检索对象;
这种API封装了基于字符串形式的查询语句,提供了更加面向对象的接口。
例:Criteria criteria = session.createCriteria(UserPo.class);创建一个Criteria对象,参数是所关联的持久化对象,criteria.add(Restrictions.ge("id",2));将查询条件加入对象中,后面的操作就和Query对象一样了。
5、 本地SQL;
使用本地数据库的SQL查询语句,hibernate会负责把检索到的JDBC ResultSet结果映射为持久化对象图。
148、hibernate 实体类可以被定义为 final 吗?
可以将hibernate的实体类定义为final,但这种做法不好。
因为hibernate会使用代理模式在延迟关联的情况下提高性能,如果你把实体类定义成final类之后,因为Java不允许对final类进行扩展,所以hibernate就无法再使用代理了,如此一来就限制了使用可以提升性能的手段。
不过,如果你的持久化类实现了一个接口,而且在该接口中声明了所有定义于实体类中的所有public的方法的话,就能避免出现前面所说的不利后果。
149、在 hibernate 中使用 Integer 和 int 做映射有什么区别?
hibernate是面向对象的ORM,所以一般定义成封装类型,要看数据库中的定义,如果数据库中有对应字段存在null值,就要定义Integer。也可以定义基本类型,在配置文件中写清楚即可。
150、什么是 Spring Boot?Spring Boot 有哪些优点?
1、 SpringBoot简介;
基于Spring4.0设计,不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化spring应用的整个搭建和开发过程。另外SpringBoot通过集成大量的框架使得依赖包的版本冲突、引用的不稳定性得到了解决。
2、 SpringBoot有哪些优点?;
1、 快速构建项目,可以选一些必要的组件;
2、 对主流框架的无配置集成;
3、 内嵌Tomcat容器,项目可独立运行;
4、 删除了繁琐的xml配置文件;
5、 极大地提高了开发和部署效率;
6、 提供starter,简化maven配置;
3、 SpringBoot有哪些缺点?;
1、 版本迭代速度快,一些模块改动很大;
2、 由于无须配置,报错时很难定位;
151、Spring Boot 中的监视器是什么?
监听器也叫listener,是servlet的监听器,可以用于监听web应用程序中某些对象的创建、销毁、增加、修改、删除等动作的发生,然后做出相应的响应处理。当范围对象的状态发生变化时,服务器自动调用监听器对象中的方法,常用于系统加载时进行信息初始化,统计在线人数和在线用户,统计网站的访问量。
配置监听器的方法:
通过@Component把监听器加入Spring容器中管理;
在application.properties中添加context.listener.classes配置;
在方法上加@EventListener注解;