1. 程式人生 > >Mybatis入門(三)

Mybatis入門(三)

動態sql

MyBatis常用OGNL表示式

  1. e1 or e2

  2. e1 and e2

  3. e1 == e2,e1 eq e2

  4. e1 != e2,e1 neq e2

  5. e1 lt e2:小於

  6. e1 lte e2:小於等於,其他gt(大於),gte(大於等於)

  7. e1 in e2

  8. e1 not in e2

  9. e1 + e2,e1 * e2,e1/e2,e1 - e2,e1%e2

  10. !e,not e:非,求反

  11. e.method(args)呼叫物件方法

  12. e.property物件屬性值

  13. e1[ e2 ]

    按索引取值,List,陣列和Map

  14. @[email protected](args)呼叫類的靜態方法

  15. @[email protected]呼叫類的靜態欄位值

1、if

  • if標籤:判斷語句,用於進行邏輯判斷的。

       test屬性:用來編寫表示式,支援ognl

案例:查詢男性使用者,如果輸入了使用者名稱,按使用者名稱模糊查詢,如果沒有輸入使用者名稱,就查詢所有男性使用者

定義介面  

在UserMapper介面中定義方法:

/**

* 通過使用者名稱進行模糊查詢,如果沒有使用者名稱,查詢所有男性使用者

* @param userName

* @return

*/

List<User> queryUsersLikeUserName(@Param("userName")String userName);

在UserMapper對映檔案中,定義介面方法對應的Statement

<select id="queryUsersLikeUserName" 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>

</select>

測試

@Test

public void testQueryUsersLikeUserName(){

List<User> list = userMapper.queryUsersLikeUserName("zhang");

for (User user : list) {

System.out.println(user);

}

}

2、choose when otherwise

  • choose標籤:條件選擇

  • when子標籤:編寫條件,不管有多少個when條件,一旦其中一個條件成立,後面的when條件都不執行。

                                               test屬性:編寫ognl表示式

otherwise子標籤:當所有條件都不滿足時,才會執行該條件

        需求:編寫一個查詢方法,設定兩個引數,一個是使用者名稱,一個是年齡。

根據使用者名稱或者年齡查詢所有男性使用者,如果輸入了使用者名稱則按照使用者名稱模糊查詢,否則就按照年齡查詢,兩個條件只能成立一個,如果都不輸入就查詢使用者名稱為“zhangsan”的使用者

定義介面

在UserMapper介面中,定義介面方法:

/**

* 根據使用者名稱或者年齡查詢男性使用者,如果有使用者名稱就按使用者名稱查詢,否則按年齡查詢,如果都沒有,就查詢名字是zhangsan的男性使用者

* @param userName

* @param age

* @return

*/

List<User> queryUserListByUserNameOrAge(@Param("userName")String userName,@Param("age")Integer age);

在UserMapper.xml中,定義介面方法對應的Statement

<select id="queryUserListByUserNameOrAge" resultType="User">

   select * from tb_user where sex = 1

   <choose>

      <when test="userName!=null and userName.trim()!=''">

         and user_name like '%' #{userName} '%'

      </when>

      <when test="age!=null">

         and age = #{age}

      </when>

      <otherwise>

         and user_name = "zhangsan"

      </otherwise>

   </choose>

</select>

測試

@Test

public void testQueryUserListByUserNameOrAge(){

    List<User> list = userMapper.queryUserListByUserNameOrAge("zhang",20);

    for (User user : list) {

        System.out.println(user);

    }

}

3、 Where

            where標籤:可以自動將動態sql中多出來的一個and或者or去除

案例:查詢所有使用者,如果輸入了使用者名稱按照使用者名稱進行模糊查詢,如果輸入年齡,按年齡進行查詢,如果兩者都輸入,兩個條件都要成立

定義介面

在UserMapper介面中,定義介面方法:

/**

 * 根據使用者名稱和年齡查詢使用者。

 * @param userName

 * @param age

 * @return

 */

List<User> queryUserListByUserNameAndAge(@Param("userName")String userName,@Param("age")Integer age);

在UserMapper.xml中,定義介面方法對應的Statement

