mybatis总结

常见面试题:

1.ORM框架工作原理

    1、以一定的映射方式,实体模型和数据库关系的映射

    2ORM框架启动时加载这些映射和数据库配置文件

    3ORM通过对最原生jdbc的封装提供更加便利的操作API

    4Dao通过ORM提供的便捷API以对象的方式操作数据库关系。

2.JDBC编程有哪些不足之处,MyBatis是如何解决这些问题的?

  (1)mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。

  (2)mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。

 3.MyBatis与Hibernate有哪些不同?

   (1)  Hibernate是一个完整ORM框架,常规CRUD不需要写SQL语句,会自动生成SQL;  MyBatis 并不是一个完整的ORM框架,因为我们还需要自己去写全部SQL.

  (2)Mybatis 简单易学,程序员直接编写原生态sql,可控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。 

  (3)Hibernate对象/关系映射能力强,数据库无关性好对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的缺点是学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。

4. mybatis的基本工作流程  

  1.读取配置文件(配置文件包含数据库连接信息和Mapper映射文件或者Mapper包路径)

    Mybatis 读取XML配置文件后会将内容放在一个Configuration类中,Configuration类会存在整个Mybatis生命周期,以便重复读取。

  2. SqlSessionFactoryBuilder会读取Configuration类中信息创建SqlSessionFactory。SqlSessionFactory的生命周期是程序级,程序运行的时候建立起来,程序结束的时候消亡。

  3.SqlSessionFactory建立SqlSession,目的执行sql语句,SqlSession是过程级,一个方法中建立,方法结束应该关闭

  4.当用户使用mapper.xml文件中配置的的方法时,mybatis首先会解析sql动态标签为对应数据库sql语句的形式,并将其封装进MapperStatement对象,然后通过executor将sql注入数据库执行,并返回结果。

  5.将返回的结果通过映射,包装成java对象。

 5. #{}和${}的区别是什么?

#{}是预编译处理,Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;防止SQL注入, 提高系统安全性.

${}是字符串替换。Mybatis在处理${}时,就是把${}替换成变量的值(没有  “ ” ),一般用与排序 和 分页场景。

6.当实体类中的属性名和表中的字段名不一样 ,怎么办 ?

  第1种: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。

1 <select id=”selectorder” parametertype=”int” resultetype=”me.gacl.domain.order”>
2        select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
3  </select>

  第2种: 通过<resultMap>来映射字段名和实体类属性名的一一对应的关系.

 1 <select id="getOrder" parameterType="int" resultMap="orderresultmap">
 2         select * from orders where order_id=#{id}
 3     </select>
 4  
 5    <resultMap type=”me.gacl.domain.order” id=”orderresultmap”>
 6         <!–用id属性来映射主键字段–>
 7         <id property=”id” column=”order_id”>
 8  
 9         <!–用result属性来映射非主键字段,property为实体类属性名,column为数据表中的属性–>
10         <result property = “orderno” column =”order_no”/>
11         <result property=”price” column=”order_price” />
12     </reslutMap>
13  

 7. 模糊查询like语句该怎么写?

8. 通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?

 Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法内的参数,就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement,

举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace为com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。

在Mybatis中,每一个<select>、<insert>、<update>、<delete>标签,都会被解析为一个MappedStatement对象。

        Dao接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。

        Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回。

9.Mybatis是如何进行分页的?分页插件的原理是什么?

 Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。

 分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。

10.Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

第一种是使用<resultMap>标签,逐一定义列名和对象属性名之间的映射关系。

第二种是使用sql列的别名功能,将列别名书写为对象属性名,比如T_NAME AS NAME,对象属性名一般是name,小写,但是列名不区分大小写,Mybatis会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成T_NAME AS NaMe,Mybatis一样可以正常工作。

 有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

 11.如何获取自动生成的(主)键值?

