java【MyBatis】面试题
一、MyBatis编程步骤。
1.创建SqlSessionFactory对象。
2.通过SqlSessionFactory获取SqlSession对象。
3.通过SqlSession获得Mapper代理对象。
4.通过Mapper代理对象,执行数据库操作。
5.执行成功,则使用SqlSession提交事务。
6.执行失败,则使用SqlSession回滚事务。
7.最终,关闭回话。
二、#{}和${}的区别是什么?
${}是Proerties文件中的变量占位符,它可以用于XML标签属性值和SQL内部,属于字符串替换。例如将${driver}会被静态替换为com.mysql.jdbc.Driver:
<dataSource type="UNPOOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
</dataSource>
${}也可以对传递进来的参数原样拼接在SQL中。代码如下:
<select id="getSubject3" parameterType="Integer" resultType="Subject">
SELECT * FROM subject WHERE id = ${id}
</select>
- 实际场景下,不推荐这么做。因为,可能有SQL注入的风险。
#{}是SQL的参数占位符,Mybatis会将SQL中的#{}替换为?号,在SQL执行前会使用。PreparedStatement的参数设置方法,按序给SQL的?号占位符设置参数值,比如ps.setInt(0,parametervalue).所以,#{}是预编译处理,可以有效防止SQL注入提高系统安全性处理。
另外,#{}和${}的取值方式非常方便。例如:#{item.name}的取值方法,为使用反射从参数对象中,获取item对象的name属性值,相当于param.getItem().getName()。
三、当实体类中的属性名和表中的字段名不一样,怎么办?
第一种,通过在查询的SQL语句中定义字段名和别名,让字段名的别名和实体类的属性名一致。代码如下:
<select id="selectOrder" parameterType="Integer" resultType="Order">
SELECT order_id AS id, order_no AS orderno, order_price AS price FROM orders WHERE order_id = #{id}
</select>
这里,还有几点建议:
- 1.数据库的关键字,统一使用大写,例如:SELECT、AS、FROM、WHERE
- 2.每5个查询字段换一行,保持整齐。
- 3.,的后面,和=的前后,需要有空格,更加清晰。
- 4.SELECT、FROM、WHERE等,单独一行,高端大气。
第二种,是第一种的特殊情况,大多数场景下,数据库字段名和实体类中的属性名,主要是前者为下划线风格,后者为驼峰风格。在这种情况下,可以直接配置如下,实现自动的下划线转驼峰的功能。
<setting name="logImpl" value="LOG4J"/>
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
也就说,约定大于配置,非常推荐!
第三种,通过<resultMap>来映射字段名和实体类属性名的--对应的关系,代码如下:
<resultMap type="me.gacl.domain.Order" id=”OrderResultMap”>
<!–- 用 id 属性来映射主键字段 -–> <id property="id" column="order_id">
<!–- 用 result 属性来映射非主键字段,property 为实体类属性名,column 为数据表中的属性 -–>
<result property="orderNo" column ="order_no" />
<result property="price" column="order_price" />
</resultMap>
<select id="getOrder" parameterType="Integer" resultMap="OrderResultMap">
SELECT * FROM orders WHERE order_id = #{id}
</select>
- 此处SELECT * 仅仅作为示例只用,实际场景下,千万千万不要这么干。用多少字段,查询多少字段。
- 相比第一种,第三种的重用性会一些。
四、XML映射文件中,除了常见的select|insert|update|delete标签之外,还有那些标签?
如下部分,可见
- <cache/>标签,给定命名空间的缓存配置。
- <cache-ref/>标签,其他命名空间缓存配置的引用。
- <resultMap>标签,是最负责也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
- <sql/>标签,引用<sql/>标签的语句。
- <selectKey/>标签,不支持自增的主键生成策略标签。
- <if/>
- <choose/>、<when/>、<otherwise/>
- <trim/>、<where/>、<set/>
- <foreach/>
- <bind/>
五、Mybatis动态SQL是做什么的?都有哪些动态SQL?能简述一下动态SQL的执行原理么?
- Mybatis动态SQL,可以让我们在XML映射文件内,以XML标签的形式编写动态sql,完成逻辑判断和动态拼接SQL的功能。
- Mybatis提供了9种动态SQL标签:<if/>、<choose/>、<when/>、<otherwise/>、<trim/>、<where/>、<set/>、<foreach/>、<bind/>。
- 其执行原理为:使用OGNL的表达式,从SQL参数对象中计算表达式的值,根据表达式的值动态拼接SQL,以此完成动态SQL的功能。
六、通常一个XML映射文件,都会写一个Mappe接口与之对应。请问,这个Mapper接口的工作原理是什么?Mapper接口里的方法,参数不同时,方法能重载吗?
Mapper接口,对应的关系如下:
- 接口的全限名,就是映射文件中的“namespace”的值。
- 接口的方法名,就是映射文件中MapperStatement的“id”值。
- 接口方法内的参数,就是传递给SQL的参数。
Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个对应的MapperStatement。举例:com.mybatis3.mappers.findStudentById,可以唯一找到“namespace”为 com.mybatis3.mappers.StudentDao 下面 "id" 为 findStudentById 的MappedStatement 。
总结来说,在Mybatis中,每一个<select/>、<insert/>、<update/>、<delete/>标签,都会被解析为一个MapperStatement对象。
另外,Mapper接口的实现类,通过Mybatis使用JDK Proxy自动生成其代理对象Proxy,而代理对象Proxy会拦截接口方法,从而“调用”对应的MappedStatement方法,最终执行SQL,返回执行结果,整体流程如下:
其中,SqlSession在调用Executor之前,会获得对应的MappedStatement方法。例如:
// DefaultSqlSession.java @Override public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { // 获得 MappedStatement 对象 MappedStatement ms = configuration.getMappedStatement(statement); // 执行查询 executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
注意:mapper接口里的方法,是不能进行重载的,因为时全限名+方法名的保存和寻找策略。所以有时,想个Mapper接口里的方法名,还是蛮闹心的。
七、Mapper接口绑定有几种实现方式,分别是怎么实现的?
接口绑定有三种实现方式:
- 第一种,通过XML Mapper里面写SQL来绑定。在这种情况下,要指定XML映射文件里面的“namespace”必须为接口的全路径名。
- 第二种,通过注解绑定,就是在接口的方法上面加上@select、@update、@Insert、@Delete注解,里面包含SQL语句来绑定。
- 第三种,是第二种的特例,也是通过注解绑定,在接口的方法上面加上@SelectProvider、@UpdateProvider、@InsertProvider、@DeleteProvider注解,通过java代码,生成对应的动态SQL。
实际场景下,最最最推荐的第一种方式。因为SQL通过注解写在java代码中,会非常杂乱,而写在XML中,更加有整体性,并且可以更加方便的使用OGNL表达式。
八、Mybatis的XML Mapper文件中,不同的XML映射文件,id是否可以重复?
不同的XML Mapper文件,如果配置了“namespace”,那么id可以重复;如果没有配置“namespace”,那么id不能重复,毕竟“namespace”不是必须的,知识最佳实践而已。
原因就是:namespace+id是作为Map<String,MappedStatement>的key使用的。如果没有“namespace”,就剩下id,那么id重复会导致数据互相覆盖。如果有了“namespace”,自然id就可以重复,“namespace”不同,namespace+id自然也就不同。
九、如何获取自动生成的(主)键值?
不同的数据库,获取自动生成 的(主)键值的方式不同。
Mysql有两种方式,但是自增主键,代码如下:
//方式一,使用 useGeneratedKeys + keyProperty 属性 <insert id="insert" parameterType="Person" useGeneratedKeys="true" keyProperty="id"> INSERT INTO person(name, pswd) VALUE (#{name}, #{pswd}) </insert>
// 方式二,使用 `<selectKey />` 标签 <insert id="insert" parameterType="Person" useGeneratedKeys="true" keyProperty="id"> <selectKey keyProperty="id" resultType="long" order="AFTER"> SELECT LAST_INSERT_ID() </selectKey> INSERT INTO person(name, pswd) VALUE (#{name}, #{pswd}) </insert>
- 其中,方式一较为常用。
// 这个是创建表的自增序列
CREATE SEQUENCE student_sequence
INCREMENT BY 1
NOMAXVALUE NOCYCLE
CACHE 10;
<insert id="add" parameterType="Student"> <selectKey keyProperty="student_id" resultType="int" order="BEFORE"> select student_sequence.nextval FROM dual </selectKey> INSERT INTO student(student_id, student_name, student_age) VALUES (#{student_id},#{student_name},#{student_age}) </insert>
// 方式二,使用 `<selectKey />` 标签 + AFTER <insert id="save" parameterType="com.threeti.to.ZoneTO" > <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" > SELECT SEQ_ZONE.CURRVAL AS id FROM dual </selectKey> INSERT INTO TBL_ZONE (ID, NAME ) VALUES (SEQ_ZONE.NEXTVAL, #{name,jdbcType=VARCHAR}) </insert>
他们使用第一种方式,因为使用触发器,在数据更改就会有问题:就改线上某几条数据时候,需要手动操作的时候。
十、Mybatis执行批量插入,能返回数据库主键列表么?
能,JDBC能做,Mybatis当然也可以做。
十一、在Mapper中如何传递多个参数?
第一种。使用Map集合,装在多个参数进行传递。
// 调用方法 Map<String, Object> map = new HashMap(); map.put("start", start); map.put("end", end); return studentMapper.selectStudents(map); // Mapper 接口 List<Student> selectStudents(Map<String, Object> map); // Mapper XML 代码 <select id="selectStudents" parameterType="Map" resultType="Student"> SELECT * FROM students LIMIT #{start}, #{end} </select>
第二种:保持传递多个参数,使用@Param注解。代码如下:
// 调用方法 return studentMapper.selectStudents(0, 10); // Mapper 接口 List<Student> selectStudents(@Param("start") Integer start, @Param("end") Integer end); // Mapper XML 代码 <select id="selectStudents" resultType="Student"> SELECT * FROM students LIMIT #{start}, #{end} </select>
第三种,保持传递多个参数,不实用@Param注解。
// 调用方法 return studentMapper.selectStudents(0, 10); // Mapper 接口 List<Student> selectStudents(Integer start, Integer end); // Mapper XML 代码 <select id="selectStudents" resultType="Student"> SELECT * FROM students LIMIT #{param1}, #{param2} </select>
其中,按照参数在方法中的位置,从1开始,逐个为#{param1}、#{param2}、#{param3}不断向下。
十二、Mybatis是否可以映射Enum枚举类?
Mybaits可以映射枚举类,对应的实现类为EnumTypeHandler或者EnumOrdinalTypeHandler
- EnumTypeHandler,基于Enum.name属性(String)。默认
- EnumOrdinnalTypeHandler,基于Enum.ordinal属性(int)。可通过<setting name="defaultEnumTypeHandler" value="EnumOrdinalTypeHandler"/>来设置。
当然,实际开发场景,我们很少使用Enum类型,更好的方式是常量。
public class Dog { public static final int STATUS_GOOD = 1; public static final int STATUS_BETTER = 2; public static final int STATUS_BEST = 3; private int status; }
并且,不但可以映射枚举,Mybatis可以映射任何对象到表的一列上。映射方式为自定义一个TypeHandler类,实现TypeHandler的#setParameter(...)和#getResult(...)接口方法。
TypeHandler有两个作用:
- 一个是,完成从javaType到jdbcType的转换。
- 二是,完成jdbcType到javaType的转换。
具体体现为#setParameter(...)和#getResult(...)两个方法,分别代表设置SQl问好占位符参数和获取列查询结果。
十三、Mybatis都有哪些Excutor执行器?它们之间的区别是什么?
Mybatis有四种Excutor执行器,分别是SimpleExcutor、ReuseExcutor、BatchExcutor、CachingExcutor。
- SimpleExcutor:每执行一次update或者select操作,就创建一个statement对象,用完立刻关闭statement对象。
- ReuseExcutor:执行update或者select操作,以SQL作为key查找缓存的statement对象,存在就使用,不存在就创建;用完后,不关闭statement对象,而是放置于缓存Map<String,Statement>内,供下一次使用,简而言之,就是重复使用statement对象。
- BatchExcutor:执行update操作(没有select操作,因为JDBC批量处理不支持select操作),将所有SQL都添加到批处理中(通过addBatch方法)。等待统一执行(使用executeBatch方法)。它缓存了多个Statement对象,每个statement对象都调用addBatch方法完毕后,等待一次执行executeBatch批量处理,实际上,整个过程与JDBC批量处理是相同的。
- CachingExecutor:在上述的三个执行器上,增加二级缓存的功能。
通过设置<setting name="defaultExecutorType" value="">的value属性,可传入SIMPLE、REUSE、BATCH三个值,分别使用SimpleExecutor、ReuseExecutor、BatchExecutor执行器。
通过设置<Setting nam=“cacheEnable” value=“”>的value属性为true时,创建CacheExecutor执行器。
十四、MyBatis 如何执行批量插入?
首先,在mapper XML编写一个简单的Insert语句,代码如下:
<insert id="insertUser" parameterType="String">
INSERT INTO users(name) VALUES (#{value})
</insert>
然后,在对应的mapper接口中,编写映射方法。
public interface UserMapper {
void insertUser(@Param("name") String name);
}
最后,调用该mapper接口方法,代码如下:
private static SqlSessionFactory sqlSessionFactory; @Test public void testBatch() { // 创建要插入的用户的名字的数组 List<String> names = new ArrayList<>(); names.add("占小狼"); names.add("朱小厮"); names.add("徐妈"); names.add("飞哥"); // 获得执行器类型为 Batch 的 SqlSession 对象,并且 autoCommit = false ,禁止事务自动提交 try ( SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, false)) { // 获得 Mapper 对象 UserMapper mapper = session.getMapper(UserMapper.class); // 循环插入 for (String name : names) { mapper.insertUser(name); } // 提交批量操作 session.commit(); } }
INSERT INTO [表名]([列名],[列名])
VALUES ([列值],[列值])), ([列值],[列值])), ([列值],[列值]));
- 对于这种方式,需要保证单条SQL不超过语句的最大限制max_allowed_packet大小,默认为1M
十五、介绍Mybatis的一级缓存和二级缓存的概念和实现原理?
十六、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
Mybatis仅支持association关联对象和collection关联集合对象的延迟加载。其中association指的是一对一,collection指的是一对多查询。
在Mybatis配置文件中,可以配置<setting name=“lazyLoadingEnable” value=“true”>来开启延迟加载的功能。默认情况下,延迟加载的功能是关闭的。
它的原理是,使用CGLIB或者javassist(默认)创建目标的代理对象,当调用代理对象的延迟加载属性的getting方法时,进入拦截器方法。比如调用a.getB().getName()方法,进入拦截器invoke(...)方法,发现a.getB()方法需要延迟加载时,那么就会单独发送事先保存好的关联B对象的SQL,把B查询上来,然后a.getB(b)方法,于是a对象b属性就有数据了,接着完成a.getB().getName()方法的调用,这就是延迟加载的基本原理。
当然了,不光是Mybatis,几乎所有的包括Hibernate在内,支持延迟加载的原理都是一样的。
十七、Mybatis能否执行一对一、一对多的关联查询吗?都有哪些实现方式,以及他们之间的区别?
能,Mybatis不仅可以执行一对一,一对多的查询,还可以执行多对一,多对多的关联查询。
- 多对一查询,其实就是一对一查询,只需要把selectOne(。。。)修改为selectList(。。)即可。
https://www.cnblogs.com/qingmuchuanqi48/p/13800057.html就是案例
- 多对多查询,其实即使一对多查询,只需要把selectOne(。。。)修改为selectList(。。。)即可
本文来自博客园,作者:King-DA,转载请注明原文链接:https://www.cnblogs.com/qingmuchuanqi48/articles/13812573.html