mybatis面试题

  1. 什么是mybatis?
    a) mybatis是一个半ORM框架,内部封装了JDBC,开发时只需要关注SQL语句本身,编写原生态的sql语句,灵活度比较高
    b) mybatis通过xml文件或者注解的方式将各种statement进行配置,并通过Java对象和statement中的sql的动态参数映射生成最终的sql语句,最后由mybatis矿建执行sql并将记过映射为对象返回
  2. mybatis的优缺点?
    a) 优点
    i. 基于sql语句编程,可以重用sql语句,比较灵活;sql写在XML中,解除sql与程序代码的耦合;还支持动态sql语句
    ii. 消除JDBC大量冗余代码,不需要手动创建连接、关闭连接等
    iii. 支持对象与数据库进行关系映射;支持对象关系组件的维护
    b) 缺点:
    i. sql语句编写工作量比较大,对开发人员编写sql语句的能力有一定要求
    ii. sql依赖数据库,移植性差,不能随意更换数据库
  3. mybatis和hibernate有哪些不同?
    a) mybatis不是一个完全的ORM框架,需要手动编写sql语句;hibernate是一个ORM 框架,可以通过直接操作对象来操作数据库
    b) hibernate对象关系映射能力强,移植性好,适用于对关系模型要求高的项目,可以节省开发成本;mybatis需要自行管理映射关系
    c) hibernate自动生成sql语句,有些语句比较繁琐,会多消耗一些性能;mybatis手动编写sql,可以避免不必要的查询,提高系统性能
  4. #{}和${}的区别是什么?
    a) 是 字 符 串 替 换 , M y b a t i s 在 处 理 {}是字符串替换,Mybatis在处理 Mybatis{}时,就是把${}替换成变量的值
    b) #{}是预编译处理,Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;适用#{}可以防止sql注入
  5. 数据库中的字段名与对象中的属性名不同,如何处理?
    a) 给查询的字段取别名,别名与对象属性名对应即可
  6. 多表查询时,出现重复字段名,如何解决?
    a) 给重复的字段取别名,然后利用来映射别名和对象属性名
  7. mybatis模糊查询怎么写?利用contact拼接,只适用于mysql数据库(可以适用bind标签定义一个变量,这样oracle数据库也适用)
select * from t_user where uname like concat('%',#{uname},'%')
<bind name="str" value=" '%' + uname + '%' "></bind>
select * from t_user where uname like #{str}
  1. Mapper接口的工作原理?
    a) mapper接口的工作原理是JDK动态代理,mybatis运行时会通过动态代理为mapper接口产生一个代理对象,代理对象拦截接口方法,然后去执行MapperStatement对应的sql语句,最后返回sql的执行结果
  2. mapper接口可以重载函数吗?
    a) 不可以,因为mybatis是通过接口的全限名+方法名匹配对应的sql语句的
    b) 接口的全限名就是mapper.xml中的namespace;接口的方法名就是mapper.xml中MapperStatement(配置文件中的每一个、等标签都是一个statement)的id;接口方法的参数就是传给mapper.xml中sql的参数
  3. 物理分页和逻辑分页
    a) 物理分页:每次从数据库中取出需要的数据(访问数据库多次)
    b) 逻辑分页:从数据库中取出所有数据,再根据参数截取指定的数据(访问数据库一次)
  4. mybatis分页插件,分页插件的原理是什么?
    a) mybatis使用RowBounds进行分页,需要分页的时候传入一个RowBounds对象,sql语句只需要查询所有即可,mybatis的拦截器会根据RowBounds将数据进行分页操作
    RowBounds rowBounds = new RowBounds(offset, page.getPageSize()); // offset起始行 // limit是当前页显示多少条数据
public List<ProdProduct> findRecords(HashMap<String,Object> map,RowBounds rowBounds);

