摘自:https://blog.csdn.net/xzm_rainbow/article/details/15336933
1,案例一:產生問題
客戶(Customer表)和訂單(Order表)之間的關係是一對多的關係,即一個用戶可以有多個訂單。
(1)建立表,並建立一對多關聯。
主表
-
create table Customer(
-
id int primary key,
-
name varchar(32)
-
);
從表,customer_id是指向Customer表的主鍵id的外鍵
-
create table `order`(
-
id int primary key,
-
name varchar(32),
-
customer_id int
-
);
建立主外鍵關係
alter table `order` add constraint foreign key(customer_id) references customer(id);
向兩個表中插入數據:
Customer
Order
(2)建立Javabean,Customer和Order
Customer
-
public class Customer {
-
private Integer id;
-
private String name;
-
private List<Order> orderList = new ArrayList<Order>();
-
………………getter and setter ……………………
-
}
Order
-
public class Order {
-
private Integer id;
-
private String name;
-
private Customer customer;
-
……………………getter and setter ………………………
-
}
(3)建立Customer的Mapper.xml配置文件CustomerMapper.xml,編寫配置文件:
-
-
-
-
-
-
<mapper namespace="cn.itcast.test1.domain">
-
<!-- 建立一對多關係,多表查詢解析 -->
-
<!-- 配置結果映射,解析結果集,id爲定義的名稱,指向SQL的返回結果類型 -->
-
<resultMap type="cn.itcast.test1.domain.Customer" id="customerTest">
-
<id property="id" column="id" />
-
<result property="name" column="name" />
-
<!-- 一對多配置,建立集合,集合中存放多表對應的對象 -->
-
<!-- property是實體類中的集合的名字,ofType是集合中存放的對象的全限定名稱 -->
-
<collection property="orderList" ofType="cn.itcast.test1.domain.Order">
-
<!-- id用於定義主表的唯一標識,是從表識別主表主鍵的唯一標識,column是結果集對應的字段名 -->
-
<id property="id" column="id" />
-
<!-- result是普通屬性,其他解釋同上 -->
-
<result property="name" column="name" />
-
</collection>
-
</resultMap>
-
-
<!-- 配置resultType,則每返回一條結果類型,就建立一個對象 -->
-
<!-- 配置resultMap,則會將返回的結果集進行解析,對應上面的resultMap解析器,根據定義的字段產生實體對象 -->
-
<select id="getinfo" parameterType="int" resultMap="customerTest">
-
select * from customer c,`order` o where c.id = o.customer_id and c.id=#{id}
-
</select>
-
</mapper>
(4)在mybatis-config.xml中配置mapper的映射
-
<mappers>
-
<mapper resource="cn/itcast/domain/UserMapper.xml" />
-
<mapper resource="cn/itcast/test1/domain/CustomerMapper.xml"/>
-
</mappers>
(5)建立測試類,TestCustomer.java,測試查詢
-
public class TestCustomer {
-
//MyBatis的Session工廠,底層是jdbc實現,
-
private SqlSessionFactory factory;
-
-
//在初始化方法中初始化配置文件,並構建工廠
-
public void init() throws IOException {
-
//讀取核心配置文件,這個配置文件配置了數據庫的相關信息以及映射的mapper配置文件的讀取配置
-
String resource = "mybatis-config.xml";
-
//通過inputstream將配置文件讀取到內存中,用於構建session工廠,
-
//Resources類封裝的是類加載器,通過類加載器的getResourceAsStream獲取輸入流。
-
InputStream inputStream = Resources.getResourceAsStream(resource);
-
//通過build方法構建工廠對象
-
factory = new SqlSessionFactoryBuilder().build(inputStream);
-
}
-
-
-
public void findCustomer() {
-
//獲取session對象,session對象底層通過jdbcAPI實現,通過反射的方式,獲取配置文件配置的類信息以及方法
-
SqlSession session = factory.openSession();
-
//CURD其實是使用jdbc的ResultSet結果集實現,
-
Customer customer = session.selectOne("cn.itcast.test1.domain.getinfo", 1);
-
System.out.println(customer);
-
//記得釋放資源
-
session.close();
-
}
-
}
(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文件即可。
具體如下:
-
<mapper namespace="cn.itcast.test1.domain">
-
<resultMap type="cn.itcast.test1.domain.Customer" id="customerTest">
-
<id property="id" column="id" />
-
<result property="name" column="name" />
-
<collection property="orderList" ofType="cn.itcast.test1.domain.Order">
-
<id property="id" column="o_id" />
-
<result property="name" column="o_name" />
-
</collection>
-
</resultMap>
-
<select id="getinfo" parameterType="int" resultMap="customerTest">
-
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}
-
</select>
-
</mapper>
這樣所有的字段就都不一樣了,可以獲取正確結果,通過junit執行,可以獲取如下控制檯信息:
可以看到,orderList中有兩條數據,且正是我們設置到數據庫的數據。