zoukankan      html  css  js  c++  java
  • 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]]]

  • 相关阅读:
    java面试笔记
    ASP.NET MVC 第三章 异步提交数据
    ASP.NET MVC 第二章 路由和修改路由
    ASP.NET MVC 第一章 我们的第一个MVC例子
    本团队承接软件开发项目
    论面向服务的架构及其应用
    软件架构设计阅读笔记3
    学习15
    软件架构设计阅读笔记2
    学习13
  • 原文地址:https://www.cnblogs.com/gshao/p/10473866.html
Copyright © 2011-2022 走看看