b) 此外,还可以自定义拦截器实现分页操作,适用于数据量大的情况
c) 分页插件的原理:使用mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截要执行的sql,然后重写sql(添加分页的参数),最后执行重写的sql
12. mybatis如何将sql的执行结果封装成对象返回?
a) 使用,设置对象属性名数据库表列名的映射关系
b) 使用sql取别名的功能,将别名与对象属性名一一对应
c) 数据库表与对象设置后映射关系后,mybatis通过反射创建对象,然后再通过反射给对象的属性赋值再返回(没有映射关系的无法赋值)
13. 如何获取自动生成的主键值?
a) insert默认返回一个int值,代表插入的行数
b) 当主键采用自增策略,可以通过设置usegeneratedkeys=”true”,这样插入时产生的主键会赋值到传入的对象中。也就是自增策略下,插入会将主键值赋值给传入的对象中
14. mapper中如何传入多个参数?
a) 直接使用#{0}、#{1}……获取参数(不需要指定参数类型)

<select id="selectUser" resultMap="BaseResultMap">  
    select * t_user where name = #{0} and area=#{1}  
</select>

b) 使用 @param 注解,再参数前面加入@param(“参数名”),然后在sql语句中就可以直接使用#{param中的参数名}获取参数值
c) 将数据封装到map或者list集合中
15. mybatis的动态sql作用?原理是什么?有哪些动态sql?
a) 作用:在xml文件中用标签的形式编写动态sql语句,解决手动拼接sql的麻烦
b) 原理:根据表达式的值,通过逻辑判断来完成sql的拼接
c) 常用动态sql标签
i. if标签,进行逻辑判断实现条件拼接

<select id="selectUserById" parameterType="Integer" resultType="User">
select * from t_user where 1=1
<if test="uid != null and uid != ''	">
	and uid = #{uid}
</if>
</select>

ii. where标签:使用该标签拼接条件后有多余的or/and,where标签会清除多余的or/and

<select id="selectUser" parameterType="User" resultType="User">
<where>
    <if test="username !=null and username !=''">
       and username like concat('%',#{username}, '%')
    </if>
    <if test="jobs !=null and jobs !=''">
       and jobs= #{jobs}
    </if>
      </where>
</select>

iii. foreach标签:相当于循环语句,可以用于遍历集合、批量删除

<select id="findCustomer" resultType="Customer">
select * from t_customer where id in
<foreach item="id" index="index" collection="list" open="(" separator="," close=")">
#{id}
</foreach>
</select>

foreach不同属性的作用,上面foreach的结果就是"(id1,id2,id3……)":
item:代表当前循环的变量,也就是会从遍历的集合中取出数据放到id中,在foreach内部直接使用#{id}引用即可
index:当前循环的变量在集合中的下标
collection:代表要遍历的集合(这里填写的是参数类型名的小写,例如List就是list,Map就是map,Array就是array,千万不要写参数名)
open:表示用什么符号与集合中的元素拼接,并拼接在最前面
separator:表示用什么符号隔开集合中的元素
close:表示用什么符号与集合中的元素拼接,并拼接在最后面
iv. set标签:用来动态拼接update时set更新字段,可以去除最后一个多余的逗号

<update id="updateCustomer"  parameterType="Customer">
        update t_customer 
        <set>
            <if test="username !=null and username !=''">
                  username=#{username},
            </if>
            <if test="jobs !=null and jobs !=''">
                  jobs=#{jobs},
            </if>
        </set>
        where id=#{id}
</update>

v. 、、标签,功能跟switch、case、default相似

<select id="findCustomer" resultType="Customer">
select * from t_customer where 1=1
      <choose>
           <when test="username !=null and username !=''">
                and username like concat('%',#{username}, '%')
           </when>
           <when test="jobs !=null and jobs !=''">
                and jobs= #{jobs}
           </when>
           <otherwise>
                and phone is not null
           </otherwise>
      </choose>
</select>

vi. bind标签:可以定义变量

  1. 例如你分页的时候需要计算起始值,mybatis不支持直接在sql语句中计算,此时你可以先利用bind标签定义一个变量起始值,然后再sql语句中引用它(其中currentPage、pageSize都是对象PageBean中的属性)
<select id="getPage" parameterType="PageBean" resultMap="CommentMap">
	<bind name="start" value="currentPage*pageSize"></bind>
	select * from t_comment 
	limit #{start},#{pageSize}
</select>
  1. 进行字符串的拼接,可以解决sql注入,且移植性好(常用数据库都适用),其中的username是Customer中的属性
<select id="findCustomer" parameterType=" Customer" resultType=Customer">
     <bind name="pattern_username" value="'%'+username+'%'" />
     select * from t_customer 
     where username like #{pattern_username}
</select>

vii. trim标签:通过配置可以是心啊set、where标签的功能’

  1. 实现where功能:prefix代表语句的前缀,prefixOverrides代表要去除的多余符号,可以是and或者or
<select id="findCustomer" parameterType=" Customer" resultType=Customer">
select * from t_customer
     <trim prefix="where" prefixOverrides="and">
        <if test="username !=null and username !=''">
             and username like concat('%',#{username}, '%')
        </if>
        <if test="jobs !=null and jobs !=''">
             and jobs= #{jobs}
         </if>
     </trim>
</select>
  1. 实现set功能:
<update id="updateCustomer" parameterType="Customer">
update t_customer
     <trim prefix="set" prefixOverrides=", ">
        <if test="username !=null and username !=''">
             username = #{username},
        </if>
        <if test="jobs !=null and jobs !=''">
             jobs = #{jobs},
         </if>
     </trim>
</update>
  1. 其他标签
    a) sql标签:可以将公共的sql语句抽取出来,成为一个常量,方便重复调用
<sql id="Column_List">
    id,username,age,hobby
</sql>

b) include标签:将sql定义的常量引用到sql语句中(通过refid属性引用上面定义的Column_List)

<select id="selectAll" resultMap="User">
    select
    <include refid="Column_List" />
    from t_user
</select>

c) resultMap标签:设置对象属性与数据库表字段的映射关系
i. resultMap的tyep属性表示这是哪个类的关系映射,id用于被引用时的标志
ii. 通常主键使用id标签设置映射,其他普通属性使用result标签(result标签也可以用于主键,不过不建议)
iii. resultMap内部标签的property属性都代表对象属性名;普通属性使用column映射数据库表的列名;自定义类对象使用javaType表示对象类型;自定义对象集合使用ofType表示对象类型
iv. 当对象中存在其他对象,例如下面的Blog对象有BlogType对象,这时需要使用association标签设置映射关系,内部标签跟resultMap内部标签遵循一样的规则
v. 当对象中含有自定义的对象集合时,例如下面的Blog对象有Comment集合,这时需要使用collection标签设置映射关系,内部标签跟resultMap内部标签遵循一样的规则