<select id="queryUserListByUserNameAndAge" resultType="User">

   select * from tb_user

   <where>

      <if test="userName!=null and userName.trim()!=''">

         user_name like '%' #{userName} '%'

      </if>

      <if test="age!=null">

         and age = #{age}

      </if>

   </where>

</select>

測試

@Test

public void testQueryUserListByUserNameAndAge(){

    List<User> list = userMapper.queryUserListByUserNameAndAge("", 30);

    for (User user : list) {

        System.out.println(user);

    }

}

4、Set

set標籤:

    可以自動新增一個set關鍵字,並且會將動態sql最後多餘的逗號去除

案例:修改使用者資訊,如果引數user中的某個屬性為null,則不修改

定義介面:

在UserMapper介面中,定義介面方法

/**

* 修改使用者

* @param user

*/

void updateUserSelective(User user);

在UserMapper.xml中,定義介面方法對應的Statement

<update id="updateUserSelective">

   update tb_user

   <set>

      <if test="userName!=null and userName.trim()!=''">

         user_name = #{userName},

      </if>

      <if test="password!=null and password.trim()!=''">

         password = #{password},

      </if>

      <if test="name!=null and name.trim()!=''">

         name = #{name},

      </if>

      <if test="age!=null">

         age = age

      </if>

   </set>

   where id = #{id}

</update>

測試

@Test

public void testUpdateUserSelective(){

    User user = new User();

    user.setUserName("admin11");

    user.setPassword("654321");

    user.setName("使用者11");

    //user.setAge(20);

    user.setId(9l);

    userMapper.updateUserSelective(user);

}

5、foreach

foreach標籤:遍歷集合或者陣列

collection屬性:接收的集合或者陣列引數

item屬性:集合或者陣列引數中的每一個元素

separator屬性:標籤分隔符

open屬性:以什麼開始

close屬性:以什麼結束

案例:按照多個id查詢使用者資訊

介面

在UserMapper介面中,定義介面方法:

/**

 * 通過多個使用者id查詢多個使用者

 * @param ids

 * @return

 */

List<User> queryUserListByIds(@Param("ids")Long[] ids);

在UserMapper.xml中,定義介面方法對應的Statement

<select id="queryUserListByIds" resultType="User">

   select * from tb_user where id in

   <foreach collection="ids" item="id" separator="," open="(" close=")">

      #{id}

   </foreach>

</select>

測試

@Test

public void testQueryUserListByIds(){

Long [] ids = new Long[] {1l,2l,3l};

List<User> users = userMapper.queryUserListByIds(ids);

for (User user : users) {

System.out.println(user);

}

}

總結

1、If標籤:條件判斷

       test屬性:編寫ognl表示式

2、choose標籤:條件選擇

       when子標籤:用於條件選擇,一旦有一個when成立,後續不再執行。

       otherwise子標籤:所有條件不成立,執行該條件。

3、where標籤:用於sql動態條件拼接,新增where關鍵字,可以將動態sql多餘的第一個and或者or去除。

4、set標籤: 用於更新語句的拼接,新增set關鍵字,並可以將動態sql中多餘的逗號去除

5、foreach標籤:用於遍歷引數中的集合

       collection屬性:引數中的集合

       item屬性:表示集合中的某個元素

       separator屬性:分隔符

       open:以什麼開始

       close:以什麼結束

高階查詢(OrderMapper.xml)

1、一對一查詢

需求:通過訂單編號20140921003查詢出訂單資訊,並查詢出下單人資訊

編寫介面

public interface OrderMapper {

    /**

     * 通過訂單編號查詢訂單和使用者

     * @param orderNumber:訂單編號

     * @return

     */

    Order queryOrderAndUserByOrderNumber(@Param("orderNumber") String orderNumber);

}

建立Order

/**

* 訂單表

*

*/

public class Order {



private Integer id;



private Long userId;



private String orderNumber;



private User user;



@Override

public String toString() {

return "Order{" +

"id=" + id +

", userId=" + userId +

", orderNumber='" + orderNumber + '\'' +

", user=" + user +

'}';

}



public User getUser() {

return user;

}



public void setUser(User user) {

this.user = user;

}



public Integer getId() {

return id;

}



public void setId(Integer id) {

this.id = id;

}



public Long getUserId() {

return userId;

}



public void setUserId(Long userId) {

this.userId = userId;

}



public String getOrderNumber() {

return orderNumber;

}



public void setOrderNumber(String orderNumber) {

this.orderNumber = orderNumber;

}

}

