Hibernate 对象/关系映射能力强, 数据库无关性好 ,对于关系模型要求高的软件,如果用 hibernate 开发可以节省很多代码,提高效率
6. MyBatis和其它持久化层技术对比
JDBC
SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
代码冗长,开发效率低
Hibernate 和 JPA
操作简便,开发效率高
程序中的长难复杂 SQL 需要绕过框架
内部自动生产的 SQL,不容易做特殊优化
基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
反射操作太多,导致数据库性能下降
MyBatis
轻量级,性能出色
SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
开发效率稍逊于HIbernate,但是完全能够接受
7. 谈谈MyBatis和JPA的区别
ORM映射不同:
MyBatis是半自动的ORM框架,提供数据库与结果集的映射;
JPA(默认采用Hibernate实现)是全自动的ORM框架,提供对象与数据库的映射。
可移植性不同:
JPA通过它强大的映射结构和HQL语言,大大降低了对象与数据库的耦合性;
MyBatis由于需要写SQL,因此与数据库的耦合性直接取决于SQL的写法,如果SQL不具备通用性而用了很多数据库的特性SQL的话,移植性就会降低很多,移植时成本很高。
SQL优化上的区别:
由于Mybatis的SQL都是写在XML里,因此优化SQL比Hibernate方便很多。
而Hibernate的SQL很多都是自动生成的,无法直接维护SQL。虽有HQL,但功能还是不及SQL强大,见到报表等复杂需求时HQL就无能为力,也就是说HQL是有局限的Hhibernate虽然也支持原生SQL,但开发模式上却与ORM不同,需要转换思维,因此使用上不是非常方便。 总之写SQL的灵活度上Hibernate不及Mybatis。
8. MyBatis输入输出支持的类型有哪些?
parameterType
MyBatis支持多种输入输出类型,包括:
简单的类型,如整数、小数、字符串等;
集合类型,如Map等;
自定义的JavaBean。
其中,简单的类型,其数值直接映射到参数上。对于Map或JavaBean则将其属性按照名称映射到参数上。
9. MyBatis里如何实现一对多关联查询?
MyBatis 实现一对多有 联合查询 和 嵌套查询 。联合查询是几个表联合查询,只查询一次,通过在 resultMap 里面的 collection 节点配置一对多的类就可以完成;嵌套查询是先查一个表,根据这个表里面的结果的外键 id,去再另外一个表里面查询数据,也是通过配置 collection,但另外一个表的查询通过 select 节点配置。
一对多:例如:根据部门id查找部门以及部门中的员工信息
⚠️ 需要查询一对多、多对一的关系,需要在“一”的pojo中加入List<多>属性,在“多”的pojo中加入“一”。
也就是说,在Dept类中,要加入 private List emps; ;在Emp类中,要加入 private Dept dept; 。然后给他们各自添加get、set方法,重写构造器和toString()
public class Dept {
private Integer did;
private String deptName;
private List emps;
//…构造器、get、set方法等
}
方法1:collection(联合查询)
DeptMapper接口
public interface DeptMapper {
/**
* 获取部门以及部门中所有的员工信息
*/
Dept getDeptAndEmp(@Param(“did”) Integer did);
}
(1)查询部门信息
(1)查询部门信息
DeptMapper接口
public interface DeptMapper {
/**
* 分步查询 查询部门及其所有的员工信息
* 第一步 查询部门信息
*/
Dept getDeptAndEmoByStepOne(@Param(“did”) Integer did);
}
2)根据部门id查询部门中的所有员工
EmpMapper:
public interface EmpMapper {
/**
KaTeX parse error: Expected 'EOF', got '#' at position 52: …值时,需要手动加单引号; 但是#̲{}使用占位符赋值的方式拼接s…设置参数时,MyBatis只是创建普通的SQL语句,然后在执行SQL语句时MyBatis将参数直接拼入到SQL里。这种方式在效率、安全性上均不如前者,但是可以解决一些特殊情况下的问题。例如,在一些动态表格(根据不同的条件产生不同的动态列)中,我们要传递SQL的列名,根据某些列进行排序,或者传递列名给SQL都是比较常见的场景,这就无法使用预编译的方式了。
总结:分成两种情况进行处理
实体类类型的参数 (若mapper接口中的方法参数为实体类对象时此时可以使用KaTeX parse error: Expected ‘EOF’, got ‘#’ at position 4: {}和#̲{},通过访问实体类对象中的属…{}需要手动加单引号
使用@Param标识参数
``java
public interface ParameterMapper {
/**
* 添加用户信息
*/
int insertUser(User user);
}
public interface ParameterMapper { /** * 验证登录 (使用@Param) */ User checkLoginByParam(@Param("username") String username, @Param("password") String password); } 11. 既然 ${}不安全,为什么还需要用它,什么时候会用到它? 是通过xml文件中 根标签的namespace属性进行绑定的,即namespace属性的值需要配置成接口的全限定名称,MyBatis内部就会通过这个值将这个接口与这个xml关联起来。 MyBatis中可以面向接口操作数据,要保证两个一致 mapper接口的全类名和映射文件的命名空间(namespace)保持一致 mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致 package com.atguigu.mybatis.mapper; public interface UserMapper { /** 添加用户信息 */ int insertUser(); } insert into t_user values(null,'张三','123',23,'女') 13. MyBatis分页和自己写的分页哪个效率高? 自己写的分页效率高。 在MyBatis中,我们可以通过分页插件实现分页,也可以通过分页SQL自己实现分页。其中,分页插件的原理是,拦截查询SQL,在这个SQL基础上自动为其添加limit分页条件。它会大大的提高开发的效率,但是无法对分页语句做出有针对性的优化,比如分页偏移量很大的情况,而这些在自己写的分页SQL里却是可以灵活实现的。 14. 了解MyBatis缓存机制吗? MyBatis的缓存分为一级缓存和二级缓存。 14.1、MyBatis的一级缓存 一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问 使一级缓存失效的四种情况: 不同的SqlSession对应不同的一级缓存 同一个SqlSession但是查询条件不同 同一个SqlSession两次查询期间执行了任何一次增删改操作 同一个SqlSession两次查询期间手动清空了缓存 14.2、MyBatis的二级缓存 二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取 二级缓存开启的条件 cacheEnabled="true" 二级缓存失效的情况: 两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效。没有提交sqlsession时,数据会保存在一级缓存中,提交后,会保存在二级缓存中。 14.3、MyBatis缓存查询的顺序 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用 如果二级缓存没有命中,再查询一级缓存 如果一级缓存也没有命中,则查询数据库 SqlSession关闭之后,一级缓存中的数据会写入二级缓存 15. 当实体类中的属性名和表中的字段名不一样 ,怎么办 ? 若 字段名 (数据库里的名字例如emp_name)和 实体类中的属性名 不一致,但是字段名符合数据库的规则(使用 _ ),实体类中的属性 名符合Java的规则(使用驼峰),此时也可通过以下两种方式处理字段名和实体类中的属性的映射关系 15.1 用起别名的方式保证字段名与属性名一致 和sql中一样,用字段名 属性名(如emp_name empName)来使二者一致。 select eid, emp_name empName, age, sex, email from t_emp 15.2 逐一设置resultMap映射关系 31、延迟加载31、延迟加载 分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息。 lazyLoadingEnabled aggressiveLazyLoading 此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=“lazy(延迟加载)|eager(立即加载)” mybatis-config.xml @Test public void getEmpAndDeptByStepOne() { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Emp emp = mapper.getEmpAndDeptByStepOne(1); System.out.println(emp.getEmpName()); } 关闭延迟加载,两条SQL语句都运行了 开启延迟加载,只运行获取emp的SQL语句 @Test public void getEmpAndDeptByStepOne() { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Emp emp = mapper.getEmpAndDeptByStepOne(1); System.out.println(emp.getEmpName()); System.out.println("----------------"); System.out.println(emp.getDept()); }