---恢復內容開始---
Mybatis知識點總結
1、#{}和${}的區別是什么?
答:#{}的使用場景:在表的sql映射文件中如下使用:
1 <mapper namespace="com.mybatis.test2.userMapper"> 3 <select id="insertUser" parameterType="_User"> 4 INSERT INTO users(name,age) values(#{name},#{age}) 5 </select> 6 </mapper>
#{}是sql的參數占位符,Mybatis會將sql中的#{}替換為?號,在sql執行前會使用PreparedStatement的參數設置方法,按序給sql的?號占位符設置參數值。
${}的使用場景:先在配置文件中定義好變量,再在conf.xml中如下使用:
1 <environments default="development"> 2 <environment id="development"> 3 <transactionManager type="JDBC" /> 4 <dataSource type="POOLED"> 5 <property name="driver" value="${jdbcDriver}" /> 6 <property name="url" value="${jdbcUrl}" /> 7 <property name="username" value="${username}" /> 8 <property name="password" value="${password}" /> 9 </dataSource> 10 </environment> 11 </environments>
${}是Properties文件中的變量占位符,它可以用於標簽屬性值和sql內部,屬於靜態文本替換,比如${jdcDriver}會被靜態替換為com.mysql.jdbc.Driver。
2、Xml映射文件中,除了常見的select|insert|updae|delete標簽之外,還有哪些標簽?
答:還有很多其他的標簽:<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>,
加上動態sql的9個標簽,trim|where|set|foreach|if|choose|when|otherwise|bind等,
其中<sql>為sql片段標簽,通過<include>標簽引入sql片段,<selectKey>為不支持自增的主鍵生成策略標簽。
3、最佳實踐中,通常一個Xml映射文件,都會寫一個Dao接口與之對應,請問,這個Dao接口的工作原理是什么?Dao接口里的方法,參數不同時,方法能重載嗎?
答:1 Dao接口,就是人們常說的Mapper接口,---這是在基於注解的方式才會創建此接口,接口內定義了增刪該查的抽象方法;
1 /** 2 * 接口: 通過注解的方法操作數據表的CRUD 3 * @version 2.2 2016/9/24 4 * @author WGS 5 * 6 */ 7 public interface UserMapper { 8 9 @Insert("insert into users(name,age) values(#{name},#{age})") 10 public int insertUser(User user); 11 12 @Delete("delete from users where id=#{id} ") 13 public int deleteUser(int id); 14 15 @Update("update users set name=#{name},age=#{age} where id=#{id}") 16 public int updateUser(User user); 17 18 @Select("select * from users where id=#{id}") 19 public User getUserById(int id); 20 21 @Select("select * from users") 22 public List<User> getAllUsers(); 23 }
接口的全限名,就是映射文件中的namespace的值:
1 <mapper namespace="com.mybatis.test3.orderMapper"> 2 <select id="selectUser" parameterType="int" resultType="Order"> 3 select * from users where id=#{id} 4 </select> 5 </mapper>
接口的方法名(getUserById),就是映射文件中MappedStatement 的id值(selectUser),接口方法內的參數,就是傳遞給sql的參數(#{id} >>> #{id})。
Mapper接口是沒有實現類的,當調用接口方法時,接口全限名+方法名拼接字符串作為key值,可唯一定位一個MappedStatement,舉例:com.mybatis.test2.userMapper.insertUser,可以唯一找到namespace為 com.mybatis.test2.userMapper 下面 id =insertUser 的 MappedStatement。
在Mybatis中,每一個<select>、<insert>、<update>、<delete>標簽,都會被解析為一個MappedStatement對象。
2 Dao接口里的方法,是不能重載的,因為是全限名+方法名的保存和尋找策略。
3 Dao接口的工作原理是JDK動態代理,Mybatis運行時會使用JDK動態代理為Dao接口生成代理proxy對象,代理對象proxy會攔截接口方法,轉而執行MappedStatement所代表的sql,然后將sql執行結果返回。
4、Mybatis動態sql是做什么的?都有哪些動態sql?能簡述一下動態sql的執行原理不?
答:Mybatis動態sql可以讓我們在Xml映射文件內,以標簽的形式編寫動態sql,完成邏輯判斷和動態拼接sql的功能,Mybatis提供了9種動態sql標簽 trim|where|set|foreach|if|choose|when|otherwise|bind。
1 <!-- 2 5 模糊查詢:根據條件查詢用戶(姓名模糊匹配,年齡在指定的最小值和最大值之間) 3 SQL:select * from d_user where name like '%o%' and age>=13 and age<=18 4 --> 5 <!--定義操作 classes 表的sql 映射文件:userMapper2.xml --> 6 <mapper namespace="com.mybatis.test6.userMapper2"> 7 8 <select id="getUser" parameterType="User" resultType="User"> 11 select * from d_user where age between #{minAge} and #{maxAge} 12 13 <!--動態sql語句:當輸入的name!=null 時就執行下面的語句 --> 14 <if test=' name != "%null%" '> 15 and name like #{name} 16 </if> 17 18 </select> 19 20 </mapper>
其執行原理為,使用OGNL從sql參數對象中計算表達式的值,根據表達式的值動態拼接sql,以此來完成動態sql的功能。
5、Mybatis是如何解決sql字段名與實體類名不一致導致的沖突?
答:有兩種方式:
1 在sql語句中定義別名:
2 通過<resultSet>標簽:
1 <!-- 2 解決字段名和實體類屬性名不相同的沖突 3 --> 4 <!--定義操作 users 表的sql 映射文件:orderMapper.xml --> 5 <mapper namespace="com.mybatis.test3.orderMapper"> 6 <!-- 方式一:通過在sql語句中定義別名 --> 7 <!-- <select id="selectOrder" parameterType="int" resultType="Order"> 8 select order_id id,order_no orderNo,order_price price from orders where order_id=#{id} 9 </select> --> 10 11 <!-- 方式二: 通過resultMap --> 12 <select id="selectOrderResultMap" parameterType="int" resultMap="orderResultMap"> 13 select * from orders where order_id = #{id} 14 </select> 15 16 <resultMap type="Order" id="orderResultMap"> 17 <id property="id" column="order_id"/> 18 <result property="orderNo" column="order_no"/> 19 <result property="price" column="order_price"/> 20 </resultMap> 21 </mapper>
6、Mybatis能執行一對一、一對多的關聯查詢嗎?都有哪些實現方式,以及它們之間的區別。
selectOne
一對一:association
1 <!-- 2 4、關聯表查詢:一對一關聯 3 如何根據id查詢班級信息(包括老師信息) 4 Class封裝了Teacher屬性,即是相互關聯的 5 --> 6 <!--定義操作 users 表的sql 映射文件:orderMapper.xml --> 7 <mapper namespace="com.mybatis.test4.classesMapper"> 8 <!-- 9 方式一:嵌套結果 10 使用嵌套結果映射來處理重復的聯合結果的子集來封裝聯表查詢的數據(去除重復的數據) 11 --> 12 <select id="getClassInfo" parameterType="int" resultMap="ClassResultMap"> 13 select * from class c,teacher t where c.teacher_id = t.t_id and c.c_id = #{id} 14 </select> 15 <!-- 解決字段名屬性名不一致問題 --> 16 <resultMap type="Classes" id="ClassResultMap"> 17 <id property="id" column="c_id"/> 18 <result property="name" column="c_name"/> 19 <!-- 一對一關聯查詢 --> 20 <association property="teacher" column="teacher_id" javaType="Teacher"> 21 <id property="id" column="t_id"/> 22 <result property="name" column="t_name"/> 23 </association> 24 </resultMap> 25 26 <!-- 27 方式二:嵌套查詢 28 通過執行另外一個SQL映射語句來返回預期的復雜類型: 29 SELECT * FROM class WHERE c_id =1; 30 SELECT * FROM teacher WHERE t_id =1;//1 是上一個查詢得到的teacher_id的值 31 --> 32 <select id="getClassInfo2" parameterType="int" resultMap="ClassResultMap2" > 33 select * from class where c_id = #{id} 34 </select> 35 36 <!-- 解決字段名屬性名不一致問題 --> 37 <resultMap type="Classes" id="ClassResultMap2"> 38 <id property="id" column="c_id"/> 39 <result property="name" column="c_name"/> 40 <association property="teacher" column="teacher_id" 41 javaType="Teacher" select="getTeacher"> 42 </association> 43 </resultMap> 44 45 <select id="getTeacher" parameterType="int" resultType="Teacher"> 46 select t_id id,t_name name from teacher where t_id=#{id} 47 </select> 48 49 </mapper>
一對多:collection
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 3 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 4 <!-- 5 4、關聯表查詢:一對多關聯 6 如何根據class_id查詢班級信息(包括老師和學生信息),學生信息為集合List 7 Class封裝了Teacher和學生屬性,即一張class表中包含teacher表和student表 8 --> 9 <!--定義操作 classes 表的sql 映射文件:classesMapper.xml --> 10 <mapper namespace="com.mybatis.test5.classesMapper2"> 11 <!-- 12 方式一:嵌套結果 13 使用嵌套結果映射來處理重復的聯合結果的子集 14 SELECT * FROM class c, teacher t,student s 15 WHERE c.teacher_id=t.t_id AND c.C_id=s.class_id AND c.c_id=1 16 --> 17 <select id="getClassInfo3" parameterType="int" 18 resultMap="getClassMap"> 19 SELECT * FROM class c, teacher t,student s 20 WHERE c.teacher_id=t.t_id AND c.C_id=s.class_id AND c.c_id=#{id} 21 </select> 22 <!-- 解決字段名和屬性不一致沖突 --> 23 <resultMap type="Classes" id="getClassMap"> 24 <id property="id" column="c_id"/> 25 <result property="name" column="c_name"/> 26 27 <!-- 關聯的教師信息 --> 28 <association property="teacher" column="teacher_id" 29 javaType="Teacher"> 30 <id property="id" column="t_id"/> 31 <result property="name" column="t_name"/> 32 </association> 33 <!-- 關聯的學生信息,是集合 --> 34 <collection property="students" 35 ofType="Student"> 36 <id property="id" column="s_id"/> 37 <result property="name" column="s_name"/> 38 </collection> 39 </resultMap> 40 41 <!-- 42 方式二:嵌套查詢 43 通過執行另外一個SQL映射語句來返回預期的復雜類型 44 SELECT * FROM class WHERE c_id =1 //查詢后獲取到teacher_id,c_id值,傳入下兩條語句 45 SELECT * FROM teacher WHERE t_id =1 //t_id=1 是上條查詢得到的teacher_id值 46 SELECT * FROM student WHERE class_id =1 //c_id = 1 是上條查詢得到的c_id值 47 --> 48 <select id="getClassInfo4" parameterType="int" resultMap="getClassMap2"> 49 SELECT * FROM class WHERE c_id =#{id} 50 </select> 51 52 <resultMap type="Classes" id="getClassMap2"> 53 <id property="id" column="c_id"/> 54 <result property="name" column="c_name"/> 55 <association property="teacher" column="teacher_id" javaType="Teacher" 56 select="getTeacher"></association> 57 <collection property="students" column="c_id" ofType="Student" 58 select="getStudent"></collection> 59 </resultMap> 60 61 <select id="getTeacher" resultType="Teacher"> 62 SELECT t_id id,t_name name FROM teacher WHERE t_id =#{id} 63 </select> 64 65 <select id="getStudent" resultType="Student"> 66 SELECT s_id id,s_name name FROM student WHERE class_id =#{id} 67 </select> 68 </mapper>
7、Mybatis是否支持延遲加載?如果支持,它的實現原理是什么?
答:Mybatis僅支持association關聯對象和collection關聯集合對象的延遲加載,association指的就是一對一,collection指的就是一對多查詢。
在Mybatis配置文件中,可以配置是否啟用延遲加載lazyLoadingEnabled=true|false。
它的原理是,使用CGLIB創建目標對象的代理對象,當調用目標方法時,進入攔截器方法,比如調用a.getB().getName(),攔截器invoke()方法發現a.getB()是null值,
那么就會單獨發送事先保存好的查詢關聯B對象的sql,把B查詢上來,然后調用a.setB(b),於是a的對象b屬性就有值了,接着完成a.getB().getName()方法的調用。這就是延遲加載的基本原理。
當然了,不光是Mybatis,幾乎所有的包括Hibernate,支持延遲加載的原理都是一樣的。
8、為什么說Mybatis是半自動ORM映射工具?它與全自動的區別在哪里?
答:Hibernate屬於全自動ORM映射工具,使用Hibernate查詢關聯對象或者關聯集合對象時,可以根據對象關系模型直接獲取,所以它是全自動的。
而Mybatis在查詢關聯對象或關聯集合對象時,需要手動編寫sql來完成,所以,稱之為半自動ORM映射工具。
4、Mybatis是如何進行分頁的?分頁插件的原理是什么?
注:我出的。
答:Mybatis使用RowBounds對象進行分頁,它是針對ResultSet結果集執行的內存分頁,而非物理分頁,可以在sql內直接書寫帶有物理分頁的參數來完成物理分頁功能,也可以使用分頁插件來完成物理分頁。
分頁插件的基本原理是使用Mybatis提供的插件接口,實現自定義插件,在插件的攔截方法內攔截待執行的sql,然后重寫sql,根據dialect方言,添加對應的物理分頁語句和物理分頁參數。
舉例:select * from student,攔截sql后重寫為:select t.* from (select * from student)t limit 0,10
5、簡述Mybatis的插件運行原理,以及如何編寫一個插件。
注:我出的。
答:Mybatis僅可以編寫針對ParameterHandler、ResultSetHandler、StatementHandler、Executor這4種接口的插件,Mybatis使用JDK的動態代理,為需要攔截的接口生成代理對象以實現接口方法攔截功能,每當執行這4種接口對象的方法時,就會進入攔截方法,具體就是InvocationHandler的invoke()方法,當然,只會攔截那些你指定需要攔截的方法。
實現Mybatis的Interceptor接口並復寫intercept()方法,然后在給插件編寫注解,指定要攔截哪一個接口的哪些方法即可,記住,別忘了在配置文件中配置你編寫的插件。
6、Mybatis執行批量插入,能返回數據庫主鍵列表嗎?
注:我出的。
答:能,JDBC都能,Mybatis當然也能。
11、Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重復?
注:我出的。
答:不同的Xml映射文件,如果配置了namespace,那么id可以重復;如果沒有配置namespace,那么id不能重復;畢竟namespace不是必須的,只是最佳實踐而已。
原因就是namespace+id是作為Map<String, MappedStatement>的key使用的,如果沒有namespace,就剩下id,那么,id重復會導致數據互相覆蓋。有了namespace,自然id就可以重復,namespace不同,namespace+id自然也就不同。
12、Mybatis中如何執行批處理?
注:我出的。
答:使用BatchExecutor完成批處理。
13、Mybatis都有哪些Executor執行器?它們之間的區別是什么?
注:我出的
答:Mybatis有三種基本的Executor執行器,SimpleExecutor、ReuseExecutor、BatchExecutor。
SimpleExecutor:每執行一次update或select,就開啟一個Statement對象,用完立刻關閉Statement對象。
ReuseExecutor:執行update或select,以sql作為key查找Statement對象,存在就使用,不存在就創建,用完后,不關閉Statement對象,而是放置於Map<String, Statement>內,供下一次使用。簡言之,就是重復使用Statement對象。
BatchExecutor:執行update(沒有select,JDBC批處理不支持select),將所有sql都添加到批處理中(addBatch()),等待統一執行(executeBatch()),它緩存了多個Statement對象,每個Statement對象都是addBatch()完畢后,等待逐一執行executeBatch()批處理。與JDBC批處理相同。
作用范圍:Executor的這些特點,都嚴格限制在SqlSession生命周期范圍內。
14、Mybatis中如何指定使用哪一種Executor執行器?
注:我出的
答:在Mybatis配置文件中,可以指定默認的ExecutorType執行器類型,也可以手動給DefaultSqlSessionFactory的創建SqlSession的方法傳遞ExecutorType類型參數。
15、Mybatis是否可以映射Enum枚舉類?
注:我出的
答:Mybatis可以映射枚舉類,不單可以映射枚舉類,Mybatis可以映射任何對象到表的一列上。映射方式為自定義一個TypeHandler,實現TypeHandler的setParameter()和getResult()接口方法。TypeHandler有兩個作用,一是完成從javaType至jdbcType的轉換,二是完成jdbcType至javaType的轉換,體現為setParameter()和getResult()兩個方法,分別代表設置sql問號占位符參數和獲取列查詢結果。
16、Mybatis映射文件中,如果A標簽通過include引用了B標簽的內容,請問,B標簽能否定義在A標簽的后面,還是說必須定義在A標簽的前面?
注:我出的
答:雖然Mybatis解析Xml映射文件是按照順序解析的,但是,被引用的B標簽依然可以定義在任何地方,Mybatis都可以正確識別。
原理是,Mybatis解析A標簽,發現A標簽引用了B標簽,但是B標簽尚未解析到,尚不存在,此時,Mybatis會將A標簽標記為未解析狀態,然后繼續解析余下的標簽,包含B標簽,待所有標簽解析完畢,Mybatis會重新解析那些被標記為未解析的標簽,此時再解析A標簽時,B標簽已經存在,A標簽也就可以正常解析完成了。
---恢復內容結束---
