MyBatis
提供了XML配置和註解配置兩種方式。今天就來搞搞這兩種方式是如何實現的。
的真正強大在於它的語句映射,這是它的魔力所在。由於它的異常強大,映射器的 XML 文件就顯得相對簡單。如果拿它跟具有相同功能的JDBC 代碼進行對比,你會立即發現省掉了將近 95% 的代碼。 致力於減少使用成本,讓用戶能更專注於 SQL 代碼。
來自官網。
Mybatis
映射九個頂級元素:
mapper:映射文件的根節點,只有一個屬性namespace(命名空間),作用如下:用於區分不同的mapper,全局唯一。綁定DAO接口,即面向接口編程,當綁定一個接口,就不用寫此接口的實現類,會通過接口的完全限定名找到對應的mapper配置來執行語句,所以,namespace的命名必須要寫接口的完全限定名。cache:配置給定命名空間的緩存。cache-ref:從其他命名空間引用緩存配置。resultMap:用來描述資料庫結果集和對象的對應關係。sql:可以重用的塊,也可以被其他語句引用。通常時存放一些公用性的。insert:映射插入語句。update:更新映射語句。delete:刪除映射語句。select:映射查詢語句。
xml方式
九個頂級映射元素對應標籤:
<mapper namespace="com.tian.mybatis.mapper.UserMapper"> <resultMap id="" type=""></resultMap> <sql id=""></sql> <cache blocking="" ></cache> <cache-ref namespace=""></cache-ref> <select id="selectUserById"></select> <insert id="insert" ></insert> <update id=""></update> <delete id=""></delete></mapper>
select詳解
可以看得出,後面可選項還是蠻多的。下面是官網對每項的解釋。
select使用案例
<?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"><mapper namespace="com.tian.mybatis.mapper.UserMapper"> <select id="selectUserById" resultType="com.tian.mybatis.entity.User" parameterType="int" > select * from m_user where id = #{id} </select></mapper>
id必須在這個Mapper中是唯一的,可以被用來引用這條語句 ,這個id必須與只對應的是XxxMapper.java中的方法,必須是一一對應。返回類型:User類型,resultType:查詢語句返回結果類型的完全限定名或別名。別名使用方式和parameterType是一樣的。參數:整形,表示查詢語句傳入參數的類型和完全限定名或別名。支持基礎數據類型和複雜數據類型。#{參數名}:告訴
生成的
PreparedStatement
參數,相對於
JDBC中
,改參數被標識為『?』。
別名與參數映射類型如下:
返回類型中別名的使用,注意:
如果是我們的entity類,那麼
是無法使用別名的,只能使用
才可以使用別名。
<?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"><mapper namespace="com.tian.mybatis.mapper.UserMapper"> <resultMap id="User" type="com.tian.mybatis.entity.User"/> <select id="selectUserById" resultMap="User" parameterType="int" > select * from m_user where id = #{id} </select></mapper>
但是如果使用的上面映射表裡,也可以直接使用別名。
資料庫裡有兩條數據:
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"><mapper namespace="com.tian.mybatis.mapper.UserMapper"> <select id="countUser" resultType="int"> select count(1) from m_user </select></mapper>
UserMapper.java
import com.tian.mybatis.entity.User;public interface UserMapper { int countUser();}
測試類:
public class MybatisApplication { public static final String URL = "jdbc:mysql://localhost.com:3306/mblog?useUnicode=true"; public static final String USER = "root"; public static final String PASSWORD = "123456"; public static void main(String[] args) { String resource = "mybatis-config.xml"; InputStream inputStream = null; SqlSession sqlSession = null; try { inputStream = Resources.getResourceAsStream(resource); //工廠模式 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //獲取sql操作會話 sqlSession = sqlSessionFactory.openSession(); //構造對象(這裡比較特殊,這裡構造對象的方式後面會專門分享) UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //查詢統計 System.out.println(userMapper.countUser()); } catch (Exception e) { e.printStackTrace(); } finally { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } sqlSession.close(); } }}
輸出:2
當資料庫表中的欄位名和我們entity中的欄位名不一致,怎麼處理?
在實際開發中,這種常見是在所難免。我們可以使用下面的這種方式解決。
實體類User
public class User { private Integer id; private String userName; private Integer age; //set get toString方法這裡就不貼了}
文件內容:
<?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"><mapper namespace="com.tian.mybatis.mapper.UserMapper"> <resultMap id="User" type="com.tian.mybatis.entity.User"> <id column="id" property="id"/> <result column="name" property="userName"/> </resultMap> <select id="selectUserById" resultMap="User" parameterType="int" > select * from m_user where id = #{id} </select></mapper>
type:對應的是我們的實體類,全路徑名。id:可以理解為別名。
id:唯一標識,此id值用於select元素屬性的引用。column:對應我們資料庫表中的欄位名稱。property:對應我們的實體類的屬性,比如:User中的屬性userName,要和資料庫表m_user中的name對應。result:標識一些簡單屬性,其中column屬性代表資料庫的欄位名,property代表查詢出來的欄位名映射到實體類的某個屬性。繼續使用我們前面的測試類進行測試:
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);System.out.println(userMapper.selectUserById(1));
輸出:
User{id=1, userName='tian', age=22}
注意:實體類的get set和toString()方法這裡給省略, 希望大家在使用的使用,使用快捷鍵很簡單的就搞定了。
上面提到過
和
,那麼他們兩到底有什麼區別呢?
resultType和resultMap 有什麼區別?
:直接表示返回類型, 包括基本數據類型和複雜數據類型。:外部定義的引用,通過對應的外部的id,表示結果映射到哪個上,一般用於欄位名和屬性名不一致的情況,或者需要做複雜的聯合查詢以便自由控制映射結果。兩者的關聯
當進行查詢時,查詢出來的每個欄位都會放在一個Map裡,當查詢元素返回屬性是
的時候,會將鍵值對取出賦所指定的屬性。其實
的每個查詢映射的返回類型都是
,只是當我們使用
的時候,會自動把對應的值賦給所指定的對象屬性,當使用
時候,因為map不是很好的表示領域,我們就進一步的轉化為對應的實體對象。
主要作用於複雜的聯合查詢上。
的自動映射級別:默認級別為PARTIAL,也可以在settings更改值。
注意:和本質是一樣的,都是Map數據結構,但是二者不能同時存在。
增刪改案例
insert
從這裡可以知道,關於增加insert是沒有返回值類型可以讓我們指定的。默認返回int類型。
<insert id="insert" parameterType="com.tian.mybatis.entity.User"> INSERT INTO m_user(`name`,age) VALUES ( #{userName},#{age})</insert>
對應Mapper中的方法
int insert(User user);
另外的update和delete類似,這裡就沒有必要逐一演示了。
註解方式
九個頂級映射元素對應註解:
其他部分註解是配合九個註解進行使用的。
select註解
把本地的UserMapper.xml刪掉,然後改一下
mybatis-config.xml
,把其中的UserMapper.xml給注釋掉。添加
<mapper/>
UserMapper.java添加註解
public interface UserMapper { @Select("select * from m_user where id = #{id}") User selectUserById(Integer id);}
再次測試
User user = sqlSession.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById", 1);System.out.println(user);
User{id=1, userName='null', age=22}
從輸出內容看到,userName為null,這也是因為和資料庫表匯中的欄位name不一致導致的,那麼如何處理呢?
這麼搞,再添加一個註解:
public interface UserMapper { @Select("select * from m_user where id = #{id}") @Results( @Result(column = "name",property = "userName")) User selectUserById(Integer id);}
這樣也就是在使用註解的時候,處理實體屬性名和資料庫表欄位名不一樣的問題的辦法。
insert、update、delete同樣也可以使用註解來搞定了。
@Insert、@Update、@Delete配上相應的SQL語句。
註解和xml是否可以共存?
<update id="updateAuthorIfNecessary"> update m_user <trim prefix="SET" suffixOverrides=","> <if test="userName != null and userName != ''"> `name` = #{userName}, </if> <if test="gender != null and gender != 0"> gender = #{gender}, </if> <if test="age != null and age != 0"> age = #{age}, </if> </trim> where id=#{id} </update>
同時在UserMapper.java中的方法上添加註解
@Update("update m_user set `name` = #{userName},gender = #{gender},age = #{age} where id=#{id}")int updateAuthorIfNecessary(User user);
再次中子星的時候回報異常的:
nested exception is java.lang.IllegalArgumentException:Mapped Statements collection already contains value for com.tian.mybatis.mapper.UserMapper.updateAuthorIfNecessary. please check file [D:\workspace\my_code\mybatis\target\classes\mapper\UserMapper.xml] and com/tian/mybatis/mapper/UserMapper.java (best guess)
大致意思就是說,已經存在了,即就是不能同時使用xml和註解。二者選其一。
xml可以喝註解結合使用,但是得保證同一個方法不能同時存在xml和註解。
建議
簡單的sql處理可以使用註解,複雜的sql使用xml。但是實際工作還得看你待的項目中有沒有對這個進行規範化。
在項目中無非就三種:
1.全部必須使用xml方式。
2.全部必須使用註解方式。
3.可以同時使用xml和註解。
高級映射
association
映射到
JavaBean
的某個複雜的」數據類型」屬性,僅處理一對一的關聯關係。
<resultMap type="com.tian.mybatis.entity.User" id="userMapRole"> <id column="id" property="id" /> <result column="name" property="userName" /> <result column="age" property="age" /> <association property="role" javaType="UserRole"> <id column="id" property="id" /> <result column="roleName" property="roleName" /> </association></resultMap>
association的屬性節點:
property:映射資料庫列的實體對象屬性名。javaType:完整的java類名和限定名。propert所映射的屬性的類型。子元素
id:一般為映射主鍵,可以提高性能。result:column:映射的資料庫的欄位名。property:映射的數據列對應的實體對象屬性。collection
映射到JavaBean的某個複雜的」數據類型」屬性,這個屬性是一個集合列表,處理一對多的關聯關係。
<resultMap type="com.tian.mybatis.entity.User" id="userMapAddress"> <id column="id" property="id"/> <result column="name" property="userName"/> <collection property="lists" ofType="UserAddress"> <id column = "id" property = "id"> <result column="addressDesc" property="addressDesc"/> </collection> </resultMap>
ofType
:完整的Java類名和限定名。propert所映射的屬性的類型。
其餘和association基本一致。
association和collection都具備延遲加載功能。
延遲加載:先從單表查詢,需要時再查關聯表,大大的提高了資料庫性能,因為相對來說單表查詢比多表查詢要快。
xml和註解的關係
上面我們已經講了兩種方式的實現,下面來對比一下,兩種方式的關係:
必須有個一個XxxMapper.xml與之對應,方法名對應xml中的id,方法入參和方法出參都必須對應起來,很容易出問題。我們在開發的時候有的是可以使用代碼生成器生成,但是有的是必須自己手寫,有的公司也是要求必須手寫,所以這裡需要注意。
不需要
XxxMapper.xml
文件,只需要在對應
中的方法上加上註解就搞定了,但是這裡是有坑的。畢竟把sql放到了我們的Java代碼裡了。
優缺點
xml方式: 增加了xml文件,修改麻煩,條件不確定(ifelse判斷),容易出錯,特殊轉義字符比如大於小於 。
註解方式:複雜sql不好用,搜集sql不方便,管理不方便,修改需重新編譯
總結
本文講述了
的兩種映射方式,以及一些注意點,一些關係和區別。
實體屬性名和資料庫表欄位名不一樣的情況下,xml和註解分別是如何處理的。resultType和resultMap的區別。