在xml映射器中配置useGeneratedKeys参数,设定keyProperty的值为在Entity里面的字段名,通过实体类对象的getId()方法得到对应的值就是数据库中的主键值。

12. mybatis一级缓存二级缓存

一级缓存

Mybatis在没有配置的默认情况下只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。

1、一级缓存的生命周期有多长?

  a、MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象。Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。

  b、如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用。

  c、如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用。

  d、SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用。

二级缓存:

SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存的开席需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。 也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置就可以开启缓存了<cache/>,如果我们配置了二级缓存就意味着:

  • 映射语句文件中的所有select语句将会被缓存。
  • 映射语句文件中的所欲insert、update和delete语句会刷新缓存。
  • 缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回。
  • 根据时间表,比如No Flush Interval,(CNFI没有刷新间隔),缓存不会以任何时间顺序来刷新。
  • 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用
  • 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改。

参考mybatis面试总结:https://blog.csdn.net/a745233700/article/details/80977133

常见编程题 (涵盖:批量插入,批量删除)

1. SQL映射器Mapper接口

MyBatis基于代理机制,可以让我们无需再编写Dao的实现。直接把以前的dao接口定义成符合规则的Mapper

注意事项:

① :接口必须Mapper结尾,名字是DomainMapper

② :mapper.xml文件要和Mapper接口建立关系,通过namespace:要能连接到Mapper接口

③ : User get(Long id)返回值,方法,参数类型等必须使:Mapper接口和mapper.xml保持一致

 具体步骤代码如下: 
1. 核心配置文件:mybatis-config.xml 及 db.properties 

<?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>
	<!-- 引入db.properties -->
	<properties resource="db.properties"></properties>
	
	<!-- 环境们的配置 -->
	<environments default="development">
		<!--环境的配置:  -->
		<environment id="development">
			<!--使用jdbc方式管理事务 -->
			<transactionManager type="JDBC" />
			<!-- 数据源的四大配置 -->
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driverClassName}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<!-- 注册对象关系映射文件 -->
	<mappers>
		<mapper resource="com/gs/domain/UserMapper.xml"/>
	</mappers>
</configuration>

  

db.properties 文件

jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8
jdbc.username = root
jdbc.password = root

  

2. 创建sqlSession 对象的工具类:MyBatisUtils.java

package com.gs.tools;

