目錄
(一)復雜查詢:1對1關系
【任務】數據庫里有學生表(student)和學生證信息表(student_card),表結構如下所示,要求使用MyBatis框架查詢所有的學生信息以及每位學生的學生證信息
解決方案1:關聯查詢實現
定義實體類
定義接口
配置SQL并完成數據映射
執行SQL
解決方案2:嵌套查詢實現
定義實體類和接口
配置SQL并完成數據映射
執行SQL
(二)復雜查詢:1對多關系
【任務】數據庫里有客戶表(customer)和訂單表(orders),表結構如下所示,要求用MyBatis框架查詢所有的客戶信息以及每位客戶的訂單信息
解決方案1:關聯查詢實現
定義實體類
定義接口
配置SQL并完成數據映射
執行SQL
解決方案2:嵌套查詢實現
(一)復雜查詢:1對1關系
在前面的課程中,我們已經學習了使用MyBatis框架實現單表的數據查詢。在實際的開發中,我們遇到的需求更多的是需要多表進行關聯查詢獲取需要的數據。
【任務】數據庫里有學生表(student)和學生證信息表(student_card),表結構如下所示,要求使用MyBatis框架查詢所有的學生信息以及每位學生的學生證信息
學生表(student)
學生證信息表(student_card)
【分析】
- 題目中要求我們查出所有的學生以及學生證信息,因此需要涉及兩張表的關聯查詢
- 通過表結構分析,學生表和學生證表之間是1對1的關系,擁有相同的主鍵stu_id。
- 兩表關聯查詢的SQL語句如下:
select stu.*,
card.stu_id as card_stu_id,card.class_info,card.major
from student stu
LEFT JOIN student_card card
on stu.stu_id=card.stu_id
- 查詢結果如下
【實現步驟】
- 定義實體類
- 定義接口
- 配置SQL并完成數據映射
- 執行SQL
解決方案1:關聯查詢實現
定義實體類
基于表結構創建兩個實體類:學生類(Student)和學生證類(StudentCard),因為學生實體和學生證實體是1對1的關系,所以在學生類中加入一個學生證類型的屬性,表示一個學生擁有一個學生證。
@Data
//學生證實體類
public class StudentCard {private int stuId;private String classInfo;private String major;
}
//學生實體類
@Data
public class Student {private int stuId;private String stuNo;private String stuName;//一個學生擁有一個學生證,體現1對1關系private StudentCard studentCard;
}
定義接口
在com.cg.mapper下新建一個接口StudentMapper,接口中加入方法
public interface StudentMapper {/*** 查詢學生信息(包含學生證)* @return 學生信息列表*/List<Student> selectStudents();
}
配置SQL并完成數據映射
在src/main/resources/mapper下新建一個SQL映射文件StudentMapper.xml,完成SQL配置:
<?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.cg.mapper.StudentMapper"><!--定義查詢結果和Student實體類映射關系--><resultMap id="studentCardMap" type="Student"><!-- 先設置Student自身屬性和字段的對應關系 --><id column="stu_id" property="stuId"/><result column="stu_no" property="stuNo"/><result column="stu_name" property="stuName"/><!-- 使用association標簽配置“一對一”關聯關系,也就是實體類型屬性的映射關系 --><!-- property屬性:實體類型屬性名 --><!-- javaType屬性:實體類型屬性全類名 --><association property="studentCard" javaType="StudentCard"><!-- 配置StudentCard類的屬性和字段的對應關系 --><id column="card_stu_id" property="stuId"/><result column="class_info" property="classInfo"/><result column="major" property="major"/></association></resultMap><select id="selectStudents" resultMap="studentCardMap">select stu.*,card.stu_id as card_stu_id,card.class_info,card.majorfrom student stuLEFT JOIN student_card cardon stu.stu_id=card.stu_id</select>
</mapper>
注:
- 一定要記得通過namespace關聯接口
- 使用association標簽配置“一對一”關聯關系,也就是實體類型屬性的映射關系
- 新建的SQL映射文件需要在全局配置文件中配置地址
<!--SQL映射文件地址配置--><mappers><mapper resource="mapper/StudentMapper.xml" /></mappers>
- 配置優化:在以上配置中,很多屬性名稱和列都是符合駝峰命名規范的,這些屬性和列的映射關系可以省略不寫,但是需要在全局配置文件(mybatis-config.xml)中配置如下內容
<settings><!--配置控制臺日志輸出--><setting name="logImpl" value="STDOUT_LOGGING"/><!--開啟駝峰式命名自動映射駝峰式命名--><setting name="mapUnderscoreToCamelCase" value="true"/><!--開啟自動映射--><setting name="autoMappingBehavior" value="FULL"/></settings>
開啟了MyBatis自動映射之后,<resultMap>標簽的配置可以簡化為如下:
<!--定義查詢結果和Student實體類映射關系--><resultMap id="studentCardMap" type="Student"><!--通過association元素定義實體類型屬性的映射關系--><association property="studentCard" javaType="StudentCard"><id column="card_stu_id" property="stuId"/></association></resultMap>
執行SQL
在測試方法中調用MyBatis API執行SQL,完成數據查詢。
@Testpublic void test1() throws IOException {//1.加載MyBatis核心配置文件InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");//2.獲取SqlSessionFactory對象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//3.獲取SqlSession會話對象SqlSession sqlSession = sqlSessionFactory.openSession();//4.執行SQL配置StudentMapper studentMapper= sqlSession.getMapper(StudentMapper.class);List<Student> students = studentMapper.selectStudents();for (Student student : students) {System.out.println(student);}//6.關閉數據庫會話sqlSession.close();}
控制臺打印結果如下:
解決方案2:嵌套查詢實現
【實現原理】嵌套查詢就是將原來多表查詢中的關聯查詢語句拆成單個表的查詢,再使用MyBatis的語法嵌套在一起。結合本任務,首先執行主查詢“select * from student”,查詢出所有的學生信息列表,然后遍歷每一條學生記錄,根據學生信息的主鍵列(stu_id)的值執行子查詢“select * from student_card where stu_id=?”查詢出每位學生的學生證信息,然后將查詢出的學生和學生證數據映射到到Student實體類對象的屬性中完成數據映射。
定義實體類和接口
實體類和接口的設計內容和解決方案1是一致的。
配置SQL并完成數據映射
結合嵌套查詢的實現原理可知,我們需要完成如下兩個SQL配置(主查詢和子查詢),然后將他們關聯起來。
- 主查詢:select * from student
- 子查詢:select * from student_card where stu_id=?
首先子查詢的配置如下,子查詢的查詢結果需要和StudentCard建立映射關系。
<!--定義映射關系--><resultMap id="cardMap" type="StudentCard"><id column="stu_id" property="stuId"/><result column="class_info" property="classInfo"/><result column="major" property="major"/></resultMap><!--子查詢配置,根據stuId查詢學生證信息并映射到實體類(StudentCard)上--><select id="selectCardByStuId" resultMap="cardMap">select * from student_card where stu_id=#{stuId}</select>
然后配置主查詢,在主查詢映射關系配置中,需要在<association>標簽中,通過select屬性關聯要執行的子查詢的SQL配置id,通過column屬性指定子查詢所需參數的來源字段,來源于主查詢結果中的stu_id字段的值。
<!--定義映射關系--><resultMap id="studentCardMap" type="Student"><!-- 先設置Student自身屬性和字段的對應關系 --><id column="stu_id" property="stuId"/><result column="stu_no" property="stuNo"/><result column="stu_name" property="stuName"/><!-- 使用association標簽配置實體類型屬性的映射關系通過select關聯子查詢SQL,通過column指定子查詢參數來源字段--><association property="studentCard"javaType="StudentCard"select="selectCardByStuId"column="stu_id"></association></resultMap><!--主查詢配置,將查詢結果映射到Student實體類上--><select id="selectStudents" resultMap="studentCardMap">select * from student</select>
執行SQL
調用MyBatis API執行接口方法,代碼和解決方案1是一樣的,控制臺打印內容如下,從打印的內容可以看到,MyBatis底層首先執行了一次主查詢SQL獲取學生信息列表,然后遍歷查詢結果的每一條記錄,根據記錄stu_id字段的值執行子查詢SQL獲取學生證信息,從而完成數據的映射,因為有4個學生記錄,因此嵌套查詢共執行了1+4次select查詢。
==> Preparing: select * from student
==> Parameters:
<== Columns: stu_id, stu_no, stu_name
<== Row: 5, 189000101, 張哲
====> Preparing: select * from student_card where stu_id=?
====> Parameters: 5(Integer)
<==== Columns: stu_id, class_info, major
<==== Row: 5, 18級軟工1班, 軟件工程
<==== Total: 1
<== Row: 6, 189000102, 王霜
====> Preparing: select * from student_card where stu_id=?
====> Parameters: 6(Integer)
<==== Columns: stu_id, class_info, major
<==== Row: 6, 18級軟工2班, 數字媒體技術
<==== Total: 1
<== Row: 7, 189000103, 趙鋒
====> Preparing: select * from student_card where stu_id=?
====> Parameters: 7(Integer)
<==== Columns: stu_id, class_info, major
<==== Row: 7, 18級軟工3班, 軟件工程
<==== Total: 1
<== Row: 8, 189000104, 李娜
====> Preparing: select * from student_card where stu_id=?
====> Parameters: 8(Integer)
<==== Total: 0
<== Total: 4
Student(stuId=5, stuNo=189000101, stuName=張哲, studentCard=StudentCard(stuId=5, classInfo=18級軟工1班, major=軟件工程))
Student(stuId=6, stuNo=189000102, stuName=王霜, studentCard=StudentCard(stuId=6, classInfo=18級軟工2班, major=數字媒體技術))
Student(stuId=7, stuNo=189000103, stuName=趙鋒, studentCard=StudentCard(stuId=7, classInfo=18級軟工3班, major=軟件工程))
Student(stuId=8, stuNo=189000104, stuName=李娜, studentCard=null)
(二)復雜查詢:1對多關系
【任務】數據庫里有客戶表(customer)和訂單表(orders),表結構如下所示,要求用MyBatis框架查詢所有的客戶信息以及每位客戶的訂單信息
客戶表(customer)
訂單表(orders)
【分析】
- 題目中要求我們查出所有的客戶以及客戶的訂單信息,因此需要涉及兩張表的關聯查詢
- 通過表結構分析,訂單表通過外鍵customer_id與客戶表關聯,一個客戶可以擁有多個訂單,因此客戶與訂單是1對多的關系
- 兩表關聯查詢的SQL語句如下:
select c.*,o.id as order_id,o.order_no,o.price,o.customer_idfrom customer cleft join orders o on c.id=o.customer_id
【實現步驟】
- 定義實體類
- 定義接口
- 配置SQL并完成數據映射
- 執行SQL
解決方案1:關聯查詢實現
定義實體類
基于表結構創建兩個實體類:客戶類(Customer)和訂單類(Order),因為客戶實體和訂單實體是1對多的關系,所以在客戶類中加入訂單類型的集合(List<Order>)屬性,表示一個客戶擁有多個訂單。
@Data
//訂單實體類
public class Order {private int id;private String orderNo;private BigDecimal price;private int customerId;
}
//客戶實體類
@Data
public class Customer {private int id;private String account;private String username;//一個客戶擁有多個訂單,體現1對多關系private List<Order> orders;
}
定義接口
在com.cg.mapper下新建一個接口CustomerMapper,接口中加入方法
public interface CustomerMapper {/*** 查詢所有的客戶列表(包含客戶的訂單列表)* @return 客戶列表*/List<Customer> selectCustomers();
}
配置SQL并完成數據映射
在src/main/resources/mapper下新建一個SQL映射文件CustomerMapper.xml,完成SQL配置:
<?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.cg.mapper.CustomerMapper"><!--定義查詢結果和Customer實體類映射關系--><resultMap id="customerOrderMap" type="Customer"><!-- 先設置Customer自身屬性和字段的對應關系 --><id column="id" property="id"/><result column="account" property="account"/><result column="username" property="username"/><!-- 使用collection標簽配置“一對多”關聯關系,也就是集合類型屬性的映射關系 --><!-- property屬性:集合類型屬性名 --><!-- ofType屬性:集合中的實體全類名 --><collection property="orders" ofType="Order"><!-- 配置Order實體類的屬性和字段的對應關系 --><id column="order_id" property="id"/><result column="order_no" property="orderNo"/><result column="price" property="price"/><result column="customer_id" property="customerId"/></collection></resultMap><!--關聯查詢SQL配置--><select id="selectCustomers" resultMap="customerOrderMap">select c.*,o.id as order_id,o.order_no,o.price,o.customer_idfrom customer cleft join orders oon c.id=o.customer_id</select>
</mapper>
注:
- 一定要記得通過namespace關聯接口
- 使用collection標簽配置“一對多”關聯關系,也就是集合類型屬性的映射關系
- 新建的SQL映射文件需要在全局配置文件中配置地址
<!--SQL映射文件地址配置--><mappers><mapper resource="mapper/CustomerMapper.xml" /></mappers>
執行SQL
在測試方法中調用MyBatis API執行SQL,完成數據查詢。
@Testpublic void test2() throws IOException {//1.加載MyBatis核心配置文件InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");//2.獲取SqlSessionFactory對象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//3.獲取SqlSession會話對象SqlSession sqlSession = sqlSessionFactory.openSession();//4.執行SQL配置CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);List<Customer> customers= customerMapper.selectCustomers();//5.處理結果for (Customer customer : customers) {System.out.println(customer);}//6.關閉數據庫會話sqlSession.close();}
控制臺打印結果如下,可以看到客戶劉丹擁有兩個訂單,客戶李義擁有1個訂單,客戶王偉擁有兩個訂單,客戶張慶沒有訂單。
==> Preparing: select c.*, o.id as order_id,o.order_no,o.price,o.customer_id from customer c left join orders o on c.id=o.customer_id
==> Parameters:
<== Columns: id, account, username, order_id, order_no, price, customer_id
<== Row: 1, 13996065424, 劉丹, 1, 10001, 234.00, 1
<== Row: 2, 13996068885, 李義, 2, 10002, 115.00, 2
<== Row: 1, 13996065424, 劉丹, 3, 10003, 214.00, 1
<== Row: 4, 13996063954, 王偉, 4, 10004, 321.00, 4
<== Row: 4, 13996063954, 王偉, 5, 10005, 115.00, 4
<== Row: 3, 13996062222, 張慶, null, null, null, null
<== Total: 6
Customer(id=1, account=13996065424, username=劉丹, orders=[Order(id=1, orderNo=10001, price=234.00, customerId=1), Order(id=3, orderNo=10003, price=214.00, customerId=1)])
Customer(id=2, account=13996068885, username=李義, orders=[Order(id=2, orderNo=10002, price=115.00, customerId=2)])
Customer(id=4, account=13996063954, username=王偉, orders=[Order(id=4, orderNo=10004, price=321.00, customerId=4), Order(id=5, orderNo=10005, price=115.00, customerId=4)])
Customer(id=3, account=13996062222, username=張慶, orders=[])
解決方案2:嵌套查詢實現
【練習】參考1對1關系任務中的解決方案2中嵌套查詢的實現原理,思考本任務如何使用嵌套查詢來實現?
【提示】結合本任務,首先執行主查詢“select * from customer”,查詢出所有的客戶信息列表,然后遍歷每一條客戶記錄,根據客戶信息的主鍵列(id)的值執行子查詢“select * from orders where customer_id=?”查詢出每位客戶的訂單列表,然后將查詢出的客戶以及訂單數據映射到到Customer實體類對象的屬性中完成數據映射。