MyBatis框架的學習(四)

前面對MyBatis框架的學習中,我們對Mapper.xml映射文件多少有些瞭解。本文將對Mapper.xml映射文件作更加細緻的梳理,首先從Mapper.xml文件中的輸入和輸出映射開始。本文案例代碼的編寫是建立在前文MyBatis框架的學習(三)案例基礎之上的!

輸入映射和輸出映射

Mapper.xml映射文件中定義了操作數據庫的sql,每個sql是一個statement,映射文件是mybatis的核心。

parameterType(輸入類型)

傳遞簡單類型

傳遞簡單類型,我之前已講過,這裏只給出案例,如下:
這裏寫圖片描述

傳遞pojo對象

MyBatis使用ognl表達式解析對象字段的值,#{}或者${}括號中的值爲pojo屬性名稱。傳遞pojo對象之前也已講過,這裏同樣只給出案例,如下:
這裏寫圖片描述

傳遞pojo包裝對象

開發中通過pojo傳遞查詢條件,查詢條件是綜合的查詢條件,不僅包括用戶查詢條件還包括其它的查詢條件(比如將用戶購買商品信息也作爲查詢條件),這時可以使用包裝對象傳遞輸入參數。即pojo類中包含pojo類。
例如這樣一個需求:根據用戶id查詢用戶信息,查詢條件放到QueryVo類的user屬性中。有需求,就要解決它。我們可在cn.itheima.mybatis.po包下新建一個QueryVo類,如下:

  
  
    public class QueryVo { private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } }

    接下來我們就要在UserMapper.xml映射文件中編寫sql語句了,即在UserMapper.xml映射文件中添加如下配置信息:

      
      
      <select id="getUserByQueryVo" parameterType="queryvo" resultType="user"> select * from user where id=#{user.id}; </select>

      使用包裝類型查詢用戶時,可使用ognl從對象中取屬性值,並且如果是包裝對象可以使用.操作符來取內容的屬性。
      然後在UserMapper接口中添加如下方法:

        
        
      • 1
      User getUserByQueryVo(QueryVo queryVo);

      最後在UserMapperTest單元測試類編寫如下測試方法:

        
        
        @Test public void testGetUserByQueryVo() { SqlSession sqlSession = sqlSessionFactory.openSession(); // 獲得mapper代理對象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 創建一個QueryVo對象 QueryVo queryVo = new QueryVo(); User user = new User(); user.setId(10); queryVo.setUser(user); // 執行查詢 User result = userMapper.getUserByQueryVo(queryVo); System.out.println(result); sqlSession.close(); }

        傳遞HashMap

        傳遞HashMap在實際開發中用的很少,但我還是要講一下。以例明示——傳遞HashMap綜合查詢用戶信息,在UserMapper.xml映射文件中添加如下配置信息:

          
          
          <!-- 傳遞HashMap綜合查詢用戶信息 --> <select id="findUserByHashmap" parameterType="hashmap" resultType="user"> select * from user where id=#{id} and username like '%${username}%' </select>

          上面的id和username是HashMap的key。
          接着在UserMapper接口中添加如下方法:

            
            
          • 1
          User findUserByHashmap(HashMap<String, Object> map);

          最後在UserMapperTest單元測試類編寫如下測試方法:

            
            
            @Test public void testFindUserByHashmap() { SqlSession sqlSession = sqlSessionFactory.openSession(); // 獲得mapper代理對象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 構造查詢條件Hashmap對象 HashMap<String, Object> map = new HashMap<String, Object>(); map.put("id", 30); map.put("username", "趙雲"); // 執行查詢 User result = userMapper.findUserByHashmap(map); System.out.println(result); sqlSession.close(); }

            resultType(輸出類型)

            輸出簡單類型

            有這樣一個需求:查詢用戶表中的記錄數。有需求就要解決它,我們首先在UserMapper.xml映射文件中添加如下配置信息:

              
              
              <!-- 查詢用戶表中的記錄數 --> <select id="getUserCount" resultType="int"> SELECT COUNT(*) FROM `user` </select>

              接着在UserMapper接口中添加如下方法:

                
                
              • 1
              Integer getUserCount();

              最後在UserMapperTest單元測試類編寫如下測試方法:

                
                
                @Test public void testGetUserCount() { SqlSession sqlSession = sqlSessionFactory.openSession(); // 獲得mapper代理對象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 執行查詢 int count = userMapper.getUserCount(); System.out.println(count); sqlSession.close(); }

                輸出簡單類型必須查詢出來的結果集只有一條記錄,最終將第一個字段的值轉換爲輸出類型。

                輸出pojo對象

                輸出pojo對象,我之前已講過,這裏只給出案例,如下:
                這裏寫圖片描述

                輸出pojo列表

                輸出pojo列表,我之前同樣已講過,這裏只給出案例,如下:
                這裏寫圖片描述
                這兒再給出一個案例——查詢訂單的所有信息。我們mybatis數據庫中已經有了訂單表(orders表)了,如下:
                這裏寫圖片描述
                大家可能想到不會想,就會像下面這樣做。首先在cn.itheima.mybatis.po包下新建一個Orders類:

                  
                  
                  public class Orders { private Integer id; private Integer userId; private String number; private Date createtime; private String note; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number == null ? null : number.trim(); } public Date getCreatetime() { return createtime; } public void setCreatetime(Date createtime) { this.createtime = createtime; } public String getNote() { return note; } public void setNote(String note) { this.note = note == null ? null : note.trim(); } }

                  然後在cn.itheima.mybatis.mapper包下創建一個OrderMapper.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="cn.itheima.mybatis.mapper.OrderMapper"> <select id="getOrderList" resultType="orders"> select * from orders; </select> </mapper>

                    緊接着在cn.itheima.mybatis.mapper包下創建一個OrderMapper接口:

                      
                      
                      public interface OrderMapper { List<Orders> getOrderList(); }

                      最後創建OrderMapper接口的單元測試類——OrderMapperTest.java,修改OrderMapperTest類的內容爲:

                        
                        
                        public class OrderMapperTest { private SqlSessionFactory sqlSessionFactory = null; // 工廠對象一般在我們的系統中是單例的 @Before public void init() throws IOException { // 第一步,創建SqlSessionFactoryBuilder對象 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 第二步,加載配置文件 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); // 第三步,創建SqlSessionFactory對象 sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); } @Test public void testGetOrderList() { SqlSession sqlSession = sqlSessionFactory.openSession(); OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); List<Orders> orderList = orderMapper.getOrderList(); for (Orders orders : orderList) { System.out.println(orders); } sqlSession.close(); } }

                        在testGetOrderList方法中的以下代碼處打一個斷點:

                          
                          
                        • 1
                        for (Orders orders : orderList) {

                        然後以Debug模式運行該方法,可發現查詢出來的每一個Orders對象中的userId屬性值都爲null,如下:
                        這裏寫圖片描述
                        很明顯這並不是我們所想要的結果。爲了達到我們預期的效果,可爲user_id列加別名,即將OrderMapper.xml映射文件中id爲getOrderList的select元素修改爲:

                          
                          
                          <select id="getOrderList" resultType="orders"> select id,user_id userId,number,createtime,note from orders; </select>

                          只要你返回的結果的列名和pojo中的屬性一致,就可以自動映射了。
                          這樣當我們再次以Debug模式運行testGetOrderList方法,就能達到我們預期的結果了,如下:
                          這裏寫圖片描述
                          這種方式比較簡單粗暴,其實要達到我們所預期的效果,還有另一種方式,那就是使用resultMap這個屬性,下面我就會講到。

                          resultMap

                          resultMap可以指定pojo將查詢結果映射爲pojo,但需要pojo的屬性名和sql查詢的列名一致方可映射成功。如果sql查詢字段名和pojo的屬性名不一致,可以通過resultMap將字段名和屬性名作一個對應關係 ,resultMap實質上還需要將查詢結果映射到pojo對象中。
                          resultMap可以實現將查詢結果映射爲複雜類型的pojo,比如在查詢結果映射對象中包括pojo和list實現一對一查詢和一對多查詢。
                          現在我就來實現查詢訂單所有信息的需求,而不是像上面那樣簡單粗暴地給user_id列加別名。首先在OrderMapper.xml映射文件中添加如下<select>元素:

                            
                            
                            <select id="getOrderListResultMap" resultMap="order_list_result_map"> select id,user_id,number,createtime,note from orders; </select>

                            使用resultMap指定上邊定義的order_list_result_map。
                            接着定義resultMap。由於上邊的OrderMapper.xml映射文件中sql查詢列和Orders.java類屬性不一致,因此需要定義resultMap:order_list_result_map將sql查詢列和Orders.java類屬性對應起來。

                              
                              
                              <resultMap type="orders" id="order_list_result_map"> <!-- id是主鍵的映射,其中property是pojo中主鍵的屬性,column是返回結果中主鍵的列 --> <id property="id" column="id" /> <!-- 普通列使用result映射 --> <result property="userId" column="user_id" /> <result property="number" column="number" /> <result property="createtime" column="createtime" /> <result property="note" column="note" /> </resultMap>
                              • type:指resultMap要映射成的數據類型(返回結果映射的pojo,可以使用別名)。
                              • <id />:此屬性表示查詢結果集的唯一標識,非常重要。如果是多個字段爲複合唯一約束則定義多個<id />
                              • property:表示Orders類的屬性。
                              • column:表示sql查詢出來的字段名。
                                column和property放在一塊兒表示將sql查詢出來的字段映射到指定的pojo類屬性上。
                              • <result />:普通列使用result標籤映射。

                              然後在OrderMapper接口添加如下方法:

                                
                                
                              • 1
                              List<Orders> getOrderListResultMap();

                              最後在OrderMapperTest單元測試類中添加如下測試方法:

                                
                                
                                @Test public void testGetOrderListResultMap() { SqlSession sqlSession = sqlSessionFactory.openSession(); OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); List<Orders> orderList = orderMapper.getOrderListResultMap(); for (Orders orders : orderList) { System.out.println(orders); } sqlSession.close(); }

                                同樣在testGetOrderListResultMap方法中的以下代碼處打一個斷點:

                                  
                                  
                                • 1
                                for (Orders orders : orderList) {

                                然後以Debug模式運行該方法,可發現查詢出來的每一個Orders對象中的userId屬性都有值了。

                                動態sql

                                我們可通過mybatis提供的各種標籤方法實現動態拼接sql。

                                if

                                現有這樣一個需求:傳遞pojo類綜合查詢用戶信息,更具體地說就是我們使用用戶的id和username能更加靈活地查詢用戶信息。
                                爲了解決這個需求,我們就要使用<if>標籤了。首先在UserMapper.xml映射文件中添加如下<select>元素:

                                  
                                  
                                  <select id="findUserList" parameterType="user" resultType="user"> select * from user where 1=1 <if test="id!=null"> and id=#{id} </if> <if test="username!=null and username!=''"> and username like '%${username}%' </if> </select>

                                  注意:

                                  1. username要做不等於空字符串的校驗。
                                  2. User類中id屬性的類型要改爲Integer包裝類型,因爲int類型的id是不可能爲null的!

                                  然後在UserMapper接口添加如下方法:

                                    
                                    
                                  • 1
                                  List<User> findUserList(User user);

                                  最後在UserMapperTest單元測試類中添加如下測試方法:

                                    
                                    
                                    @Test public void testFindUserList() { SqlSession sqlSession = sqlSessionFactory.openSession(); // 獲得mapper代理對象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 設置查詢條件 User user = new User(); // user.setId(10); user.setUsername("張"); // 執行查詢 List<User> userList = userMapper.findUserList(user); for (User user2 : userList) { System.out.println(user2); } sqlSession.close(); }

                                    讀者可試着給User對象只爲id屬性賦值,或者只爲username屬性賦值,又或者兩者同時賦值。

                                    where

                                    UserMapper.xml映射文件中如下<select>元素:

                                      
                                      
                                      <select id="findUserList" parameterType="user" resultType="user"> select * from user where 1=1 <if test="id!=null"> and id=#{id} </if> <if test="username!=null and username!=''"> and username like '%${username}%' </if> </select>

                                      也可使用<where>標籤寫爲:

                                        
                                        
                                        <select id="findUserList" parameterType="user" resultType="user"> select * from user <where> <if test="id!=null"> and id=#{id} </if> <if test="username != null and username != ''"> and username like '%${username}%' </if> </where> </select>

                                        <where />可以自動處理第一個and。

                                        foreach

                                        現有這樣一個需求:傳入多個id查詢用戶信息。如若編寫sql語句,可用下邊兩個sql實現:

                                        1. SELECT * FROM USER WHERE username LIKE '%張%' AND (id =10 OR id =89 OR id=16)
                                        2. SELECT * FROM USER WHERE username LIKE '%張%' id IN (10,89,16)

                                        爲了解決這個需求,首先在QueryVo類中定義List屬性ids存儲多個用戶id,並添加getter/setter方法,如下:

                                          
                                          
                                          public class QueryVo { private User user; private List<Integer> ids; public List<Integer> getIds() { return ids; } public void setIds(List<Integer> ids) { this.ids = ids; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } }

                                          然後在UserMapper.xml映射文件中添加如下<select>元素:

                                            
                                            
                                            <!-- 動態sql foreach測試 --> <select id="findUserByIds" parameterType="queryvo" resultType="user"> SELECT * FROM `user` <where> <!-- and id IN(1,10,20,21,31) --> <foreach collection="ids" item="id" open="and id in(" close=")" separator=","> #{id} </foreach> </where> </select>

                                            向sql中傳遞數組或List,mybatis將使用foreach解析。
                                            接着在UserMapper接口添加如下方法:

                                              
                                              
                                            • 1
                                            List<User> findUserByIds(QueryVo queryVo);

                                            最後在UserMapperTest單元測試類中添加如下測試方法:

                                              
                                              
                                              @Test public void testFindUserByIds() { SqlSession sqlSession = sqlSessionFactory.openSession(); // 獲得mapper代理對象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 設置查詢條件 QueryVo queryVo = new QueryVo(); List<Integer> ids = new ArrayList<Integer>(); ids.add(1); ids.add(10); ids.add(16); ids.add(22); queryVo.setIds(ids); // 執行查詢 List<User> userList = userMapper.findUserByIds(queryVo); for (User user2 : userList) { System.out.println(user2); } sqlSession.close(); }

                                              sql片段

                                              sql中可將重複的sql提取出來,使用時用include引用即可,最終達到sql重用的目的,如下:

                                                
                                                
                                                <select id="findUserList" parameterType="user" resultType="user"> select * from user <where> <if test="id!=null"> and id=#{id} </if> <if test="username != null and username != ''"> and username like '%${username}%' </if> </where> </select>

                                                將where條件抽取出來,同時我們也可將要查詢的字段抽取出來。

                                                  
                                                  
                                                  <sql id="find_user_list_where"> <where> <if test="id!=null"> and id=#{id} </if> <if test="username != null and username != ''"> and username like '%${username}%' </if> </where> </sql> <sql id="user_field_list"> id,username,birthday,sex,address </sql>

                                                  使用include引用:

                                                    
                                                    
                                                    <select id="findUserList" parameterType="user" resultType="user"> select <include refid="user_field_list"/> from user <include refid="find_user_list_where"/> </select>

                                                    注意:如果引用其它mapper.xml映射文件的sql片段,則在引用時需要加上namespace,如下:

                                                      
                                                      
                                                    • 1
                                                    <include refid="namespace.sql片段"/>
                                                    <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-778f64ae39.css" rel="stylesheet">
                                                                </div>