編寫OrderMapper.xml

注意:一旦涉及到巢狀對映,一定要設定手動設定為自動對映。不管是Order的自動對映還是User的自動對映都需要手動設定為true

<mapper namespace="cn.itcast.mapper.OrderMapper">

<!--

配置自定義結果集

id屬性:自定義結果集的唯一標識

type屬性:結果集型別

autoMapping屬性:多表查詢時,必須設定為true,Order物件和tb_order表的屬性和欄位才會進行自動對映

-->

<resultMap id="orderAndUserResultMap" type="Order" autoMapping="true">

<!--配置Order的主鍵對映-->

<id column="id" property="id"></id>

<!--

association標籤:用於對一的對映

property屬性:類中的關聯屬性的名稱

javaType屬性:屬性對應的型別

autoMapping屬性:autoMapping屬性:多表查詢時,必須設定為true,User物件和tb_user表的屬性和欄位才會進行自動對映

-->



<association property="user" javaType="User" autoMapping="true">

<!--配置User的主鍵對映-->

<id column="uid" property="id"></id>

</association>

</resultMap>



<!--一對一查詢-->

<select id="queryOrderAndUserByOrderNumber" resultMap="orderAndUserResultMap">

SELECT

*,u.id as uid

FROM

tb_order o

INNER JOIN tb_user u ON o.user_id = u.id

WHERE

o.order_number = #{orderNumber}

</select>



</mapper>

全域性配置檔案管理對映檔案

在mybatis-config.xml中新增程式碼如下:

<mapper resource="OrderMapper.xml"/>

編寫測試類 

@Test

    public void queryOrderAndUserByOrderNumber() {

        Order order = orderMapper.queryOrderAndUserByOrderNumber("20140921003");

        System.out.println(order);



    }

2、 一對多查詢

一對多查詢:通過訂單編號20140921001查詢訂單,並查詢出下單人資訊以及查詢出訂單詳情

思路: 訂單 : 訂單詳情 = 1 : n(體現在pojo物件中,就是在Order物件中新增OrderDetail物件的集合)

SQL:查詢出兩條記錄,除了item_id和total_price不同外,其他都一樣

修改Order

在Order類新增List<OrderDetial>屬性,並新增get、set方法和toString方法:

/**

* 訂單表

*

*/

public class Order {



    private Integer id;



    private Long userId;



    private String orderNumber;



    //配置Order和User一對一的關係

    private User user;

    //配置Order和Orderdetail一對多的關係

    private List<Orderdetail> orderdetails;



    @Override

    public String toString() {

        return "Order{" +

                "id=" + id +

                ", userId=" + userId +

                ", orderNumber='" + orderNumber + '\'' +

                ", user=" + user +

                ", orderdetails=" + orderdetails +

                '}';

    }



    public List<Orderdetail> getOrderdetails() {

        return orderdetails;

    }



    public void setOrderdetails(List<Orderdetail> orderdetails) {

        this.orderdetails = orderdetails;

    }



    public User getUser() {

        return user;

    }



    public void setUser(User user) {

        this.user = user;

    }

編寫介面方法

/**

     * 通過訂單編號查詢訂單、使用者和訂單詳情

     * @param orderNumber:訂單編號

     * @return

     */

    Order queryOrderAndUserAndOrderdetailByOrderNumber(@Param("orderNumber") String orderNumber);

編寫statement

<!--一對多自定義結果集-->

<resultMap id="orderAndUserAndOrderdetailsResultMap" type="Order" autoMapping="true">

   <id column="id" property="id"></id>

   <!--一對一-->

   <association property="user" javaType="User" autoMapping="true">

      <id column="uid" property="id"></id>

   </association>

   <!--一對多-->

   <collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true">

      <id column="detail_id" property="id"></id>

