對比JdbcTempalte,mybatis才能稱得上是框架,JdbcTempalte頂多算是工具類,同時,對比Hibernate,Mybatis又有更多的靈活性,算是一種折中方案。html
特色:java
mybatis總體結構:mysql
主配置文件
web
依賴sql
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency>
主配置文件數據庫
<?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> <!-- 環境:說明能夠配置多個,default:指定生效的環境 --> <environments default="development"> <!-- id:環境的惟一標識 --> <environment id="development"> <!-- 事務管理器,type:類型 --> <transactionManager type="JDBC" /> <!-- 數據源:type-池類型的數據源 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <!-- 映射文件 --> <mappers> <mapper resource="UserMapper.xml"/> </mappers> </configuration>
映射文件:
apache
<?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"> <!-- namespace(命名空間):映射文件的惟一標識 --> <mapper namespace="UserMapper"> <!-- 查詢的statement,id:在同一個命名空間下的惟一標識,resultType:sql語句的結果集封裝類型,這裏須要全名 --> <select id="queryUserById" resultType="cn.bilibili.mybatis.pojo.User"> select * from tb_user where id = #{id} </select> </mapper>
使用slf4j12記錄日誌:
數組
依賴緩存
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.4</version> </dependency>
配置文件session
log4j.rootLogger=DEBUG,A1 log4j.logger.org.apache=DEBUG log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.Target=System.err log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
測試方法:
SqlSession sqlSession = null; try { // 指定mybatis的全局配置文件 String resource = "mybatis-config.xml"; // 讀取mybatis-config.xml配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); // 構建sqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 獲取sqlSession會話 sqlSession = sqlSessionFactory.openSession(); // 執行查詢操做,獲取結果集。參數:1-命名空間(namespace)+「.」+statementId,2-sql的佔位符參數 User user = sqlSession.selectOne("UserMapper.queryUserById", 1L); System.out.println(user); } finally { // 關閉鏈接 if (sqlSession != null) { sqlSession.close(); } }
大體就是經過SqlSessionFactoryBuilder類得到sql會話工廠,經過sqlSession執行sql語句,而要執行的語句及其映射bean都已經配置在xml裏面。須要注意的是mybatis默認開啓事務,因此執行增刪改時須要手動提交。
mybatis能夠直接映射Dao接口,而沒必要寫其實現類(實際上是mybatis幫咱們實現了啦)
Dao接口:
public interface UserMapper { public User queryUserById(Long id); public List<User> queryUserList(); public void insertUser(User user); public void updateUser(User user); public void deleteUserById(Long id); }
映射配置文件
<?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="UserMapper"> <!-- id和方法名對應,而且惟一,所以不能有重載方法;返回類型和resultType對應; --> <select id="queryUserById" resultType="cn.bilibili.mybatis.pojo.User"> select * from tb_user where id = #{id} </select> ... </mapper>
Tips:在IDEA中可使用Ctrl + Shift + T
快速建立測試用例
<settings> <!-- 開啓駝峯匹配 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
配置文檔的頂層結構以下:
具體能夠參考官方文檔
這裏介紹幾個經常使用的屬性。
屬性:properties,能夠定義變量,而後使用${變量名}
來取得其值,如:
<properties resource = "jdbc.properties" />
jdbc.properties內容:
driverClass = com.mysql.jdbc.Driver url = jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8 username = root password = 1234
而後即可以經過${driverClass}
來配置驅動了。
包含<setting>
標籤,有name
和value
屬性
駝峯匹配(默認關閉)
<setting name="mapUnderscoreToCamelCase" value="true"/>
延遲加載(默認關閉)
<setting name="lazyLoadingEnabled" value="true"/>
二級緩存(默認開啓)
<setting name="cacheEnabled" value="true"/>
自動映射規則:
name屬性 | 描述 | value屬性 | 默認value |
---|---|---|---|
autoMappingBehavior | 指定 MyBatis 應如何自動映射列到字段或屬性。 NONE 表示取消自動映射;PARTIAL 只會自動映射沒有定義嵌套結果集映射的結果集。 FULL 會自動映射任意複雜的結果集(不管是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
什麼沙雕排版😅
類型別名是爲 Java 類型設置一個短的名字。 它只和 XML 配置有關,存在的意義僅在於用來減小類徹底限定名的冗餘。
<typeAliases> <!-- 第一種 --> <typeAlias alias="User" type="com.bilibili.pojo.User"/> <!-- 第二種 --> <package name="com.bilibili.pojo"/> </typeAliases>
第一種是直接配置一個類的別名,第二種是配置掃描一個包下的全部類
或者註解方式配置別名:
@Alias("User") public class User { ... }
以及數據類型的別名(不區分大小寫,基本數據類型特殊命名):
別名 | 映射的類型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
Mybatis能夠配置多個環境,可是一個SQLSessionFactory只對應一個環境
<!-- 能夠配置多個環境,並設置默認環境 --> <environments default="development"> <!-- 配置環境,id:環境的惟一標識 --> <environment id="development"> <!-- 事務管理器,type:使用jdbc的事務管理器 --> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <!-- 數據源,type:池類型的數據源 --> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>
能夠經過build方法的重載指定環境:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environmentID); //reader爲主配置文件流,environment爲environmentID,properties爲讀取的變量文件,三個參數的任一改變都能改變環境 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environmentID, properties);
若是忽略了環境參數,那麼默認環境將會被加載,以下所示:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);
mapper的做用是告訴mybatis去哪裏照執行的SQL語句,能夠經過下面四種方式:
相對路徑的xml文件引用(resources目錄):
<!-- 使用相對於類路徑的資源引用,相對的是resources目錄 --> <mappers> <mapper resource="UserMapper.xml"/> </mappers>
絕對路徑的xml文件引用(不推薦):
<!-- 使用徹底限定資源定位符(URL) --> <mappers> <mapper url="file:///D:/UserMapper.xml"/> </mappers>
經過接口路徑:
<!-- 使用映射器接口實現類的徹底限定類名 --> <mappers> <mapper class="com.bilibili.mapper.UserMapper"/> </mappers>
此方式條件:
經過接口所在包的路徑
<!-- 將包內的映射器接口實現所有註冊爲映射器 --> <mappers> <package name="com.bilibili.mapper"/> </mappers>
注意事項
使用動態代理的方式實現接口映射時,mapper.xml文件的命名空間必須是接口的全限定名,而且不能使用typeAliases
別名,由於別名是針對Java Bean的
前兩種爲直接指定xml文件位置,所以對xml路徑沒有什麼要求。
後兩種爲指定接口而後尋找xml文件,所以xml須要在和接口相同的路徑(相對resources),而且!idea中resources目錄不能使用.
(點)來創建多級目錄,須要使用/
或者\
來創建多級目錄!!!
映射文件的結構:
示例:
<select id="queryUserById" resultType="User"> SELECT * FROM user WHERE ID = #{id} </select>
示例:
<insert id = "addUser" useGeneratedKeys = "true" keyColumn = "id" keyProperty = "id" parameterType = "User"> INSERT INTO user ( id, username, password ) VALUES ( NULL, #{userName}, #{password} ) </insert>
示例:
<update id = "updateUserById" parameterType = "User"> UPDATE user SET username = #{username}, password = #{password} WHERE id = #{id} </update> <delete id = "deleteUserById" parameterType = "long"> DELETE FROM user WHERE id = #{id} </delete>
CRUD都有個parameterType標籤,用來指定接受的參數類型。
接收參數有兩種方式:
#{ }
預編譯,相似佔位符${ }
非預編譯,直接sql拼接,不能防止SQL注入,通常用來接收表名等屬性參數類型有三種:
基本數據類型
當sql方法的參數是多個時:
例如queryUserByUserNameAndPassword(String username, String password)
這種兩個參數時,能夠這樣接收參數:
<select id="queryUserByUserNameAndPassword" resultType="User"> SELECT * FROM user WHERE username = #{0}/#{arg0}/#{param1} AND password = #{1}/#{arg1}/#{param2} </select>
不能見名知意的變量不是好方法,因此咱們的解決方案是添加@Param
註解:
queryUserByUserNameAndPassword(@Param(username) String username, @Param(password)String password)
<select id="queryUserByUserNameAndPassword" resultType="User"> SELECT * FROM user WHERE username = #{username} AND password = #{password} </select>
這樣就能夠見名知意了。
HashMap參數
示例:
User loginMap(Map<String,String> map); /****************************************/ Map<String, String> map = new HashMap<>(); map.put("userName","zhangsan"); map.put("password","123456"); User user = userMapper.loginMap(map);
xml中和註解用法相似:
<select id="loginMap" resultType="User"> SELECT * FROM user WHERE username = #{userName} AND password = #{password} </select>
Pojo
mapper.xml用法不變,xml是經過Getter方法來獲取值的。
${}
的用法
一個參數時,默認狀況下使用${value}
接收數據。可是這樣不能見名知意。
一樣使用@Param()
註解。
${ }
和#{ }
#{ }
${ }
SQL拼接(不能防止SQL注入)
$
還有一個問題是,在主配置文件中,使用${driver}
獲取資源文件的驅動或者url等數據,若是用戶的password屬性和資源文件中的password屬性同名時,此時會讀取資源文件中的password而不會使用傳入的參數password,解決方法是在資源文件中的屬性都加入前綴:
jdbc.driverClass = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8 jdbc.username = root jdbc.password = 1234
固然,主配置文件裏面的引用也要修改。
resultMap是mybatis中最強大的特性,能夠很方便的解決下面兩個問題:
簡單映射示例:
在映射文件中配置自定義ResultMap:
<resultMap id="userResultMap" type="User" autoMapping="true"> <!--配置主鍵映射關係,配置主鍵能夠增長查詢效率--> <id column="id" property="id"></id> <!--配置普通字段的映射關係--> <result column="user_name" property="userName"></result> </resultMap>
autoMapping
屬性:
在查詢語句中使用自定義映射:
<!-- resultMap屬性:引用自定義結果集做爲數據的封裝方式 --> <select id="queryUserById" resultMap="userResultMap"> select * from tb_user where id = #{id} </select>
一對一映射:
當訂單(Order)對象內有用戶(User)屬性時,以前的狀況咱們是不能一次查詢出來的,可是有了映射即可以很方便的查詢:
<!-- id:惟一標識,type:返回類型,autoMapping:自動映射 --> <resultMap id="orderResultMapper" type="Order" autoMapping="true"> <!-- 主鍵映射 --> <id column="id" property="id"/> <!-- 通常屬性映射 --> <result column="order_number" property="orderNumber"/> <result column="user_id" property="userId"/> <!-- 一對一映射,property:屬性,javaType:屬性類型 --> <association property="user" javaType="User" autoMapping="true"> <!-- 主鍵映射,寫法和resultMap同樣 --> <id column="user_id" property="id"/> </association> </resultMap> <select id="queryOrderByOrderNumber" resultMap="orderResultMapper"> SELECT * FROM tb_order o, tb_user u WHERE o.user_id = u.id AND o.order_number = #{orderNumber} </select>
一對多映射
當一個訂單內有多個信息時,即Order類中持有List<OrderDetail>
,即可以進行一對多映射。
這裏的訂單能夠理解爲多個商品一次下單,這一訂單中有多個OrderDetail,每一個OrderDetail對應一個商品
<resultMap id="orderResultMapper2" type="Order" autoMapping="true"> <!-- 主鍵的字段使用SQL語句的中的別名 --> <id column="oid" property="id"/> <association property="user" javaType="User" autoMapping="true"> <id column="uid" property="id"/> </association> <!-- 一對多映射,property:類中的屬性,javaType:該屬性對應的Java類型,ofType:該屬性存儲的類型,也就是泛型 --> <collection property="orderDetailList" javaType="list" ofType="OrderDetail" autoMapping="true"> <id column="did" property="id"/> </collection> </resultMap> <select id="queryOrderAndUserAndOrderDetailsByOrderNumber" resultMap="orderResultMapper2"> <!-- 當表數量較多時,須要指定不一樣表主鍵的別名來區分 --> SELECT *, o.id oid, detail.id did, u.id uid FROM tb_order o, tb_orderdetail detail, tb_user u where o.order_number = #{orderNumber} AND o.user_id = u.id AND detail.order_id = o.id; </select>
注意,當表數量較多時,須要指定不一樣表主鍵的別名來區分。
多對多映射
每一個OrderDetail對應一個商品,這時即可以添加映射:
<resultMap id="orderResultMapper3" type="Order" autoMapping="true"> <id column="oid" property="id"/> <association property="user" javaType="User" autoMapping="true"> <id column="uid" property="id"/> </association> <collection property="orderDetailList" javaType="list" ofType="OrderDetail" autoMapping="true"> <id column="did" property="id"/> <!-- 添加一對一映射 --> <association property="item" javaType="Item" autoMapping="true"> <id column="iid" property="id"/> </association> </collection> </resultMap> <select id="queryOrderAndUserAndOrderDetailAndItemByOrderNumber" resultMap="orderResultMapper3"> SELECT *, o.id oid, detail.id did, u.id uid, item.id iid FROM tb_order o, tb_user u, tb_orderdetail detail, tb_item item where o.order_number = #{orderNumber} and o.user_id = u.id and detail.order_id = o.id and detail.item_id = item.id </select>
繼承
從上面的代碼能夠看出這樣映射雖然很方便,可是代碼存在冗餘的狀況:
圖中的代碼咱們已經在其餘映射中配置過了,當後面的映射須要這一段時,咱們即可以使用繼承。
修改後的第三個映射的resultMap爲:
<!-- 添加extends屬性,即可以映射重用 --> <resultMap id="orderResultMapper3" type="Order" autoMapping="true" extends="orderResultMapper"> <collection property="orderDetailList" javaType="list" ofType="OrderDetail" autoMapping="true"> <id column="did" property="id"/> <association property="item" javaType="Item" autoMapping="true"> <id column="iid" property="id"/> </association> </collection> </resultMap>
延遲加載
延遲加載是指當咱們須要哪部分數據時,而後再去查。
上面的幾個映射,每次查詢時會一股腦將數據所有查詢出來,即便不須要的數據也會查詢(由於只有一條語句)。當咱們查詢訂單時,須要用戶信息的時候再去查,所以SQL語句須要拆成兩條。
主配置文件中開啓延遲加載:
<settings> <!-- 開啓延遲加載(默認關閉) --> <setting name="lazyLoadingEnabled" value="true" /> <!-- 關閉使用任意屬性,就加載延遲對象的功能。(在3.4.1及以前的版本默認值爲 true,3.4.1以後的版本不須要配置此項) --> <setting name="aggressiveLazyLoading" value="false"/> </settings>
延遲加載示例:
<resultMap id="orderLazyUser" type="Order" autoMapping="true"> <id column="id" property="id"/> <!-- property:屬性,select:延遲加載對象依賴的SQL語句,column:傳進去的參數 --> <association property="user" select="queryUserById" column="user_id" autoMapping="true"> <id column="id" property="id"/> </association> </resultMap> <select id="queryOrderLazyUser" resultMap="orderLazyUser"> SELECT * FROM tb_order where order_number = #{orderNumber} </select> <select id="queryUserById" resultType="User"> SELECT * FROM tb_user WHERE id = #{id} </select>
若是報錯須要添加cglib依賴(3.3以上不須要添加此依賴):
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.1</version> </dependency>
對於使用很頻繁的SQL語句,能夠單獨抽離出來進行復用。
在同一個mapper.xml中:
<sql id = "testSql"> id, username, password, ... </sql> <select id = "queryAllUser" resultType = "User"> SELECT <include refid = "testSql"></include> FROM user </select>
可是這樣只能在一個mapper文件中使用,所以咱們能夠把常用的寫入在一個mapper文件中:
CommonSQL.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="CommonSQL"> <sql id="testSql"> id, user_name, password, ... </sql> </mapper>
在主配置中引入:
<mappers> <mapper resource = "CommonSQL" /> </mappers>
而後就能夠在mapper中使用:
<select id = "queryAllUser" resultType = "User"> SELECT <include refid = "CommonSQL.testSql"></include> FROM user </select>
即refid = "namespace.sqlID"
變一下便可。
感受這個好麻煩啊,由於這些字符在SQL中出現的頻率過高了
符號 | 轉義 |
---|---|
< | < |
> | > |
& | & |
' | ' |
" | " |
或者使用CDATA:<![CDATA[]]>
真是反人類。。。
mybatis中也能夠動態拼接SQL語句(可是在xml中寫SQL太難受了),支持ognl表達式(Struts不就是由於這東西才被黑客利用爆出漏洞死掉的麼,還用。。。)
if
示例:
<select id="queryMaleUserByUserNameOrId" resultType="User" > select * from tb_user where sex = 1 <!-- if標籤,條件判斷,test屬性編寫ognl表達式 --> <if test="userName != null and userName.trim() != ''" > and user_name like #{userName} </if> <if test="id != null and id.trim() != '' " > and id like #{id} </if> </select>
上面的SQL能夠經過用戶名或者ID來查詢。
choose, when, otherwise
至關於switch, case, default,只不過自動break,一旦有一個when成立,後續的when都再也不執行。
示例:
<select id="queryMaleUserByIdOrUserName" resultType="User"> select * from tb_user where sex = 1 <choose> <when test="id != null and id.trim()!='' " > and id like #{id} </when> <when test="userName != null and userName.trim()!=''" > and user_name like #{userName} </when> <otherwise> and active = 1 </otherwise> </choose> </select>
上面的SQL意思是若是提供了id就用id查詢,沒提供id就用用戶名查詢,都沒有的話則查詢全部的激活用戶。
where, set, trim
第一個查詢若是把性別也改成動態的:
<select id="queryUserByUserNameOrId" resultType="User" > select * from tb_user where <if test = "sex != null and id.trim() != '' " > sex = #{sex} </if> <if test = "userName != null and userName.trim() != '' " > and user_name like #{userName} </if> <if test = "id != null and id.trim() != '' " > and id like #{id} </if> </select>
若是sex參數爲空的話這條SQL語句就變成了這樣:
select * from tb_user where and user_name like #{userName} and id like #{id}
有點逗比是否是,此時<where>
標籤的做用就體現出來了:
<select id="queryUserByUserNameOrId" resultType="User" > select * from tb_user <where> <if test = "sex != null and id.trim() != '' " > sex = #{sex} </if> <if test = "userName != null and userName.trim() != '' "> and user_name like #{userName} </if> <if test = "id != null and id.trim() != '' " > and id like #{id} </if> </where> </select>
where 元素只會在至少有一個子元素的條件返回 SQL 子句的狀況下才去插入「WHERE」子句。並且,若語句的開頭爲「AND」或「OR」,where 元素也會將它們去除。
若是 where 元素沒有按正常套路出牌,咱們能夠經過自定義 trim 元素來定製 where 元素的功能。好比,和 where 元素等價的自定義 trim 元素爲:
<trim prefix="WHERE" prefixOverrides="AND | OR "> ... </trim>
<set>
標籤和where相似:
<update id = "changePasswordOrUserName"> UPDATE user <set> <if test = "password != null"> password = #{password}, </if> <if test = "username != null"> username = #{username} </if> </set> WHERE id = #{id} </update>
set 元素會動態前置 SET 關鍵字,同時也會刪掉無關的逗號,由於用了條件語句以後極可能就會在生成的 SQL 語句的後面留下這些逗號。
上面的句子就至關於:
<trim prefix = "SET" suffixOverrides = ","> ... </trim>
foreach
動態 SQL 的另一個經常使用的操做需求是對一個集合進行遍歷,一般是在構建 IN 條件語句的時候。好比:
<select id="selectUserIn" resultType="User"> SELECT * FROM user WHERE id in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
能夠迭代Collection、數組、Map,當參數爲Map時,index爲鍵,item爲值。
一級緩存:
緩存是sqlSession級別,默認開啓(沒法關閉?),可使用session.clearCache()
方法清除緩存。對於同一條數據再次查詢會查詢緩存裏的數據。須要注意的是增、刪、改語句都會清除緩存,即便是不一樣數據。
二級緩存:
二級緩存須要Pojo對象實現Serializable接口,能夠實現不一樣sqlSession間公用緩存。當第一個sqlSession查詢一條數據後調用sqlSession.close()
方法會將數據添加到二級緩存,第二個sqlSession再次查詢同一數據時會使用緩存。(數據增刪改一樣會狀況二級緩存)
開啓二級緩存:
<settings> <!-- 開啓二級緩存,默認是開啓的 --> <setting name="cacheEnabled" value="true"/> </settings>
註解須要在主配置文件中添加包掃描。
給參數添加別名(估計是由於反射不能獲取接口中聲明的局部變量名稱)
示例:
User queryUserById(@Param("id") Integer id);
@Select("select * from tb_user where id = #{id}") User queryUserById(@Param("id") Integer id); @Delete("DELETE FROM tb_user WHERE id = #{id}") int deleteUserById(@Param("id") Integer id); @Update("UPDATE tb_user SET name = #{name} WHERE id = #{id}") int updateNameById(@Param("name") String name, @Param("id") Integer id); /* @Options:參數配置 useGeneratedKeys:主鍵回寫(默認false) keyColumn:主鍵字段名 keyProperty:主鍵屬性(默認id) */ @Insert("insert into tb_user(user_name,password,name) values (#{userName}, #{password}, #{name}) ") @Options(useGeneratedKeys = true,keyColumn = "id") int addUser(User user);
@Select("select id uid,user_name,password pwd from tb_user where id=#{id}") /* Results:定義結果集,內部是一個Result註解的數組 Result:結果集映射關係 column:列名 property:屬性名 */ @Results({ @Result(column = "uid",property = "id"), @Result(column = "user_name",property = "userName"), @Result(column = "pwd",property = "password") }) public User findUserByIdResultMap(@Param("id") Long id);
@Select("select * from tb_order where order_number = #{orderNumber}") @Results({ /* 一對一映射調用其餘接口的方法 column:傳入的參數 property:返回對應的屬性 one表明一對一,值爲@One註解 @One:一對一註解 select:引用的查詢方法 */ @Result(column = "user_id",property = "user", one = @One(select = "com.bilibili.mybatis.mapper.UserMapper.queryUserById")) }) Order queryOrderAndUserByOrderNumber(@Param("orderNumber") String orderNumber);
UserMapper接口:
//被調用的方法 public interface UserMapper { @Select("select * from tb_user where id = #{id}") User queryUserById(@Param("id") Integer id); }
@Select("select * from tb_order where order_number = #{orderNumber}") @Results({ @Result(column = "id", property = "id"), //一對一映射 @Result(column = "user_id",property = "user",one = @One(select = "com.bilibili.mybatis.mapper.UserMapper.queryUserById")), /* 一對多映射 many表明一對多,值爲@Many註解 @Many:一對多註解 select:引用的方法 */ @Result(column = "id", property = "orderDetailList", many = @Many(select = "com.bilibili.mybatis.mapper.OrderDetailMapper.queryOrderDetailsByOrderId" )) }) Order queryOrderAndUserAndOrderDetailsByOrderNumber(@Param("orderNumber") String orderNumber);
OrderDetailMapper接口:
//引用的方法 public interface OrderDetailMapper { @Select("select * from tb_orderdetail where order_id = #{oid}") @Results({ @Result(column = "id",property = "id"), }) List<OrderDetail> queryOrderDetailsByOrderId(@Param("oid") Integer oid); }
多對多隻須要在被引用的一對多方法裏添加一對一便可:
public interface OrderDetailMapper { @Select("select * from tb_orderdetail where id = #{id}") OrderDetail queryOrderDetailById(@Param("id") Integer id); @Select("select * from tb_orderdetail where order_id = #{oid}") @Results({ @Result(column = "id",property = "id"), //添加一對一 @Result(column = "item_id", property = "item", one = @One(select = "com.bilibili.mybatis.mapper.ItemMapper.queryItemById")) }) List<OrderDetail> queryOrderDetailsByOrderId(@Param("oid") Integer oid); }
在@One註解裏添加fetchType
屬性:
@Select("select * from tb_order where order_number = #{orderNumber}") @Results({@Result(column = "user_id", property = "user", one = @One( select = "com.bilibili.mybatis.mapper.UserMapper.queryUserById", //添加延遲加載屬性 fetchType = FetchType.LAZY )) }) Order queryOrderAndUserByOrderNumber(@Param("orderNumber") String orderNumber);
fetchType屬性會覆蓋全局屬性,可選值有:
註解方式使用動態SQL的話須要一個類來專門構建SQL語句:
//用來構建SQL語句的類 public class UserSqlBuilder { //想要在匿名內部類中訪問須要將變量聲明爲爲final public String queryUserByConditions(final User user) { //第一種方式,直接SQL拼接 StringBuilder sqlSb = new StringBuilder("SELECT * FROM tb_user WHERE 1=1 "); if (user.getUserName() != null && user.getUserName().trim().length() > 0) { sqlSb.append(" AND user_name like #{userName} "); } if (user.getSex() != null) { sqlSb.append(" AND sex = #{sex} "); } return sqlSb.toString(); //第二種方式,使用mybatis提供的類 //注意下面的SELECT、FROM等都是方法,而且是寫在構造代碼塊裏的(猛地一看還真沒看明白) String sql = new SQL() {{ SELECT("*"); FROM("tb_user"); if (user.getUserName() != null && user.getUserName().trim().length() > 0){ WHERE("user_name like #{userName}"); } if (user.getSex() != null){ WHERE("sex = #{sex} "); } }}.toString(); return sql; } }
接口中的方法爲:
public interface UserMapper { //只需將@Select()註解替換爲@SelectProvider()便可 //type:提供SQL語句的類,method:提供SQL的方法 @SelectProvider(type = UserSqlBuilder.class,method = "queryUserByConditions") List<User> queryUserByConditions(final User user); }
除了SelectProvider還有:
按需添加便可。
若是@SelectProvider描述的抽象方法沒有使用@Param給變量添加別名,而且聲明瞭多個變量,那麼提供SQL的類的方法參數須要和接口中的方法同樣,也就是聲明出全部的變量:
public interface UserMapper { @SelectProvider(type = UserSqlBuilder.class,method = "queryUserByConditions") List<User> queryUserByConditions(final User user, final String name); }
提供SQL的類的方法也須要聲明爲接口一樣的參數:
public class UserSqlBuilder { //提供SQL的方法也須要聲明兩個變量 public String queryUserByConditions(final User user, final String name){ ... } }
若是接口中的抽象方法使用了@Param參數,那麼類中提供SQL的方法即可以用哪一個參數聲明哪一個參數:
//接口 public interface UserMapper { @SelectProvider(type = UserSqlBuilder.class,method = "queryUserByConditions") List<User> queryUserByConditions(@Param("user") final User user, @Param("user") final String name); }
提供SQL的類中的方法即可以這樣寫:
public class UserSqlBuilder { //只須要聲明本身須要的類即可以了 public String queryUserByConditions(@Param("user") final User user){ ... } }
若是Mybatis的版本在3.5.1之後,能夠將這樣簡化:
映射的接口:
public interface UserMapper { @SelectProvider(UserSqlProvider.class) List<User> queryUserByConditions(final User user); }
提供SQL的類:
//提供類繼承一個超類:ProviderMethodResolver class UserSqlProvider implements ProviderMethodResolver { //這裏的方法名須要和接口中的方法名同樣 public static String queryUserByConditions(final User user){ ... } }