<resultMap type="Blog" id="BlogMap">
		<id property="id" column="id"/>
		<result property="title" column="title"/>
		<result property="content" column="content"/>
		<association property="blogType" javaType="BlogType">
			<id property="id" column="typeId"/>
			<result property="typename" column="typename"/>
		</association>
        <collection property="comment" ofType="Comment">
<id property="id" column="commentId"/>
			<result property="replyContent" column="reply_content"/>
</collection>
	</resultMap>
  1. 多表查询的两种方式:联合查询和嵌套查询
    a) 联合查询:只查询一次数据库,通过resultMap中的association或collection进行配置,使用javaType 和ofType配置映射关系属于联合查询,且需要在association或collection标签体内配置相应属性的映射关系(通过一条查询语句)
    BlogMapper.xml文件
<mapper namespace="com.blog.mapper.BlogMapper">
<resultMap type="Blog" id="BlogMap">
		<id property="bid" column="bid"/>
		<result property="title" column="title"/>
		<result property="content" column="content"/>
		<association property="blogType" javaType="BlogType">
			<id property="tid" column="tid"/>
			<result property="typename" column="typename"/>
		</association>
        <collection property="comment" ofType="Comment">
<id property="cid" column="cid"/>
			<result property="replyContent" column="reply_content"/>
</collection>
</resultMap>
<select id="selectAllBlog" resultMap="BlogMap">
  select * from t_blog a,t_blogtype b,t_comment c where a.tid=b.tid and a.cid=c.cid
</select>
</mapper>

b) 嵌套查询:先查询一个表,然后再利用这个表的外键去查询另一个表(通过多条查询语句,查询语句写在不同类对应的xml文件中),需要使用column进行关系映射,还需要用select属性配置另一条查询语句的id
BlogMapper.xml文件:

<mapper namespace="com.blog.mapper.BlogMapper">
<resultMap type="Blog" id="BlogMap">
		<id property="bid" column="bid"/>
		<result property="title" column="title"/>
		<result property="content" column="content"/>
		<association property="blogType" column="tid" select="BlogTypeMapper.selectBlogTypeById"></association>
        <collection property="comment" column="cid" select="CommentMapper.selectCommentById"></collection>