   </collection>



</resultMap>



<!--一對多查詢-->

<select id="queryOrderAndUserAndOrderdetailByOrderNumber" resultMap="orderAndUserAndOrderdetailsResultMap">

   SELECT

      *,u.id as uid,od.id as detail_id

   FROM

      tb_order o

   INNER JOIN tb_user u ON o.user_id = u.id

   INNER JOIN tb_orderdetail od on od.order_id = o.id

   WHERE

      o.order_number = #{orderNumber}

</select>

測試

@Test

public void queryOrderAndUserAndOrderdetailByOrderNumber(){

    Order order = orderMapper.queryOrderAndUserAndOrderdetailByOrderNumber("20140921001");

    System.out.println(order);

}

注意:在使用別名查詢來對映主鍵的時候,一定要手動在sql語句中自行定義別名

3、多對多查詢

多對多查詢:通過訂單號20140921001查詢訂單,查詢出下單人資訊並且查詢出訂單詳情以及商品資料

思路:訂單:訂單詳情 = 1 : n(體現在pojo物件中就是在Order物件中新增OrderDetail物件的集合)

          訂單詳情:商品 = 1 : 1(體現在pojo物件中就是在OrderDetail物件中新增Item物件)

Sql:#通過訂單號20140921001查詢訂單,查詢出下單人資訊並且查詢出訂單詳情中的商品資料

通過訂單號20140921001查詢訂單,查詢出下單人資訊並且查詢出訂單詳情中的商品資料。

1、由於是通過Order對一查詢User,因此要在Order的實體中設定對一User的屬性配置,然後還要對多去查詢Orderdetail,因此要在Order實體類中設定對多detailList的屬性來進行查詢,並新增get、set方法:之前已設定過

2、還要通過Orderdetail去對一查詢Item,因此要在Orderdetail中去設定對一Item的屬性,並新增get、set方法:

修改Orderdetail

在Orderdetail中新增item屬性:

public class Orderdetail {

   

    private Integer id;

   

    private Double totalPrice;

   

    private Integer status;



    //配置Orderdetail和Item一對一的關係

    private Item item;



    @Override

    public String toString() {

        return "Orderdetail{" +

                "id=" + id +

                ", totalPrice=" + totalPrice +

                ", status=" + status +

                ", item=" + item +

                '}';

    }



    public Item getItem() {

        return item;

    }



    public void setItem(Item item) {

        this.item = item;

    }

編寫介面方法

/**

     * 通過訂單編號查詢訂單、使用者和訂單詳情以及商品

     * @param orderNumber

     * @return

     */

    Order queryOrderAndUserAndOrderdetailAndItemByOrderNumber(@Param("orderNumber") String orderNumber);

編寫statement

OrderMapper配置(通過在collection標籤中巢狀使用association標籤):

<resultMap id="orderAndUserAndOrderdetailsAndItemResultMap" type="Order" autoMapping="true">

   <id column="id" property="id"></id>

   <!--一對一-->

   <association property="user" javaType="User" autoMapping="true">

      <id column="uid" property="id"></id>

   </association>

   <!--一對多-->

   <collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true">

      <id column="detail_id" property="id"></id>

      <!--訂單詳情和商品的一對一的關係-->

      <association property="item" javaType="Item" autoMapping="true">

         <id column="iid" property="id"></id>

      </association>

   </collection>



</resultMap>



<!--多對多查詢-->

<select id="queryOrderAndUserAndOrderdetailAndItemByOrderNumber" resultMap="orderAndUserAndOrderdetailsAndItemResultMap">

   SELECT

      *,u.id as uid,od.id as detail_id,i.id as iid

   FROM

      tb_order o

   INNER JOIN tb_user u ON o.user_id = u.id

   INNER JOIN tb_orderdetail od on od.order_id = o.id

   INNER JOIN tb_item i on i.id = od.item_id

   WHERE