import java.io.IOException;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MyBatisUtils {
	private static SqlSessionFactory factory= null;
	static {
		try {
			factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public static SqlSession getSession(){
		return factory.openSession();
	}
}

 

3. User.java 三个字段。

public class User {
	private Long id;
	private String username;
	private String password;
    ......getter setter 无参 有参,toString()
    
}    

  

4. UserMapper 接口

public interface UserMapper {
	
	 //得到一个用户
	User get(Long id);
	
	 // 得到所有用户
	List<User> getAll();
	
	 //批量删除用户
	void deleteBatch(List<Long> list);
	
	 //批量插入用户
	void insertBatch(List<User> lists);
}

  

5.UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 使用sql映射器Mapper接口的方式
namespace的值 是  对应接口的全限定名称

 -->
<mapper namespace="com.gs.mapper.UserMapper">
	
	<!-- 得到一个用户 -->
	<select id="get" parameterType="long" resultType="com.gs.domain.User">
		select * from user where id = #{?}
	</select>
	
	<!--得到所有用户  -->
	<select id="getAll" resultType="com.gs.domain.User">
		select * from user 
	</select>
	
	<!--批量删除用户  -->
	<delete id="deleteBatch" parameterType="list">
		delete from user where id in 
		<foreach collection="list" index="index" open="(" separator="," close=")" item="ids">
			#{ids}
		</foreach>
	</delete>
	
	<!-- 批量插入用户 -->
	<insert id="insertBatch" parameterType="list">
	insert into user(id,username,password) 
	values   
	<foreach collection="list" item="item" index="index" separator=",">
		(#{item.id},#{item.username},#{item.password})
	</foreach> 

	</insert>
</mapper> 

  

6.测试类 testMybatis.java

package com.gs.test;

import java.util.ArrayList;
import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import com.gs.domain.User;
import com.gs.mapper.UserMapper;
import com.gs.tools.MyBatisUtils;

public class testMybatis{
	@Test
	public void getuser(){
		SqlSession session = MyBatisUtils.getSession();
		//获取UserMapper接口的对象
		UserMapper userMapper = session.getMapper(UserMapper.class);
		//namespace = 接口全限定名称 +.+ 方法名
		User user = userMapper.get(1L);
		System.out.println(user);
	}
	@Test
	public void getAllUser(){
		SqlSession session = MyBatisUtils.getSession();
		UserMapper userMapper = session.getMapper(UserMapper.class);
		List<User> users = userMapper.getAll();
		System.out.println(users);
	}
	@Test
	public void deleteBatchUser(){
		SqlSession session = MyBatisUtils.getSession();
		UserMapper userMapper = session.getMapper(UserMapper.class);
		List<Long> lists = new ArrayList<>();
		lists.add(2L);
		lists.add(3L);
		userMapper.deleteBatch(lists);
		session.commit();//提交事务
		session.close();
	}
	@Test
	public void insertBatchUser(){
		SqlSession session = MyBatisUtils.getSession();
		UserMapper userMapper = session.getMapper(UserMapper.class);
		List<User> lists = new ArrayList<>();
		lists.add(new User(null,"樊俊杰","456"));
		lists.add(new User(null,"樊太快了","789"));
		userMapper.insertBatch(lists);
		session.commit();//提交事务
		session.close();
	}
}

  

 

2. MyBatis提供两种方式处理我们多表关联对象,嵌套查询和嵌套结果。

 1.(多对一)关联映射。  (一个部门有多个用户,先从多个用户入手。

1. User 和 Dept 实体类

public class User {
	private Long id;
	private String name;
	private String password;
	private Dept dept;
}

public class Dept {
	private Long id;
	private String name;
	
}

  

2.  DomainMapper.xml

<!-- 单向多对一嵌套结果(发一条左外连接sql解决问题,映射文件Mapper结果的手动封装ResultMap)
		使用嵌套结果映射来处理重复的联合(association)结果的子集。
	-->
	<select id="getUser1" resultMap="many2oneMap">
		select u.id, u.name, u.password,  d.id did,  d.name dname
		from t_user u  left join t_dept d  on u.dept_id=d.id; 
	</select>
	
	<!-- 封装查询的User结果 -->
	<resultMap type="com.gs.manytoone.User" id="many2oneMap">
		<id column="id" property="id"/>
		<result column="name" property="name"/>
		<result column="password" property="password"/>
		<!--user的dept属性是一个对象Dept,使用联合查询关键字来封装 association:联合查询-->
		<association property="dept" javaType="com.gs.manytoone.Dept">
			<id column="did" property="id"/>
			<result column="dname" property="name"/>
		</association>
	</resultMap>
	
	
	
	<!-- 单向多对一嵌套查询(先查询多方User,使用ResultMap来封装结果,在结果里,再通过User的dept属性,再查一次Dept对象.)
	 -->
	 <select id="getUser2" resultMap="many2oneList">
	 		select id,name,password,dept_id from t_user;
	 </select>
	 <select id="getDeptById" parameterType="long" resultType="com.gs.manytoone.Dept">
	 		select id,name from t_dept where id= #{id}
	 </select>
	 <resultMap type="com.gs.manytoone.User" id="many2oneList">
	 		<id column="id" property="id"/>
			<result column="name" property="name"/>
			<result column="password" property="password"/>
			<association property="dept" column="dept_id" 
						select="com.gs.manytoone.DomainMapper.getDeptById">
			</association>
	 </resultMap>

  

3. 测试类

@Test
	public void getUser1() {
		SqlSession session = MyBatisUtils.getSession();
		List<User> users = session.selectList("com.gs.manytoone.DomainMapper.getUser1");
		for (User user : users) {
			System.out.println(user);
		}
		session.commit();
		session.close();
	}
	
	@Test
	public void getUser2() {
		SqlSession session = MyBatisUtils.getSession();
		List<User> users = session.selectList("com.gs.manytoone.DomainMapper.getUser2");
		for (User user : users) {
			System.out.println(user);
		}
		session.commit();
		session.close();
	}

  

4.查询的结果:

User [id=1, name=张三, password=123456, dept=Department [id=1, name=财务部]]
User [id=2, name=李四, password=111, dept=Department [id=1, name=财务部]]
User [id=3, name=五五, password=123, dept=Department [id=2, name=后勤部]]
User [id=4, name=周六, password=122, dept=Department [id=3, name=经理部]]
User [id=5, name=周七, password=12332, dept=Department [id=3, name=经理部]]

2.  (一对多,多对多)集合映射

员工和部门:

   在部门方,需要查询到当前部门下的所有员工。----集合查询

1. User 和 Dept 实体类

public class User {
	private Long id;
	private String name;
	private String password;
}
public class Dept {
	private Long id;
	private String name;
	private Set<User> users = new HashSet<>();
}

  

2.  DomainMapper.xml

<!-- 单向一对多嵌套结果(一条sql语句) -->
	<select id="getDept1" resultMap="one2manyMap">
		select d.id did, d.name dname, u.id, u.name, u.password 
		from t_dept d left join t_user u
		on d.id=u.dept_id order by d.id
	</select>
	<resultMap type="com.gs.onetomany.Dept" id="one2manyMap">
		<id column="did" property="id"/>
		<result column="dname" property="name"/>
		<collection property="users" ofType="com.gs.onetomany.User"> 
			<id column="id" property="id"/>
			<result column="name" property="name"/>
			<result column="password" property="password"/>
		</collection>
	</resultMap>
	
	
	<!-- 单向一对多嵌套查询(先查询多个Dept,使用ResultMap来封装结果,在结果里,再通过Dept的users属性,再查一次User对象.) -->
	<select id="getDept2" resultMap="one2manyList">
		select d.id did , d.name dname from t_dept d
	</select>
	<select id="getUserById" parameterType="long" resultType="com.gs.onetomany.User">
		select id,name,password from t_user where dept_id=#{?}
	</select>
	<resultMap type="com.gs.onetomany.Dept" id="one2manyList">
		<id column="did" property="id"/>
		<result column="dname" property="name"/>
		<collection property="users" column="did"
					select="com.gs.onetomany.DomainMapper.getUserById">
		</collection>
	</resultMap>

  

3. 测试类

@Test
	public void getUser1() {
		SqlSession session = MyBatisUtils.getSession();
		List<Dept> allDept = session.selectList("com.gs.onetomany.DomainMapper.getDept1");
		for (Dept dept : allDept) {
			System.out.println(dept);
		}
		session.commit();
		session.close();
	}
	
	@Test
	public void getUser2() {
		SqlSession session = MyBatisUtils.getSession();
		List<Dept> allDept = session.selectList("com.gs.onetomany.DomainMapper.getDept2");
		for (Dept dept : allDept) {
			System.out.println(dept);
		}
		session.commit();
		session.close();
	}

  

  

4.查询的结果:

Dept [id=1, name=财务部, users=[User [id=2, name=李四, password=111], User [id=1, name=张三, password=123456]]]
Dept [id=2, name=后勤部, users=[User [id=3, name=五五, password=123]]]
Dept [id=3, name=经理部, users=[User [id=5, name=周七, password=12332], User [id=4, name=周六, password=122]]]

原文地址:https://www.cnblogs.com/gshao/p/10473866.html