MyBatis 高級映射

Mapper自動映射

MyBatis中,自動映射的三種模式:java

  • NONE 表示不啓用自動映射
  • PARTIAL 表示只對非嵌套的resultMap進行自動映射
  • FULL 表示對全部的resultMap都進行自動映射

默認的自動映射模式爲PARTIAL。
在MyBatis全局配置文件中,在setting標籤內設置自動映射模式:sql

<!--全局配置參數-->
<settings>
    <setting name="autoMappingBehavior" value="PARTIAL" />
</settings>

若是某些resultMap中不想使用自動映射,則能夠單獨在該resultMap中設置autoMapping屬性爲false:mybatis

<select id="findUserById" parameterType="Long" resultMap="UserResult" autoMapping="false">
    select id,name,email from t_user where id=#{id}
</select>

這裏autoMapping屬性會忽略全局配置文件中"outoMappingBehavior"映射模式。app

若是Java包裝類使用駝峯命名規則,則要開啓mapUnderscoreToCamelCase屬性,讓自動映射機制將SQL查詢出的非駝峯命名方式的字段名與Java包裝類中的屬性進行自動映射:測試

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

Mapper配置動態SQL語句

根據一些查詢條件來選擇不一樣的SQL語句:this

<select id="findUserList" parameterType="cn.com.mybatis.po.UserQueryVo" resultType="cn.com.mybatis.po.User">
    select * from user
    <where>
        <if test="UserQuertVo != null">
            <if test="UserQueryVo.gender != null and UserQueryVo.gender !=''">
                and user.sex=#{UserQueryVo.gender}
            </if>
            <if test="UserQueryVo.username != null and UserQueryVo.username != ''">
                and user.username like '%${UserQueryVo.username}%'
            </if>
        </if>
     </where>
</select>

當使用"<where>"標籤對包裹if條件語句時,將會忽略查詢條件中的第一個"and"。spa

將複用性比較強的SQL語句封裝成SQL片斷,但在SQL片斷中不支持動態SQL語句的<where>標籤:代理

<sql id="queryUserWhere">
    <!--要複用的SQL語句-->
</sql>

引用SQL映射配置:code

<select id="findUserList" parameterType="cn.com.mybatis.po.UserQueryVo" resultType="cn.com.mybatis.po.User">
    select * from user
    <where>
        <include refid="queryUserWhere"></include>
        <!--在這裏可能還要引用其餘的SQL片斷-->
     </where>
</select>

除了自身所在的Mapper文件,每一個SQL映射配置還能夠引用外部Mapper文件中的SQL片斷,只須要在refid屬性中填寫SQL片斷的id名添加所在Mapper文件的namespace信息便可。orm

有時查詢語句可能包含多個查詢信息,好比查詢多個id的User用戶:

select * from user where id in (2.4.6)

此時若是要在Mapper文件中配置這樣的語句,這裏定義一個Java包裝類,其屬性爲一個包含多個id信息的List集合:

public class UserQueryVo{
    private List<Integer> ids;//包含多個id信息
    public List<Integer> getIds(){
        return ids;
    }
    public void setIds(List<Integer> ids){
        this.ids = ids;
    }
}

在Mapper中配置一個SQL片斷,並在查詢SQL映射中引入它:

<sql id="queryUserWhere">
    <if test="ids!=null">
        <foreach collection="ids" item="user_id" open="and id in (" close=")" separator=",">
        #{user_id}
        </foreach>
    </if>
</sql> 

<select id="findUserList" parameterType="cn.com.mybatis.po.UserQueryVo" resultType="cn.com.mybatis.po.User">
    select * from user
    <where>
        <include refid="query_user_where"></include>
    </where>
</select>

在SQL片斷裏的"and"用來拼接已有一個或多個查詢條件的語句,當此語句爲第一個查詢條件時,會由於"<where>"的存在而屏蔽第一個"and"。

一對一查詢

在實現一對一查詢時,推薦使用resultType。
使用resultMap時,對映射輸出的數據須要單獨定義resultMap,過程有些繁瑣,若是對查詢結果有特殊的要求(好比JavaBean裏面又包含有其餘JavaBean)就可使用。

使用resultType實現

好比查詢一個購買批次的信息以及建立該批次的用戶,一個批次對應着一個用戶。批次表名爲batch。
首先建立批次表batch對應的Java實體類Batch:

package cn.com.mybatis.po;  
  
import java.util.Date;  
import java.util.List;  
public class Batch {  
    private int batch_id;  
    private int cus_id;  
    private String number;  
    private Date createtime;  
    private String note;    
    public int getBatch_id() {  
        return batch_id;  
    }  
    public void setBatch_id(int batch_id) {  
        this.batch_id = batch_id;  
    }  
    //其餘的get和set方法省略......
}

建立一個以batch爲父類,而後追加用戶信息:

package cn.com.mybatis.po;  
  
public class BatchCustomer extends Batch {  
    private String username;  
    private String acno;  
    public String getUsername() {  
        return username;  
    }  
    public void setUsername(String username) {  
        this.username = username;  
    }  
    public String getAcno() {  
        return acno;  
    }  
    public void setAcno(String acno) {  
        this.acno = acno;  
    }  
  
}

而後在UserMapper.xml映射文件中定義select類型的查詢語句:

<select id="findBatchCustomer" resultType="cn.com.mybatis.po.BatchCustomer">  
    SELECT  
    batch.*,customer.username,customer.acno  
    FROM batch INNER JOIN customer  
    ON batch.cus_id = customer.cus_id 
</select>

編寫測試方法:

@Test  
public void testBatchCustomer() throws Exception{  
    SqlSession sqlSession = dataConnection.getSqlSession();  
    List<BatchCustomer> batchCustomerList = sqlSession.selectList("test.findBatchCustomer");  
    if(batchCustomerList != null){  
        BatchCustomer batchCustomer = null;  
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
        for (int i=0;i<batchCustomerList.size();i++){  
            batchCustomer = batchCustomerList.get(i);  
            System.out.println("卡號爲" + batchCustomer.getAcno() + "的名爲" +batchCustomer.getUsername()+"的客戶:\\n於"+sdf.format(batchCustomer.getCreatetime()) +"採購了批次號爲"+batchCustomer.getNumber()+"的一批理財產品");  
        }  
    }  
    sqlSession.close();  
}

使用resultMap實現

使用resultMap能夠映射實體類中包裹的其餘實體類。
建立一個封裝了批次屬性和Customer實體類的BatchItem批次類:

package cn.com.mybatis.po;  
  
import java.util.Date;  
import java.util.List;  
public class BatchItem {  
    private int batch_id;  
    private int cus_id;  
    private String number;  
    private Date createtime;  
    private String note;  
    private Customer customer;   
    public int getBatch_id() {  
        return batch_id;  
    }  
    public void setBatch_id(int batch_id) {  
        this.batch_id = batch_id;  
    }  
}

SQL映射文件:

<!--一對一查詢,使用resultMap實現-->  
<resultMap id="BatchInfoMap" type="cn.com.mybatis.po.BatchItem">  
    <id column="batch_id" property="batch_id"/>  
    <result column="cus_id" property="cus_id"/>  
    <result column="number" property="number"/>  
    <result column="createtime" property="createtime" javaType="Date"/>  
    <result column="note" property="note"/>  
    <association property="customer" javaType="cn.com.mybatis.po.Customer">  
        <id column="cus_id" property="cus_id"/>  
        <result column="username" property="username"/>  
        <result column="acno" property="acno"/>  
        <result column="gender" property="gender"/>  
        <result column="phone" property="phone"/>  
    </association>  
</resultMap>  
  
<select id="findBatchCustomerToMap" resultMap="BatchInfoMap">  
    SELECT  
    batch.*,customer.username,customer.acno  
    FROM batch INNER JOIN customer  
    ON batch.cus_id = customer.cus_id 
</select>

編寫測試類:

@Test  
public void testBatchCustomerToMap() throws Exception{  
    SqlSession sqlSession = dataConnection.getSqlSession();  
    List<BatchItem> batchItemList = sqlSession.selectList("test.findBatchCustomerToMap");  
    if(batchItemList != null){  
        BatchItem batchItem = null;  
        Customer customer = null;  
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
        for (int i=0;i<batchItemList.size();i++){  
            batchItem = batchItemList.get(i);  
            customer = batchItem.getCustomer();  
            System.out.println("卡號爲" + customer.getAcno() + "的名爲" +customer.getUsername()+"的客戶:\\n於"+sdf.format(batchItem.getCreatetime()) +"採購了批次號爲"+batchItem.getNumber()+"的一批理財產品");  
        }  
    }  
}

一對多查詢

好比,查詢一批中一個用戶信息,和有哪些理財產品。
修改一下Batch類:

package cn.com.mybatis.po;  
  
import java.util.Date;  
import java.util.List;  
public class Batch {  
    private int batch_id;  
    private int cus_id;  
    private String number;  
    private Date createtime;  
    private String note;  
    //批次中包含的理財產品訂購信息
    private List<BatchDetail> batchDetials;  
    //get和set方法省略......
}

理財產品的訂購信息實體類以下:

package cn.com.mybatis.po;  
  
import java.util.List;  
  
public class BatchDetail {  
    private int id;  
    private int batch_id;  
    private int product_id;  
    private int product_num;  
    private FinacialProduct finacialProduct;  
    //get和set方法省略...... 
}

編寫SQL配置,使用extends標籤繼承上面一對一查詢中的名爲BatchInfoMap的resultMap:

<!--一對多-->  
<resultMap  
  id="BatchAndBatchDetailResultMap" type="cn.com.mybatis.po.BatchItem" extends="BatchInfoMap">  
    <collection  
  property="batchDetails" ofType="cn.com.mybatis.po.BatchDetail">  
        <!-- id:訂單明細的惟一標識 -->  
  <id column="id" property="id"/>  
        <result column="batch_id" property="batch_id"/>  
        <result column="product_id" property="product_id"/> 
        <result column="product_num" property="product_num"/>  
    </collection>  
</resultMap>  
  
<select id="findBatchAndBatchDetail" resultMap="BatchAndBatchDetailResultMap">  
    SELECT  
        batch.*,  
        customer.username,customer.acno,  
        batchdetail.product_id,  
        batchdetail.product_num  
    FROM ((batch  
    INNER JOIN customer  
    ON batch.cus_id = customer.cus_id)  
    INNER JOIN batchdetail  
    ON batch.batch_id = batchdetail.batch_id)  
</select>

測試查詢結果:

@Test  
public void testfindBatchAndBatchDetail() throws Exception{  
  
    SqlSession sqlSession=dataConnection.getSqlSession();  
    //調用userMapper的方法  
  BatchItem batchItem=  
            sqlSession.selectOne(  
                    "test" +  
                            ".findBatchAndBatchDetail");  
    if(batchItem!=null){  
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
        Customer customer = batchItem.getCustomer();//取出該批次的用戶信息  
  //取出該批次訂購的理財產品信息  
  List<BatchDetail> batchDetails = batchItem.getBatchDetails();  
        System.out.println("卡號爲"+customer.getAcno()+"的名爲"  
  +customer.getUsername()+"的客戶:\\n於"  
  +sdf.format(batchItem.getCreatetime())+"採購了批次號爲"  
  +batchItem.getNumber()+"的一批理財產品,詳情以下:");  
        BatchDetail batchDetail = null;  
        if(batchDetails!=null){  
            for (int i = 0; i < batchDetails.size(); i++) {  
                batchDetail = batchDetails.get(i);  
                System.out.println("id爲"+batchDetail.getProduct_id()  
                        +"的理財產品"+batchDetail.getProduct_num()+"份");  
            }  
        }  
    }  
    sqlSession.close();  
}

這裏使用了association與collocation,一個映射單一實體對象,一個映射集合對象。

多對多查詢

查詢全部用戶以及用戶對應的批次訂單中全部理財產品的詳細信息。
修改後的Customer用戶類,它包含用戶信息以及用戶下的全部批次信息:

package cn.com.mybatis.po;  
  
import java.io.Serializable;  
import java.util.List;  
  
public class Customer implements Serializable{  
    private int cus_id;  
    private String username;  
    private String acno;  
    private String gender;  
    private String phone;  
    private List<Batch> batchList;  
    //get和set方法省略
}

修改後的Batch批次信息類,它包含單個批次信息以及批次明細列表:

package cn.com.mybatis.po;  
  
import java.util.Date;  
import java.util.List;  
public class Batch {  
    private int batch_id;  
    private int cus_id;  
    private String number;  
    private Date createtime;  
    private String note;  
    private List<BatchDetail> batchDetials; 
    //get和set方法省略
}

修改後的BatchDetail批次明細類,它包含單個批次明細和對應的理財產品引用:

package cn.com.mybatis.po;  
  
import java.util.List;  
  
public class BatchDetail {  
    private int id;  
    private int batch_id;  
    private int product_id;  
    private int product_num;  
    private FinacialProduct  finacialProduct;  
    //get和set方法省略
}

新增的FinacialProduct產品明細類,包含理財產品的各類屬性:

package cn.com.mybatis.po;  
  
import java.util.Date;  
  
public class FinacialProduct {  
    private int id;  
    private String name;  
    private double price;  
    private String detail;  
    private String imgpath;  
    private Date invattime;  
    //get和set方法省略
  
}

編寫Mapper映射文件:

<resultMap  
  id="UserAndProductsResultMap" type="cn.com.mybatis.po.Customer">  
      <!-- 客戶信息 -->  
  <result column="username" property="username"/>  
      <result column="acno" property="acno"/>  
  
      <!--批次訂單信息,一個客戶對應多個訂單-->  
  <collection property="batchList" ofType="cn.com.mybatis.po.Batch">  
          <id column="batch_id" property="batch_id"/>  
          <result column="cus_id" property="cus_id"/>  
          <result column="number" property="number"/>  
          <result column="createtime" property="createtime" javaType="java.util.Date"/>  
          <result column="note" property="note"/>  
  
          <collection property="batchDetials" ofType="cn.com.mybatis.po.BatchDetail">  
              <!-- id:訂單明細的惟一標識-->  
  <id column="id" property="id"/>  
              <result column="batch_id" property="batch_id"/>  
              <result column="product_id" property="product_id"/>  
              <result column="product_num" property="product_num"/>  
  
              <association property="finacialProduct" javaType="cn.com.mybatis.po.FinacialProduct">  
                  <id column="product_id" property="id"/>  
                  <result column="name" property="name"/>  
                  <result column="price" property="price"/>  
                  <result column="detail" property="detail"/>  
              </association>  
          </collection>  
      </collection>  
  </resultMap>  
  <select id="findUserAndProducts" resultMap="UserAndProductsResultMap">  
      SELECT  
  BATCH.*,  
  CUSTOMER.username,  
  CUSTOMER.acno,  
  BATCHDETAIL.product_id,  
  BATCHDETAIL.product_num,  
  FINACIAL_PRODUCTS.name,  
  FINACIAL_PRODUCTS.detail,  
  FINACIAL_PRODUCTS.price  
FROM  
  BATCH,  
  CUSTOMER,  
  BATCHDETAIL,  
  FINACIAL_PRODUCTS  
WHERE BATCH.cus_id = CUSTOMER.cus_id  
AND BATCHDETAIL.batch_id=BATCH.batch_id  
AND FINACIAL_PRODUCTS.product_id=BATCHDETAIL.product_id;  
  </select>

延遲加載

在MyBatis中,一般會進行多表聯合查詢,可是有時候並不會當即用到全部的聯合查詢結果。這種「按需查詢」的機制,就可使用延遲加載來實現。

開啓延遲加載功能:

//在全局配置文件中配置setting屬性
<configuration>
    <settings>
        <!--打開延遲加載的開關-->
        <setting name="lazyLoadingEnable" value="true" />
        <!--將積極加載改成按需加載-->
        <setting name="aggressiveLazyLoading" value="false" />
    </settings>
</configuration>

例如查詢全部批次訂單的信息,而每一個批次訂單中會關聯查詢用戶,延遲加載用戶信息,首先定義只查詢全部批次訂單信息的SQL配置:

<select id="findBatchUserLazyLoading" resultMap="BatchUserLazyLoadingResultMap">
    select * from batch
</select>

<!--延遲加載的resultMap-->
<resultMap id="BatchUserLazyLoadingResultMap" type="cn.com.mybatis.po.BatchItem">
    <!--對訂單信息進行映射配置-->
    <id column="batch_id" property="batch_id" />
    <result column="cus_id" property="cus_id" />
    <result column="number" property="number" />
    <result column="createtime" property="createtime" javaType="java.util.Date" />
    <result column="note" property="note" />
    <!--實現延遲加載用戶信息-->
    <association property="customer" javaType="cn.com.mybatis.po.Customer" select="findCustomerById" column="cus_id">
    </association>
</resultMap>

其中select用來指定Mapper.xml配置文件中的某個select標籤對的id。
column是指訂單信息中關聯用戶信息查詢的列。

最後配置延遲加載要執行的獲取用戶信息的SQL:

<select id="findCustomerById" parameterType="int" resultType="cn.com.mybatis.po.Customer">
    select * from customer where cus_id=#{id}
</select>

其中輸入參數就是association中column中定義的字段信息。

編寫測試方法:

@Test  
    public void testFindBatchCustomerLazyLoading() throws Exception{  
          
        SqlSession sqlSession=dataConn.getSqlSession();
        //調用userMapper的方法,獲取全部訂單信息(未加載關聯的用戶信息)  
          List<BatchItem> batchItemList=sqlSession.selectList("findBatchUserLazyLoading"); 
          BatchItem batchItem = null;
          Customer customer = null;
        for (int i = 0; i < batchItemList.size(); i++) {  
            batchItem = batchItemList.get(i);  
            System.out.println("訂單編號:"+batchItem.getNumber());
            //執行getCustomer時纔會去查詢用戶信息,這裏實現了延遲加載  
            customer=batchItem.getCustomer();  
            System.out.println("訂購用戶姓名:"+customer.getUsername());  
        }  
        sqlSession.close();
    }

綜上所述,使用延遲加載方法,先執行簡單的查詢SQL(最好查詢單表,也能夠關聯查詢),再按須要加載關聯查詢的其餘信息。

Mapper動態代理

新增一個CustomerMapper.xml,例如對Customer的增、刪、改、查的SQL配置:

<?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.com.mybatis.mapper.CustomerMapper">    
    <!-- 查詢用戶 -->
    <select id="findCustomerById" parameterType="int" resultType="cn.com.mybatis.po.Customer">  
      SELECT * FROM CUSTOMER WHERE cus_id=#{cus_id}
    </select>  
    <!-- 新增用戶 -->   
    <insert id="insertCustomer" parameterType="cn.com.mybatis.po.Customer">    
        INSERT INTO CUSTOMER(username,acno,gender,phone) 
            value(#{username},#{acno},#{gender},#{phone})  
    </insert>
    <!-- 刪除用戶 -->  
    <delete id="deleteCustomer" parameterType="java.lang.Integer">  
        DELETE FROM CUSTOMER WHERE cus_id=#{cus_id}
    </delete>  
    <!-- 修改用戶 -->  
    <update id="updateCustomerAcNo" parameterType="cn.com.mybatis.po.Customer" >
        UPDATE CUSTOMER SET acno = #{acno} WHERE cus_id=#{cus_id}
    </update>
</mapper>

其中,namespace中的路徑爲即將建立的mapper代理接口的路徑。

使用CustomerMapper.xml的Mapper代理,建立CustomerMapper接口:

package cn.com.mybatis.mapper;

import cn.com.mybatis.po.Customer;
public interface CustomerMapper {

    public Customer findCustomerById(int id) throws Exception;  
      
    public void insertCustomer(Customer customer) throws Exception;  
      
    public void deleteCustomer(int id) throws Exception;  
      
    public void updateCustomerAcNo(Customer customer) throws Exception;  
}

測試動態代理效果:

@Test  
    public void testFindCustomerOnMapper() throws Exception{  
        SqlSession sqlSession=dataConn.getSqlSession();    
          
        //獲取Mapper代理  
        CustomerMapper customerMapper=sqlSession.getMapper(CustomerMapper.class);
        //執行Mapper代理對象的查詢方法
        Customer customer=customerMapper.findCustomerById(1);
        System.out.println("用戶姓名:"+customer.getUsername()+"|"
                +"卡號:"+customer.getAcno());
        sqlSession.close();
    }

其中SqlSession類的getMapper方法的原理就是,根據Mapper代理接口的類型及Mapper.xml文件的內容,建立一個Mapper接口的實現類,其中實現了具體的增刪改查方法。

相關文章
相關標籤/搜索