MyBatis:一對多表關係詳解(從案例中解析)

摘自:https://blog.csdn.net/xzm_rainbow/article/details/15336933

1,案例一:產生問題

客戶(Customer表)和訂單(Order表)之間的關係是一對多的關係,即一個用戶可以有多個訂單。

(1)建立表,並建立一對多關聯。

主表

 

  1.  
    create table Customer(
  2.  
    id int primary key,
  3.  
    name varchar(32)
  4.  
    );

 

從表,customer_id是指向Customer表的主鍵id的外鍵

  1.  
    create table `order`(
  2.  
    id int primary key,
  3.  
    name varchar(32),
  4.  
    customer_id int
  5.  
    );

建立主外鍵關係

alter table `order` add constraint foreign key(customer_id) references customer(id);

 

向兩個表中插入數據:

Customer

Order

(2)建立Javabean,Customer和Order

Customer

  1.  
    public class Customer {
  2.  
    private Integer id;
  3.  
    private String name;
  4.  
    private List<Order> orderList = new ArrayList<Order>();
  5.  
    ………………getter and setter ……………………
  6.  
    }

Order

  1.  
    public class Order {
  2.  
    private Integer id;
  3.  
    private String name;
  4.  
    private Customer customer;
  5.  
    ……………………getter and setter ………………………
  6.  
    }

(3)建立Customer的Mapper.xml配置文件CustomerMapper.xml,編寫配置文件:

  1.  
    <?xml version="1.0" encoding="UTF-8"?>
  2.  
    <!DOCTYPE mapper
  3.  
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4.  
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5.  
     
  6.  
    <mapper namespace="cn.itcast.test1.domain">
  7.  
    <!-- 建立一對多關係,多表查詢解析 -->
  8.  
    <!-- 配置結果映射,解析結果集,id爲定義的名稱,指向SQL的返回結果類型 -->
  9.  
    <resultMap type="cn.itcast.test1.domain.Customer" id="customerTest">
  10.  
    <id property="id" column="id" />
  11.  
    <result property="name" column="name" />
  12.  
    <!-- 一對多配置,建立集合,集合中存放多表對應的對象 -->
  13.  
    <!-- property是實體類中的集合的名字,ofType是集合中存放的對象的全限定名稱 -->
  14.  
    <collection property="orderList" ofType="cn.itcast.test1.domain.Order">
  15.  
    <!-- id用於定義主表的唯一標識,是從表識別主表主鍵的唯一標識,column是結果集對應的字段名 -->
  16.  
    <id property="id" column="id" />
  17.  
    <!-- result是普通屬性,其他解釋同上 -->
  18.  
    <result property="name" column="name" />
  19.  
    </collection>
  20.  
    </resultMap>
  21.  
     
  22.  
    <!-- 配置resultType,則每返回一條結果類型,就建立一個對象 -->
  23.  
    <!-- 配置resultMap,則會將返回的結果集進行解析,對應上面的resultMap解析器,根據定義的字段產生實體對象 -->
  24.  
    <select id="getinfo" parameterType="int" resultMap="customerTest">
  25.  
    select * from customer c,`order` o where c.id = o.customer_id and c.id=#{id}
  26.  
    </select>
  27.  
    </mapper>

(4)在mybatis-config.xml中配置mapper的映射

  1.  
    <mappers>
  2.  
    <mapper resource="cn/itcast/domain/UserMapper.xml" />
  3.  
    <mapper resource="cn/itcast/test1/domain/CustomerMapper.xml"/>
  4.  
    </mappers>

 

(5)建立測試類,TestCustomer.java,測試查詢

  1.  
    public class TestCustomer {
  2.  
    //MyBatis的Session工廠,底層是jdbc實現,
  3.  
    private SqlSessionFactory factory;
  4.  
    @Before
  5.  
    //在初始化方法中初始化配置文件,並構建工廠
  6.  
    public void init() throws IOException {
  7.  
    //讀取核心配置文件,這個配置文件配置了數據庫的相關信息以及映射的mapper配置文件的讀取配置
  8.  
    String resource = "mybatis-config.xml";
  9.  
    //通過inputstream將配置文件讀取到內存中,用於構建session工廠,
  10.  
    //Resources類封裝的是類加載器,通過類加載器的getResourceAsStream獲取輸入流。
  11.  
    InputStream inputStream = Resources.getResourceAsStream(resource);
  12.  
    //通過build方法構建工廠對象
  13.  
    factory = new SqlSessionFactoryBuilder().build(inputStream);
  14.  
    }
  15.  
     
  16.  
    @Test
  17.  
    public void findCustomer() {
  18.  
    //獲取session對象,session對象底層通過jdbcAPI實現,通過反射的方式,獲取配置文件配置的類信息以及方法
  19.  
    SqlSession session = factory.openSession();
  20.  
    //CURD其實是使用jdbc的ResultSet結果集實現,
  21.  
    Customer customer = session.selectOne("cn.itcast.test1.domain.getinfo", 1);
  22.  
    System.out.println(customer);
  23.  
    //記得釋放資源
  24.  
    session.close();
  25.  
    }
  26.  
    }

 

