mybatis高級查詢【掌握】
1、準備工作
【1】包結構
創建java項目,導入jar包和log4j日志配置文件以及連接數據庫的配置文件;
【2】導入SQL腳本
運行資料中的sql腳本:mybatis.sql
【3】創建實體來包,導入資料中的pojo
【4】UserMapper接口
package com.itheima.sh.dao;
import com.itheima.sh.pojo.User;
public interface UserMapper {//完成根據id查詢用戶數據;User selectById(Long id);
}
【5】UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heima.mybatis.mapper.UserMapper"><!--根據id查詢:statement--><select id="selectById" resultType="User">SELECT * FROM tb_user WHERE id=#{id}</select></mapper>
【6】測試
package com.itheima.sh.test;import com.itheima.sh.dao.UserMapper;
import com.itheima.sh.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.BeforeClass;
import org.junit.Test;import java.io.IOException;
import java.io.InputStream;public class MybatisTest01 {private static UserMapper mapper = null;@BeforeClasspublic static void beforeClass() throws Exception {//1.構建SessionFactoryString resouce = "mybatis-config.xml";InputStream is = Resources.getResourceAsStream(resouce);SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);//2.獲取sessionSqlSession sqlSession = build.openSession(true);//3.獲取接口對象mapper = sqlSession.getMapper(UserMapper.class);}@Testpublic void selectById() {User user = mapper.selectById(1L);System.out.println(user);}
}
2、表介紹和表關系說明
導入資料中mybatis.sql腳本。新建以下4張表
tb_user:用戶表
tb_order:訂單表
tb_item:商品表
tb_orderdetail:訂單詳情表
【表關系】
1.tb_user和 tb_order表關系tb_user 《==》 tb_order:一對多, 一個人可以下多個訂單tb_order 《==》 tb_user:一對一,一個訂單只能屬于一個人結論:tb_user和tb_order屬于一對多的關系,需要將一方tb_user的主鍵作為多方tb_order的外鍵維護關系
2.tb_order 和 tb_item 表關系tb_order 《==》 tb_item :一個訂單可以有多個商品tb_item 《==》 tb_order:一個商品可以在多個訂單上結論:tb_order和tb_item屬于多對多的關系,需要創建中間表tb_orderdetail維護兩個表的關系,并且將兩張表 的主鍵作為中間表的外鍵
3、一對一查詢
需求:通過訂單編號20140921003查詢出訂單信息,并查詢出下單人信息。
【實現:關聯查詢】
【目標】使用多表關聯查詢,完成根據訂單號查詢訂單信息和下單人信息(訂單號:20140921003)
【分析】
一個訂單編號對應一個訂單,一個訂單只能屬于一個人。所以上述需求實現是一對一的實現。
【步驟】
1、首先,編寫接口方法。編寫SQL語句;
2、第二步:分析SQL,封裝數據(關聯對象);
3、處理多表之間的數據封裝(數據庫字段名---》實體類的屬性名之間的映射)
【實現】
第一步:需求分析
? 編寫多表關聯查詢SQL,根據訂單號查詢訂單信息及下單人信息;
查詢語句以及查詢結果:
#方式一:分步查詢#第一步:根據order_number查詢訂單信息;SELECT * FROM tb_order WHERE order_number = '20140921003';#第二步:根據訂單信息中的user_id查詢出下單人的信息;SELECT * FROM tb_user WHERE id = 1;#方式二:多表關聯查詢,內連接SELECT * FROM tb_order tbo inner join tb_user tbu on tbo.user_id = tbu.id where tbo.order_number='20140921003'#多表數據封裝問題:#關聯對象封裝數據(在Order中引用User)
第二步:添加關聯
修改Order:
? 在Order類中,添加關聯對象User,并添加getter和setter方法;
package com.itheima.sh.pojo;
/*** 訂單表* */
public class Order {private Integer id;private String orderNumber;//關聯User對象private User user;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getOrderNumber() {return orderNumber;}public void setOrderNumber(String orderNumber) {this.orderNumber = orderNumber;}public User getUser() {return user;}public void setUser(User user) {this.user = user;}@Overridepublic String toString() {return "Order{" +"id=" + id +", orderNumber='" + orderNumber + '\'' +", user=" + user +'}';}
}
第三步:添加方法
編寫OrderMapper接口
public interface OrderMapper {/*** 根據訂單號查詢訂單及下單人的信息:方式二* @param orderNumber* @return*/Order queryOrderAndUserByOrderNumber2(@Param("orderNumber")String orderNumber);
}
第四步:編寫SQL
在OrderMapper.xml中編寫對應的SQL,并將OrderMapper.xml加入到mybatis-config.xml全局配置中;
【OrderMapper.xml代碼;】
說明:
association:配置關聯對象(User)的映射關系<association property="user" javaType="User" autoMapping="true"></association>屬性:property:關聯對象在主表實體類中的屬性名;property="user" 表示在Order類中的引用的User類的對象 成員變量名javaType:關聯對象的類型;javaType="User" 表示引用的user對象屬于User類型
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
映射文件
namespace 指定接口的類全名
-->
<mapper namespace="com.itheima.sh.dao.OrderMapper"><!--1.autoMapping="true" 表示只需要給當前表的id然后自動映射當前表的其他列值到對應實體類的屬性中,這屬于偷懶行為,開發中我們最好都書寫出來2.id標簽表示id的映射關系3.result標簽表示其他列和pojo類的屬性映射關系4.一對一映射關系使用子標簽association來表示引用的另一個pojo類的對象--><resultMap id="orderAndUserResultRelative" type="Order" autoMapping="true"><!--主表主鍵--><id column="id" property="id"/><!--關聯關系--><!--1.property="user" 表示在Order類中的引用的User類的對象成員變量名2.javaType="User" 表示引用的user對象屬于User類型--><association property="user" javaType="User" autoMapping="true"><!--從表主鍵--><id column="id" property="id"/><!--<result column="user_name" property="userName"/>--></association></resultMap><!--多表關聯查詢:一對一--><select id="queryOrderAndUserByOrderNumber2" resultMap="orderAndUserResultRelative">SELECT*FROMtb_order tboINNER JOIN tb_user tbu ON tbo.user_id = tbu.idWHEREtbo.order_number = #{orderNumber}</select>
</mapper>
說明:
1、由于queryOrderAndUserByOrderNumber2查詢的結果Order對象中需要封裝User信息,所以返回值不能夠再使用單純的resultType來操作;2、定義resultMap進行關聯查詢的配置,其中:屬性:id:標識這個resultMap;type:返回的結果類型autoMapping="true": 表示只需要給當前表的id然后自動映射當前表的其他列值到對應實體類的屬性中,這 屬于偷懶行為,開發中我們最好都書寫出來子元素:id:主表主鍵映射result:主表普通字段的映射association:關聯對象的映射配置3、association:配置關聯對象(User)的映射關系屬性:property:關聯對象在主表實體類中的屬性名;property="user" 表示在Order類中的引用的User類的對象 成員變量名javaType:關聯對象的類型;javaType="User" 表示引用的user對象屬于User類型
第五步:測試
package com.itheima.sh.test;import com.itheima.sh.dao.OrderMapper;
import com.itheima.sh.dao.UserMapper;
import com.itheima.sh.pojo.Order;
import com.itheima.sh.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.InputStream;
public class MybatisTest02 {private static OrderMapper mapper = null;@BeforeClasspublic static void beforeClass() throws Exception {//1.構建SessionFactoryString resouce = "mybatis-config.xml";InputStream is = Resources.getResourceAsStream(resouce);SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);//2.獲取sessionSqlSession sqlSession = build.openSession(true);//3.獲取接口對象mapper = sqlSession.getMapper(OrderMapper.class);}@Testpublic void selectById() {Order order = mapper.queryOrderAndUserByOrderNumber2("20140921003");System.out.println("order = " + order);}
}
【測試結果】
注意事項
通過上述測試結果,我們發現User的id是錯誤的,不是3,正確結果是1:
因為tb_user表的主鍵是id,tb_order的主鍵也是id。查詢的結果中有兩列相同的id字段。在將查詢結果封裝到實體類的過程中就會封裝錯誤。
注意:user表查詢的是id不是id1,由于SQLyog圖形化界面顯示的原因。可以在cmd窗口查看結果:
【解決方案】
1、建議將所要查詢的所有字段顯示地寫出來;
2、將多表關聯查詢結果中,相同的字段名取不同的別名;
resultMap中應該如下配置:
【正確結果】
【小結】
一對一關聯查詢:
1、需要在Order實體類中關聯User對象;最終將數據封裝到Order中;
2、在OrderMapper.xml文件中書寫關聯語句并配置關系;
3、關聯關系配置:<resultMap id="orderAndUserResultRelative" type="Order" autoMapping="true"><!--主表主鍵--><id column="oid" property="id"/><!--關聯關系--><association property="user" javaType="User" autoMapping="true"><!--從表主鍵--><id column="uid" property="id"/></association></resultMap>
4、一對多查詢
【目標】查詢id為1的用戶及其訂單信息
【分析】
? 一個用戶可以有多個訂單。
? 一個訂單只能屬于一個用戶。
用戶(1)-----訂單(n)
【步驟】
第一步:查詢SQL分析;
第二步:添加關聯關系;
第三步:編寫接口方法;
第四步:編寫映射文件;
第五步:測試
【實現】
第一步:需求分析
編寫SQL實現查詢id為1的用戶及其訂單信息
查詢語句及查詢結果:
#查詢id為1的用戶及其訂單信息
select * from tb_user where id=1;
select * from tb_order where user_id=1;#一對多 內連接查詢
select * from tb_user tbu inner join tb_order tbo on tbu.id = tbo.user_id where tbu.id=1;
# 封裝數據:關聯對象,一個用戶關聯多個訂單 User(List<Order> orderList)
說明:一個用戶關聯多個訂單 User(List orderList) ,在User類中定義一個List集合存儲多個訂單Order對象。
第二步:添加映射關系
? 因為一個用戶可以擁有多個訂單,所以用戶和訂單是一對多的關系;需要在User類中添加一個List<Order>
屬性;
package com.itheima.sh.pojo;import java.io.Serializable;
import java.util.List;public class User implements Serializable {private Long id;// 用戶名private String userName;// 密碼private String password;// 姓名private String name;// 年齡private Integer age;//0 女性 1 男性private Integer sex;//訂單List<Order> orders;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Integer getSex() {return sex;}public void setSex(Integer sex) {this.sex = sex;}public List<Order> getOrders() {return orders;}public void setOrders(List<Order> orders) {this.orders = orders;}@Overridepublic String toString() {return "User{" +"id=" + id +", userName='" + userName + '\'' +", password='" + password + '\'' +", name='" + name + '\'' +", age=" + age +", sex=" + sex +", orders=" + orders +'}';}
}
第三步:編寫接口方法
在UserMapper接口中,添加關聯查詢;
/*** 根據用戶id查詢用戶及其訂單信息* @param id* @return*/User oneToManyQuery(@Param("id") Long id);
第四步:編寫SQL
? 在UserMapper.xml文件中編寫SQL語句完成一對多的關聯查詢;
說明:
1.一對多使用collection子標簽進行關聯多方Order<collection property="類中引用多方的成員變量名" javaType="存放多方容器的類型" ofType="多方類型" autoMapping="true"></collection>
2.屬性:1)property="orders" 這里的orders表示User類的成員變量orders2)javaType="List" 表示User類的成員變量orders存儲的Order對象使用的類型,這里是List 一般不書寫3) ofType="Order" 表示List集合中存儲數據的類型 Order
3.一定要記住這里給user表的id起別名是uid,order表的id起別名是oid.在resultMap標簽的id子標簽中的column屬性值書寫對應的uid和oid.
<!--自定義結果集--><resultMap id="oneToManyResult" type="User" autoMapping="true"><!--User的主鍵--><id column="uid" property="id"/><!--Order關聯映射--><!--1.一對多使用collection子標簽進行關聯多方Order2.屬性:1)property="orders" 這里的orders表示User類的成員變量orders2)javaType="List" 表示User類的成員變量orders存儲的Order對象使用的類型,這里是List,可以不配置3) ofType="Order" 表示List集合中存儲數據的類型 Order--><collection property="orders" javaType="List" ofType="Order" autoMapping="true"><!--Order的主鍵--><id column="oid" property="id" /></collection></resultMap><!--根據用戶ID查詢用戶及其訂單數據--><select id="oneToManyQuery" resultMap="oneToManyResult">SELECTtbo.id as oid,tbo.order_number,tbu.id as uid,tbu.user_name,tbu.password,tbu.name,tbu.age,tbu.sexFROMtb_user tbuINNER JOIN tb_order tbo ON tbu.id = tbo.user_idWHEREtbu.id = #{id}</select>
第五步:測試
在用戶的測試類中
public class MybatisTest01 {private static UserMapper mapper = null;@BeforeClasspublic static void beforeClass() throws Exception {//1.構建SessionFactoryString resouce = "mybatis-config.xml";InputStream is = Resources.getResourceAsStream(resouce);SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);//2.獲取sessionSqlSession sqlSession = build.openSession(true);//3.獲取接口對象mapper = sqlSession.getMapper(UserMapper.class);} //根據用戶ID查詢用戶及其訂單數據@Testpublic void oneToManyQuery() {User user = mapper.oneToManyQuery(1L);System.out.println("user = " + user);}
}
【小結】
一對多關系配置:
1、在對象中添加映射關系;
2、編寫接口方法,編寫SQL;
3、編寫resultMap處理數據庫字段和實體類之間數據的封裝;
5、多對多
【需求】:查詢訂單號為20140921001的訂單的詳情信息即查詢訂單信息+訂單中的商品信息;
【步驟】
第一步:需求分析;
第二步:添加關聯關系;
第三步:編寫SQL;
第四步:配置關聯關系;
第五步:運行;
第一步:【需求分析】
1、查詢訂單詳情信息即:查詢訂單信息+訂單中的商品信息;
2、訂單信息在tb_order中,訂單中的商品信息在tb_item中,這兩個表是通過中間表 tb_orderdetail進行關聯的。
3、關聯查詢思路:先查詢訂單表,通過訂單表中的id關聯中間表order_id,然后查詢中間表,根據中間表的item_id關聯商品表的id,最后查詢商品表;
【SQL查詢及結果】
# 【需求】:查詢訂單號為20140921001的訂單的詳情信息 訂單的詳情信息 = 訂單+商品
SELECT*
FROMtb_order tbo
INNER JOIN tb_orderdetail detail ON tbo.id = detail.order_id
INNER JOIN tb_item item ON detail.item_id = item.id
WHERE
tbo.order_number = '20140921001';
第二步:添加關聯關系
【修改Order】
? 一個訂單表中關聯了多個訂單詳情信息,所以在訂單表中添加List<Orderdetail>
屬性:
【Order.java】
package com.itheima.sh.pojo;
import java.util.List;
/*** 訂單表* */
public class Order {private Integer id;private String orderNumber;//關聯User對象private User user;//關聯訂單詳情列表private List<Orderdetail> detailList;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getOrderNumber() {return orderNumber;}public void setOrderNumber(String orderNumber) {this.orderNumber = orderNumber;}public User getUser() {return user;}public void setUser(User user) {this.user = user;}public List<Orderdetail> getDetailList() {return detailList;}public void setDetailList(List<Orderdetail> detailList) {this.detailList = detailList;}@Overridepublic String toString() {return "Order{" +"id=" + id +", orderNumber='" + orderNumber + '\'' +", user=" + user +", detailList=" + detailList +'}';}
}
【修改Orderdetail】
? 每一條訂單詳情記錄中都包含了一條商品信息,所以需要在Orderdetail中添加一個Item屬性;
【Orderdetail.java】
package com.itheima.sh.pojo;
public class Orderdetail { private Integer id; private Double totalPrice; private Integer status;//商品信息private Item item;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public Double getTotalPrice() {return totalPrice;}public void setTotalPrice(Double totalPrice) {this.totalPrice = totalPrice;}public Integer getStatus() {return status;}public void setStatus(Integer status) {this.status = status;}public Item getItem() {return item;}public void setItem(Item item) {this.item = item;}@Overridepublic String toString() {return "Orderdetail{" +"id=" + id +", totalPrice=" + totalPrice +", status=" + status +", item=" + item +'}';}
}
第三步:編寫接口方法
? 在OrderMapper接口中新增,根據orderNumber查詢訂單及訂單詳情的方法:
public interface OrderMapper {/*** 根據orderNumber查詢訂單及其詳情信息* @param orderNumber* @return*/Order queryOrderAndDetailByOrderNumber(@Param("orderNumber") String orderNumber);
}
第四步:編寫SQL
說明:一定要記住這里給order表的id起別名是oid,訂單詳情表的id起別名是detailId,商品表item的id起別名是itemId。在resultMap標簽的id子標簽中的column屬性值書寫對應的oid、detailId和itemId.
<!--訂單及訂單詳情結果集--><resultMap id="orderAndDetailMap" type="Order" autoMapping="true"><!--tb_order表 和 Order實體類--><!--訂單表主鍵--><id property="id" column="oid"/><!--多個訂單詳情 1對多:detailList--><collection property="detailList" javaType="List" ofType="Orderdetail" autoMapping="true"><!--tb_order_detail表 和 Orderdetail實體類--><!--訂單詳情主鍵 detailId表示下面sql語句的別名--><id property="id" column="detailId"/><!--關聯商品對象 一對一:orderdetail-Item--><association property="item" javaType="Item" autoMapping="true"><!--tb_item表 和 Item實體類 itemId 表示下面的sql語句別名--><id property="id" column="itemId"/></association></collection></resultMap><!--多對多查詢--><select id="queryOrderAndDetailByOrderNumber" resultMap="orderAndDetailMap">SELECTtbo.id as oid,tbo.order_number,detail.id as detailId,detail.total_price,detail.status,item.id as itemId,item.item_detail,item.item_name,item.item_priceFROMtb_order tboINNER JOIN tb_orderdetail detail ON tbo.id = detail.order_idINNER JOIN tb_item item ON detail.item_id = item.idWHEREtbo.order_number = #{orderNumber};</select>
第五步:測試
@Testpublic void queryOrderAndDetailByOrderNumber() {Order order = mapper.queryOrderAndDetailByOrderNumber("20140921001");System.out.println("order = " + order);}
【結果】
【擴展】
【需求】根據訂單號(20140921001)
? 查詢訂單信息
? 查詢訂單所屬用戶信息
? 查詢訂單中的詳細商品信息
【SQL實現及查詢結果】
? 通過分析,實現這個查詢就在上面的查詢基礎上再關聯一個一對一的User信息;
#查詢訂單詳情
SELECTtbo.id as oid,tbo.order_number,detail.id as detailId,detail.total_price,detail.status,item.id as itemId,item.item_detail,item.item_name,item.item_price,tbu.id as uid,tbu.age,tbu.name,tbu.password,tbu.sex,tbu.user_name
FROMtb_order tbo
INNER JOIN tb_orderdetail detail ON tbo.id = detail.order_id
INNER JOIN tb_item item ON detail.item_id = item.id
INNER JOIN tb_user tbu ON tbo.user_id = tbu.id
WHERE
tbo.order_number = '20140921001';
【添加關聯關系】
都已經在實體類添加完畢,直接操作即可
【編寫接口方法】
在OrderMapper接口中再擴展一個方法:queryOrderAndDetailAndUserByOrderNumber
/*** 根據orderNumber查詢 訂單,詳情,商品及用戶數據* @param orderNumber* @return*/Order queryOrderAndDetailAndUserByOrderNumber(@Param("orderNumber") String orderNumber);
【編寫SQL】
<!--訂單及訂單詳情結果集--><resultMap id="orderAndDetailMapPlus" type="Order" autoMapping="true"><!--tb_order表 和 Order實體類--><!--訂單表主鍵--><id property="id" column="oid"/><!--Order-User:一對一關聯--><association property="user" javaType="User" autoMapping="true"><!--User主鍵--><id property="id" column="uid"/></association><!--多個訂單詳情 1對多:detailList--><collection property="detailList" javaType="List" ofType="Orderdetail" autoMapping="true"><!--tb_order_detail表 和 Orderdetail實體類--><!--訂單詳情主鍵--><id property="id" column="detailId"/><!--關聯商品對象 一對一:orderdetail-Item--><association property="item" javaType="Item" autoMapping="true"><!--tb_item表 和 Item實體類--><id property="id" column="itemId"/></association></collection></resultMap><select id="queryOrderAndDetailAndUserByOrderNumber" resultMap="orderAndDetailMapPlus">SELECTtbo.id as oid,tbo.order_number,detail.id as detailId,detail.total_price,detail.status,item.id as itemId,item.item_detail,item.item_name,item.item_price,tbu.id as uid,tbu.age,tbu.name,tbu.password,tbu.sex,tbu.user_nameFROMtb_order tboINNER JOIN tb_orderdetail detail ON tbo.id = detail.order_idINNER JOIN tb_item item ON detail.item_id = item.idINNER JOIN tb_user tbu ON tbo.user_id = tbu.idWHEREtbo.order_number = #{orderNumber};</select>
【測試】
【結果】
6、ResultMap繼承
? 如果兩個結果集有重疊的部分,如下圖所示。我們可以使用結果集繼承來實現重疊的結果集的復用。
orderAndDetailAndUserMap
結果集可以繼承orderAndDetailMap
結果集。
7、高級查詢小結
resutlType無法幫助我們自動的去完成映射,所以只有使用resultMap手動的進行映射
resultMap: 屬性:type 結果集對應的數據類型 Orderid 唯一標識,被引用的時候,進行指定autoMapping 開啟自動映射extends 繼承子標簽:id:配置id屬性result:配置其他屬性association:配置一對一的映射property 定義對象的屬性名javaType 屬性的類型autoMapping 開啟自動映射collection:配置一對多的映射property 定義對象的屬性名javaType 集合的類型ofType 集合中的元素類型 泛型autoMapping 開啟自動映射