</resultMap>
<select id="selectAllBlog" resultMap="BlogMap">
  select * from t_blog
</select>
</mapper>
BlogTypeMapper.xml文件:
<mapper namespace="com.blog.mapper.BlogTypeMapper">
<select id="selectBlogTypeById" resultType="BlogType">
  select * from t_blogtype where tid=#{tid}
</select>
</mapper>
CommentMapper.xml文件:
<mapper namespace="com.blog.mapper.CommentMapper">
<select id="selectCommentById" resultType="Comment">
  select * from t_comment where cid=#{cid}
</select>
</mapper>
  1. mybatis是否支持延迟加载?延迟加载的原理是什么?
    a) 支持association关联对象和collection关联对象集合的延迟加载,可以配置lazyLoadingEnable="true"实现延迟加载
    b) 原理:mybatis通过cglib动态创建延迟加载对象的代理对象,当延迟加载对象调用自身属性方法时,代理对象会去数据库中查询数据并返回查询结果
  2. mybatis的一级缓存和二级缓存
    a) 一级缓存:基于PerpetualCache的HashMap本地缓存,存储作用域是Session,当session执行flush或close方法后,该session的缓存将清空,默认打开一级缓存
    b) 二级缓存:机制与一级缓存相同,不同的是二级缓存的作用域是整个mapper(即同一个命名空间内),默认不打开二级缓存,可以利用Ehcache自定义存储源,开启二级缓存的类需要实现Serializable序列化接口
    c) 缓存更新机制:当某个作用域(一级缓存Session/二级缓存namespace)执行C/U/D操作后,默认该作用域下的所有select的缓存将clear并重新更新,开启二级缓存则根据配置决定是否刷新
  3. mybatis使用接口开发需要注意一下四点:
    a) 接口中的方法名与对应的mapper.xml文件定义的sql的id一一对应
    b) 接口中方法的参数类型与对应的sql中的paramterType的类型相同
    c) 接口中方法的返回值与对应sql中resultType/resultMap相同
    d) 接口的全包名与对应mapper.xml文件的namespace相同
  4. 插件运行原理,以及如何自定义插件?
    a) 运行原理:mybatis可以编写针对Executor、StatementHandler、ParameterHandler、ResultSetHandler四个接口的插件,mybatis使用JDK的动态代理为需要拦截的接口生成代理对象,然后实现接口的拦截方法,所以当执行需要拦截的接口方法时,会进入拦截方法(AOP面向切面编程的思想)
    b) 自定义插件
    i. 编写Intercepror接口的实现类
    ii. 设置插件的签名,告诉mybatis拦截哪个对象的哪个方法
    iii. 最后将插件注册到全局配置文件中
    在全局配置文件中注册插件:
//插件签名,告诉mybatis当前插件拦截哪个对象的哪个方法,type表示要拦截的目标对象,method表示要拦截的方法,args表示要拦截方法的参数
@Intercepts({
	@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
})
public class MyFirstPlugin implements Interceptor {

	//拦截目标对象的目标方法执行
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		System.out.println("MyFirstPlugin拦截目标对象:"+invocation.getTarget()+"的目标方法:"+invocation.getMethod());
		//执行目标方法
		Object proceed = invocation.proceed();
		//返回执行后的返回值
		return proceed;
	}
	//包装目标对象:为目标对象创建代理对象
	@Override
	public Object plugin(Object target) {
		System.out.println("MyFirstPlugin为目标对象"+target+"创建代理对象");
		//this表示当前拦截器,target表示目标对象,wrap方法利用mybatis封装的方法为目标对象创建代理对象(没有拦截的对象会直接返回,不会创建代理对象)
		Object wrap = Plugin.wrap(target, this);
		return wrap;
	}
	//设置插件在配置文件中配置的参数值
	@Override
	public void setProperties(Properties properties) {
		System.out.println("MyFirstPlugin配置的参数:"+properties);
	}

}

在全局配置文件中注册插件

<plugins>
	<plugin interceptor="com.mybatis_demo.plugin.MyFirstPlugin">
		<property name="username" value="acodebird"/>
		<property name="password" value="123456"/>
	</plugin>
	<plugin interceptor="com.mybatis_demo.plugin.MySecondPlugin"></plugin>
	</plugins>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值