(6)通過junit運行findCustomer,

結果如下:

使用我們的sql語句

select * from customer c,`order` owhere c.id = o.customer_id and c.id=1;

在數據庫查詢,結果是:

案例一的問題:

存入集合的對象應該是order對象,但是裏面的數據卻是customer的數據,爲什麼呢?

這涉及到ResultMap的實現機制。ResultMap是把實體類也就是我們的javabean的屬性與返回結果集的字段進行匹配,如果匹配,則建立對象,並通過設個對象的這個屬性的set方法將值設置進去。

詳解:

ResultMap接收的結果集信息,當前的返回的結果集信息也就是上面通過sql語句在數據庫查詢出來的圖上所示的信息。ResultMap會從返回的結果集逐條讀取記錄(每一行數據),當前獲取第一行數據:



 

每次獲取一行後,又會逐列讀取信息。讀取到id字段,值爲1。根據配置文件

<resultMaptype="cn.itcast.test1.domain.Customer"id="customerTest">,發現這是一個Customer對象,就new一個Customer對象,然後再將結果集中的字段值id與column屬性值逐一匹配,<id property="id"column="id" />匹配,就通過Customer的set方法將id的值1設置進id屬性;然後對結果集這一列的匹配並沒有結束,ResultMap會繼續拿着這個id字段向下匹配,下面一個配置是<resultproperty="name" column="name" />,不匹配,則不做任何動作;繼續向下匹配,發現<collection property="orderList"ofType="cn.itcast.test1.domain.Order">建立了一對多關係,實體是Order,於是就建立了一個Order對象,然後繼續拿着id字段向下匹配,接着發現在Order中也有id,<id property="id"column="id" />匹配了,再用Order的serId方法將id字段的值1設置進去,所以現在List集合中的一個Order對象的id值是1。繼續拿着id向下匹配,<resultproperty="name" column="name" />,不匹配,且配置結束,則結束這個id字段的配置。

進入下一個name字段的配置,與id一樣,進去匹配,當前記錄Customer的name還沒有通過set方法賦值,就set進去。後面的Order也是一樣。

至此,結果集中建立的對象包括:一個id=1,name=小強的Customer對象,一個id=1,name=小強的Order對象,並且Order對象存儲在Customer對象的List集合中。

進行下一個結果集字段的匹配,下一個也是id,同樣進入配置文件逐一匹配,需要注意的是,如果之前有一個與當前字段id相同的字段,在同一條記錄中對同一個對象做了set操作,那麼當前這個id字段將不會再對當前對象做set操作。

也就是說,當前的字段是id,這個id在配置文件中匹配的時候,遍歷到column爲id的列,就要通過創建的當前對象Customer的setId()方法設值,但是,在同一條記錄中,之前有一個字段相同(同爲id)的字段已經通過這個Customer對象的serId()方法設值,那麼當前這個id字段就不會再set賦值,所以,這個id匹配過後,Customer的id值不變,Order的值也不變,後面的name字段同理,所以,集合中的Order的id是1,name是小強。

 

另外,執行完畢後,集合裏面只有一條記錄,而查詢出來的是兩條記錄。因爲查詢的是Customer,而且是通過selectOne查詢的,MyBatis根據返回的記錄數判斷,通過selectOne查詢,返回必須是沒有或者只有一條,如果超過一條,就會報錯。但是通過selectList查詢,就不會出現這個問題了。

如果查詢的是Order,因爲order表中有兩條記錄,所以查詢結果是兩條,如果通過selectOne查詢,會報異常,必須通過selectList查詢。

 

2,案例二:解決了案例一中的問題

爲了解決案例一中的問題,需要修改一下我們的sql語句。通過上面的解析可以知道,如果字段名重複,則後面的字段不會再通過set方法設值。所以解決辦法就是如果出現重複字段,就換個名稱,讓所有的字段不一樣即可。

具體方案:

查詢語句中,爲字段建立別名,通過別名返回結果集(爲重複的字段建立別名)。只需要修改CustomerMapper.xml文件即可。

具體如下:

 

  1.  
    <mapper namespace="cn.itcast.test1.domain">
  2.  
    <resultMap type="cn.itcast.test1.domain.Customer" id="customerTest">
  3.  
    <id property="id" column="id" />
  4.  
    <result property="name" column="name" />
  5.  
    <collection property="orderList" ofType="cn.itcast.test1.domain.Order">
  6.  
    <id property="id" column="o_id" />
  7.  
    <result property="name" column="o_name" />
  8.  
    </collection>
  9.  
    </resultMap>
  10.  
    <select id="getinfo" parameterType="int" resultMap="customerTest">
  11.  
    select c.*,o.id as o_id,o.name as o_name from customer c,`order` o where c.id = o.customer_id and c.id=#{id}
  12.  
    </select>
  13.  
    </mapper>

 

 

這樣所有的字段就都不一樣了,可以獲取正確結果,通過junit執行,可以獲取如下控制檯信息:

 

可以看到,orderList中有兩條數據,且正是我們設置到數據庫的數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章