      o.order_number = #{orderNumber}

</select>

測試

@Test

public void queryOrderAndUserAndOrderdetailAndUserByOrderNumber(){

    Order order = orderMapper.queryOrderAndUserAndOrderdetailAndItemByOrderNumber("20140921001");

    System.out.println(order);

}

4、 resultMap的繼承

如果說有的resultMap中的資料其他地方也要用到,那麼可以使用繼承的方法來複用即可:

5、 高階查詢的整理

resutlType無法幫助我們自動的去完成對映,所以只有使用resultMap手動的進行對映

resultMap:

type 結果集對應的資料型別

id 唯一標識,被引用的時候,進行指定

autoMapping 開啟自動對映

extends 繼承

子標籤:

       association:配置對一的對映

              property 定義物件的屬性名

javaType 屬性的型別

autoMapping 開啟自動對映

       collection:配置對多的對映

              property 定義物件的屬性名

              javaType 集合的型別

              ofType 集合中的元素型別

    autoMapping 開啟自動對映

延遲載入

何為延遲載入?

延遲載入:就是在需要用到資料時才進行載入,不需要用到資料時就不載入資料。延遲載入也稱懶載入.

好處:先從單表查詢,需要時再從關聯表去關聯查詢,大大提高資料庫效能,因為查詢單表要比關聯查詢多張錶速度要快。。

壞處:因為只有當需要用到資料時,才會進行資料庫查詢,這樣在大批量資料查詢時,因為查詢工作也要消耗時間,所以可能造成使用者等待時間變長,造成使用者體驗下降。

需求:

延遲載入需求:通過訂單編號20140921001查詢order並延遲載入user

如果通過訂單編號查詢order並且查詢user資訊,在正常情況下的查詢語句應該是:

如果改成延遲載入,也就意味著,先查詢order,等需要的時候再去查詢user,那就相當於將上面的一條語句變成了兩條語句:

1、通過訂單編號查詢order

2、通過查詢出來的order中的user_id查詢user

延遲載入案例

編寫介面方法

/**

 * 延遲載入

 * @param orderNumber

 * @return

 */

Order queryOrderUserLazy(@Param("orderNumber")String orderNumber);

編寫statement

兩個statement就分別相當於兩條查詢order和查詢user的sql語句,然後通過resultMap將延遲載入的查詢user的statement關聯到查詢查詢order的statement上。使得在查詢order之後,可以延遲載入user.

<!--通過Order延遲載入User-->

<resultMap id="orderUserLazyResultMap" type="Order">

<!--

select屬性:呼叫指定sql語句來執行延遲載入

column屬性:延遲載入的sql語句中所需的引數

-->

<association property="user" javaType="User" select="queryUserByIdOfOrder" column="{id=user_id}"></association>



</resultMap>



<!--通過訂單編號查詢訂單-->

<select id="queryOrderUserLazy" resultMap="orderUserLazyResultMap">

select * from tb_order where order_number = #{orderNumber}

</select>

<select id="queryUserByIdOfOrder" resultType="User">

select * from tb_user where id = #{id}

</select>

測試

@Test

public void queryOrderUserLazy(){

Order order = orderMapper.queryOrderUserLazy("20140921001");

System.out.println(order.getId()+"..."+order.getOrderNumber()+"..."+order.getUserId());

System.out.println(order.getUser());



}

測試結果:發現雖然已經將語句進行了分開查詢,但是並沒有延遲載入,還是一起查詢了。

原因:沒有開啟延遲載入的開關

開啟延遲載入的開關

延遲載入預設是false:

在全域性配置檔案中開啟延遲載入的開關:

<!-- 設定引數 -->

<settings>

<!--開啟駝峰-->

<setting name="mapUnderscoreToCamelCase" value="true"/>

<!--開啟延遲載入-->

<setting name="lazyLoadingEnabled" value="true"></setting>

</settings>

延遲載入分析圖例

注意:association標籤總的colunm屬性,如果傳遞的引數只有一個的話,直接使用user_id即可,如果是多個可以使用{user_id=id,user_nam=userName}來傳遞。

也可以修改如下:

附:

如果sql語句中出現’<’的解決方案

1、使用xml中的字元實體

【示例】

修改如下即可:

2、使用<![CDATA[ < ]]>

CDATA 內部的所有東西都會被解析器忽略,因此只要將字元實體編寫在其中,就不需要進行轉義。

如下:

附:Mybatis入門三天原始碼: