mybatis中many2one

來源:互聯網
上載者:User

標籤:style   java   color   使用   資料   os   

上一章我們講到,如果說hibernate是物件導向為主,關係為輔,那麼在mybatis中則是著重考慮的是關聯式模式,換句話說,如果物件模型設計的不好,就會很容易的感覺到實現的難度。

首先來看看最簡單的單向many2one:
建立對象:
public class Customer {
    private Long id;
    private String name;
}

public class Orders {
    private Long id;
    private String sn;
    private Double price;
    private Customer customer;
}
很簡單,Orders和Customer是一個單向的many2one的關係。和hibernate一樣,我們先來配置簡單的customer:
建立CustomerMapper.xml:
<mapper namespace="cd.itcast.mybatis.customer">
    <insert id="save" keyProperty="id" parameterType="Customer"
    useGeneratedKeys="true">
        INSERT INTO customer(name) values (#{name})
    </insert>

    <select id="get" resultType="Customer" parameterType="int">
        SELECT * FROM customer WHERE id = #{id}
    </select>
</mapper>
只是一個很簡單的單對象映射操作。
在hibernate的many2one中我們知道,在many方的外部索引鍵關聯到one方的主鍵。在many方儲存的時候,應該在對應外鍵儲存關聯one方的id。所以,many方的儲存insert應該是這樣:
OrdersMapper.xml:
<insert id="save" keyProperty="id" parameterType="Orders"
useGeneratedKeys="true">
    INSERT INTO orders(sn,price,customer_id) values (#{sn},#{price},#{customer.id})
</insert>
在這裡看到,我們在對應外鍵(customer_id)的地方,使用#{customer.id}來代表orders的customer屬性的id屬性,mybatis會像EL那樣自動幫我們得到值。所以,在這裡一定要注意儲存對象的順序,必須是先儲存one再儲存many方。在hibernate中,如果儲存順序有誤,hibernate會在提交事務的時候同步髒資料,幫我們補一條update語句來保證對象的關係正確,但是在mybaits中,所有的sql都由我們來完成,所以沒人會幫我們來完成update。所以,對應的儲存測試應該是:
@Test
public void testSave(){
    SqlSession session=MyBatisUtil.openSession();

    Customer c=new Customer();
    c.setName("itcasT");

    Orders o=new Orders();
    o.setPrice(800d);
    o.setSn("001");
    o.setCustomer(c);

    session.insert("cd.itcast.mybatis.customer.save",c);
    session.insert("cd.itcast.mybatis.orders.save",o);

    session.commit();
    session.close();
}

下面就是many方的擷取了。如果只是簡單的把orders配置成一個單對象,即使用resultType來映射:
<select id="get" parameterType="long" resultType="Orders">
    SELECT * FROM orders WHERE o.id = #{id}
</select>
如果是這樣,在測試的時候:
@Test
public void testGet(){
    SqlSession session=MyBatisUtil.openSession();
    Orders o=session.selectOne("cd.itcast.mybatis.orders.get", 1l);
    System.out.println(o);
    Customer c=o.getCustomer();
    System.out.println(c);
    session.close();
}
會看到,orders對應的customer是null,只能說明mybatis不能幫我們自動完成相關的關係映射。下面就來看下在mybatis中的關係映射:
第一種方式:使用額外的sql:
<select id="get" parameterType="int" resultMap="ordersmap">
     SELECT * FROM orders WHERE o.id = #{id}
</select>
在這裡,添加resultMap來完成相關的映射:
<resultMap type="Orders" id="ordersmap">
    <id property="id" column="id"/>
    <result property="sn" column="sn"/>
    <result property="price" column="price"/>
    <association property="customer" column="customer_id" javaType="Customer" 
    select="cd.itcast.mybatis.customer.get">
</resultMap>
可能第一次看這個配置會覺得很奇怪,其實很好理解:
1,association:在mybatis中不像hibernate那樣,把關係分的很細緻,比如one-to-one,many-to-one,而只是把關係分成了兩種:第一種是對單對象的關係,就用association,一種是集合的關係,這個待會再看。因為這裡orders只是把customer對象作為自己的一個屬性,所以使用association來表示這個關係;
2,property:和hibernate一樣,定義這個關係在orders對象上面的對應屬性。在完成關聯對象的映射後,會使用這個屬性把對象設定到orders對象上;
3,column:代表從查詢結果集(select * from orders where id = ?)中得到對關係維護的列,這裡就指明的是外鍵customer_id;
4,javaType:代表完成映射後應該返回的物件類型,這裡肯定就是Orders
5,select:是這種映射方式的最關鍵的地方,這個select指代著cd.itcast.mybatis.customer.get這條SQL,這條sql即是得到customer的sql,而這條sql需要傳入一個customer的id,我們前面已經通過column="customer_id"指定了應該傳給cd.itcast.mybatis.customer.get的值就是結果集中的customer_id。
所以,可以看出,mybatis在這種映射的方式為:首先執行SELECT * FROM orders WHERE o.id = #{id},然後對id,sn,price屬性直接從結果集中得到;接著,從結果集中得到customer_id列對應的值,這個值即是orders對應customer對象的id,然後把這個值作為id交給cd.itcast.mybatis.customer.get去執行,而這個執行的結果會被拼裝為一個Customer對象,接著這個對象再會被設定到orders的customer屬性中。完成映射。
再次執行get測試:
DEBUG [main] - ooo Using Connection [[email protected]]
DEBUG [main] - ==>  Preparing: SELECT * FROM ORDERS WHERE id = ? 
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <==    Columns: id, sn, cust_id
TRACE [main] - <==        Row: 1, 001, 1
DEBUG [main] - ooo Using Connection [[email protected]]
DEBUG [main] - ==>  Preparing: SELECT * FROM CUSTOMER WHERE id = ? 
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <==    Columns: id, name
TRACE [main] - <==        Row: 1, c
Orders [id=1, sn=001]
Customer [id=1, name=c]
可以看到,正常查詢得到結果。但是,可能會有點疑問,我們之前在學hibernate的時候,不是說消極式載入是一種比較好的處理對象的方式嗎?但是現在我們看到例子中的對象並沒有消極式載入。因為預設情況下,mybatis不會消極式載入對象:
要開啟消極式載入,需要給mybatis增加幾個配置,在mybatis-config.xml中:
<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>
再次執行get測試:得到的結果和剛才一樣,仍然感覺沒有執行消極式載入。問題在哪裡呢?其實mybatis已經完成了消極式載入,只是mybatis的消極式載入策略和hibernate的消極式載入策略不一樣。Hibernate是把被載入的目標對象做成proxy對象提供消極式載入(即這裡的Customer),而mybatis是把對象本身做成消極式載入對象,我們把get測試修改一下:
SqlSession session = MyBatisUtil.getInstance().openSession();
OrdersMapper om=session.getMapper(OrdersMapper.class);
Orders o=om.get(1l);
System.out.println(o.getClass());
session.close();
列印結果:
DEBUG [main] - ooo Using Connection [[email protected]]
DEBUG [main] - ==>  Preparing: SELECT * FROM ORDERS WHERE id = ? 
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <==    Columns: id, sn, cust_id
TRACE [main] - <==        Row: 1, 001, 1
class cd.itcast.mybatis.domain.Orders$$EnhancerByCGLIB$$39a00050
001
可以看到,orders對象卻是是proxy對象,並且訪問其他屬性,不會發送sql。但是,只要得到customer,那麼查詢customer的sql就會立刻發送。簡單來說,mybatis的消極式載入原則是,只要你去拿這個對象,表明你在用這個對象。注意和hibernate的區別。

第二種方式,也是mybatis推薦的方式。上面那種方式,容易產生N+1問題。所以,mybatis建議使用內聯的映射方式。修改ordersmapping:
<resultMap type="Orders" id="ordersmapper">
    <id property="id" column="id"/>
    <result property="sn" column="sn"/>
    <association property="customer" javaType="Customer">
        <id property="id" column="cid"/>
        <result property="name" column="cname"/>
    </association>
</resultMap>
<select id="get" resultMap="ordersmapper" parameterType="long">
    SELECT c.id as cid,c.name as cname,o.* FROM
ORDERS o LEFT JOIN CUSTOMER c ON o.CUST_ID = o.id WHERE o.id = #{id}
</select>
第一,在select中使用left join的方式把orders和對應的customer一起查詢出來,並且為customer設定對應的別名。
第二,修改resultMap,去掉select和column,而修改為內部的映射形式。
那麼,當mybatis執行完成sql之後,會把響應的屬性轉換成Orders對象的屬性,當解析到customer屬性時,mybatis會使用內部的映射關係把結果集中對應的列轉換成Customer對象。
再次運行get,執行結果相同。

再次說明,mybatis建議使用第二種方式來完成映射。有人又會考慮到,那麼如果只是orders的下拉式清單,在結果裡面不需要customer,這條sql不是很浪費效能嗎?這裡又要說到mybatis的另一個原則,按需配置。意思是,如果只是用作下拉式清單,那麼只需要再做一個select來專門為orders的下拉式清單做篩選。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.