Mybatis,其中难点问题做了详细解释

  1. 什么是 Mybatis?
  2. Mybatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高.
    MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.
    通过 xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回(从执行 sql 到返回 result 的过程)
  3. Mybaits 的优点?
    基于 SQL 语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL 写在 XML 里,解除 sql 与程序代码的耦合,便于统一管理;提供 XML 标签,支持编写动态 SQL 语句,并可重用.
    与 JDBC 相比,减少了50%以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开关连接
    很好的与各种数据库兼容(因为 MyBatis 使用 JDBC 来连接数据库,所以只要 JDBC 支持的数据库 MyBatis 都支持)
    能够与 Spring 很好的集成
    提供映射标签,支持对象与数据库的 ORM 字段关系映射;提供对象关系映射标签,支持对象关系组件维护-
  4. MyBatis 框架的缺点?
  5. SQL 语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写 SQL 语句的功底有一定要求
    SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库
  6. MyBatis 框架适用场合?
  7. MyBatis 专注于 SQL 本身,是一个足够灵活的 DAO 层解决方案
  8. 对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis 将是不错的选择.
    1. MyBatis 与 Hibernate 有哪些不同?
  9. Mybatis 和 hibernate 不同,它不完全是一个 ORM 框架,因为 MyBatis 需要程序员自己编写 Sql 语句.
    Mybatis 直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需求变化要求迅速输出成果。但是灵活的前提是 mybatis 无法做到数据库无关性 ,如果需要实现支持多种数据库的软件,则需要自定义多套 sql 映射文件,工作量大.

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);
}

select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did = #{did} 方法2: 分步查询(嵌套查询)

(1)查询部门信息

(1)查询部门信息
DeptMapper接口

public interface DeptMapper {
/**
* 分步查询 查询部门及其所有的员工信息
* 第一步 查询部门信息
*/
Dept getDeptAndEmoByStepOne(@Param(“did”) Integer did);
}

<resultMap id="deptAndEmoByStepOneMap" type="Dept">
    <id property="did" column="did"></id>
    <result property="deptName" column="dept_name"></result>
    <collection property="emps"
                select="com.atguigu.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
                column="did">
    </collection>
</resultMap>
<select id="getDeptAndEmoByStepOne" resultMap="deptAndEmoByStepOneMap">
    select * from t_dept where did = #{did} </select>

2)根据部门id查询部门中的所有员工

EmpMapper:
public interface EmpMapper {
/**

  • 分步查询 查询部门及其所有的员工信息
  • 第一步 查询部门信息
  • 第二步 根据查询员工信息
    */
    List getDeptAndEmpByStepTwo(@Param(“did”) Integer did);
    }
    EmpMapper.xml
<select id="getDeptAndEmpByStepTwo" resultType="Emp">
    select * from t_emp where did = #{did} </select>
  1. MyBatis获取参数值的两种方式-- #{} 和 ${} 的区别是什么
  2. #{} 是预编译处理,${} 是字符串替换。
    Mybatis 在处理 #{} 时,会将 sql 中的 #{} 替换为 ? 号,调用 PreparedStatement 的 set 方法来赋值;Mybatis 在处理 ${} 时,就是把 ${} 替换成变量的值。
    使用 #{} 可以有效的防止 SQL 注入,提高系统安全性。
    ${}的本质就是字符串拼接
    #{}的本质就是占位符赋值

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();  
}

<?xml version="1.0" encoding="UTF-8" ?>  
  
  
	
 
   
	  
		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());
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值