一、MyBatis查詢語句專題
模塊名:mybatis-008-select
打包方式:jar 引入依賴:mysql驅動依賴、mybatis依賴、logback依賴、junit依賴。
引入配置文件:jdbc.properties、mybatis-config.xml、logback.xml
創建pojo類:Car
創建Mapper接口:CarMapper
創建Mapper接口對應的映射文件:CarMapper.xml
創建單元測試:CarMapperTest
拷貝工具類:SqlSessionUtil
1.返回Car
當查詢的結果,有對應的實體類,并且查詢結果只有一條時:
/*** Car SQL映射器*/
public interface CarMapper {/*** 根據id主鍵查詢:結果最多只有一條* @param id* @return*/Car selectById(Long id);
}
<select id="selectById" resultType="Car">select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where id = #{id}
</select>
public class CarMapperTest {@Testpublic void testSelectById(){CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);Car car = mapper.selectById(35L);System.out.println(car);}
}
執行結果:
查詢結果是一條的話可以使用List集合接收嗎?當然可以。
@Test
public void testSelectByIdToList(){CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);List<Car> cars = mapper.selectByIdToList(35L);System.out.println(cars);
}
執行結果:
2.返回List<Car>
當查詢的記錄條數是多條的時候,必須使用集合接收。如果使用單個實體類接收會出現異常。
/**
* 查詢所有的Car
* @return
*/
List<Car> selectAll();
<select id="selectAll" resultType="Car">select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car
</select>
@Test
public void testSelectAll(){CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);List<Car> cars = mapper.selectAll();cars.forEach(car -> System.out.println(car));
}
如果返回多條記錄,采用單個實體類接收會怎樣?
/**
* 查詢多條記錄,采用單個實體類接收會怎樣?
* @return
*/
Car selectAll2();
<select id="selectAll2" resultType="Car">select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car
</select>
@Test
public void testSelectAll2(){CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);Car car = mapper.selectAll2();System.out.println(car);
}
3.返回Map
當返回的數據,沒有合適的實體類對應(比如返回的應該對應User類,里面的pojo只有Car類)的話,可以采用Map集合接收。字段名做key,字段值做value。 查詢如果可以保證只有一條數據,則返回一個Map集合即可。
/*** 通過id查詢一條記錄,返回Map集合* @param id* @return*/
Map<String, Object> selectByIdRetMap(Long id);
<select id="selectByIdRetMap" resultType="map">select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car where id = #{id}
</select>
resultMap="map",這是因為mybatis內置了很多別名。【參見mybatis開發手冊】
@Test
public void testSelectByIdRetMap(){CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);Map<String,Object> car = mapper.selectByIdRetMap(35L);System.out.println(car);
}
執行結果:
當然,如果返回一個Map集合,可以將Map集合放到List集合中嗎?當然可以,這里就不再測試了。 反過來,如果返回的不是一條記錄,是多條記錄的話,只采用單個Map集合接收,這樣同樣會出現之前的異常:TooManyResultsException
4.返回List<Map>
查詢結果條數大于等于1條數據,則可以返回一個存儲Map集合的List集合。List<Map>等同于List<Car>
/*** 查詢所有的Car,返回一個List集合。List集合中存儲的是Map集合。* @return*/
List<Map<String,Object>> selectAllRetListMap();
<select id="selectAllRetListMap" resultType="map">select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car
</select>
@Test
public void testSelectAllRetListMap(){CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);List<Map<String,Object>> cars = mapper.selectAllRetListMap();System.out.println(cars);
}
執行結果:
[{carType=燃油車, carNum=103, guidePrice=50.30, produceTime=2020-10-01, id=33, brand=奔馳E300L}, {carType=電車, carNum=102, guidePrice=30.23, produceTime=2018-09-10, id=34, brand=比亞迪漢}, {carType=燃油車, carNum=103, guidePrice=50.30, produceTime=2020-10-01, id=35, brand=奔馳E300L}, {carType=燃油車, carNum=103, guidePrice=33.23, produceTime=2020-10-11, id=36, brand=奔馳C200},......
]
5.返回Map<String,Map>
拿Car的id做key,以后取出對應的Map集合時更方便。
/*** 查詢所有的Car,返回一個大Map集合。* Map集合的key是每條記錄的主鍵值。* Map集合的value是每條記錄。* {* 160={car_num=3333, id=160, guide_price=32.00, produce_time=2000-10-10, brand=奔馳E300L, car_type=新能源},* 161={car_num=4444, id=161, guide_price=32.00, produce_time=2000-10-10, brand=奔馳C200, car_type=新能源},* 162={car_num=9999, id=162, guide_price=30.00, produce_time=2020-10-11, brand=帕薩特, car_type=燃油車},* 163={car_num=9991, id=163, guide_price=30.00, produce_time=2020-11-11, brand=凱美瑞, car_type=燃油車},* 158={car_num=1111, id=158, guide_price=3.00, produce_time=2000-10-10, brand=比亞迪漢, car_type=新能源},* 159={car_num=2222, id=159, guide_price=32.00, produce_time=2000-10-10, brand=比亞迪秦, car_type=新能源}* }* @return*/@MapKey("id") // 將查詢結果的id值作為整個大Map集合的key。Map<Long, Map<String,Object>> selectAllRetMap();
<select id="selectAllRetMap" resultType="map">select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car
</select>
@Test
public void testSelectAllRetMap(){CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);Map<Long,Map<String,Object>> cars = mapper.selectAllRetMap();System.out.println(cars);
}
6.resultMap結果映射
查詢結果的列名和java對象的屬性名對應不上怎么辦?
-
第一種方式:as 給列起別名
-
第二種方式:使用resultMap進行結果映射
-
第三種方式:是否開啟駝峰命名自動映射(配置settings)
⑴.使用resultMap進行結果映射
/*** 查詢所有Car,使用resultMap進行結果映射* @return*/
List<Car> selectAllByResultMap();
<!--1.專門定義一個結果映射,在這個結果映射當中指定數據庫表的字段名和Java類的屬性名的對應關系。2. type屬性:用來指定POJO類的類名。3. id屬性:指定resultMap的唯一標識。這個id將來要在select標簽中使用。--><resultMap id="carResultMap" type="Car"><!--如果數據庫表中有主鍵,一般都是有主鍵,要不然不符合數據庫設計第一范式。--><!--如果有主鍵,建議這里配置一個id標簽,注意:這不是必須的。但是官方的解釋是什么呢?這樣的配置可以讓mybatis提高效率。--><id property="id" column="id"/><!--<result property="id" column="id"/>--><!--property后面填寫POJO類的屬性名--><!--column后面填寫數據庫表的字段名--><result property="carNum" column="car_num" javaType="java.lang.String" jdbcType="VARCHAR"/><!--如果column和property是一樣的,這個可以省略。--><!--<result property="brand" column="brand"/>--><result property="guidePrice" column="guide_price"/><result property="produceTime" column="produce_time"/><result property="carType" column="car_type" javaType="string" jdbcType="VARCHAR"/></resultMap><!--select標簽的resultMap屬性,用來指定使用哪個結果映射。resultMap后面的值是resultMap的id--><select id="selectAllByResultMap" resultMap="carResultMap">select * from t_car</select>
@Testpublic void testSelectAllByResultMap(){SqlSession sqlSession = SqlSessionUtil.openSession();CarMapper mapper = sqlSession.getMapper(CarMapper.class);List<Car> cars = mapper.selectAllByResultMap();cars.forEach(car -> System.out.println(car));sqlSession.close();}
⑵是否開啟駝峰命名自動映射
使用這種方式的前提是:屬性名遵循Java的命名規范,數據庫表的列名遵循SQL的命名規范。 Java命名規范:首字母小寫,后面每個單詞首字母大寫,遵循駝峰命名方式。
SQL命名規范:全部小寫,單詞之間采用下劃線分割。
比如以下的對應關系:
如何啟用該功能,在mybatis-config.xml文件中進行配置:
<!--放在properties標簽后面-->
<settings><setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
/**
* 查詢所有Car,啟用駝峰命名自動映射
* @return
*/
List<Car> selectAllByMapUnderscoreToCamelCase();
<select id="selectAllByMapUnderscoreToCamelCase" resultType="Car">select * from t_car
</select>
@Test
public void testSelectAllByMapUnderscoreToCamelCase(){CarMapper carMapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);List<Car> cars = carMapper.selectAllByMapUnderscoreToCamelCase();System.out.println(cars);
}
7.返回總記錄條數
需求:查詢總記錄條數
/*** 獲取總記錄條數* @return*/
Long selectTotal();
<!--long是別名,可參考mybatis開發手冊。-->
<select id="selectTotal" resultType="long">select count(*) from t_car
</select>
@Test
public void testSelectTotal(){CarMapper carMapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);Long total = carMapper.selectTotal();System.out.println(total);
}
二、動態SQL
有的業務場景,也需要SQL語句進行動態拼接,例如:
-
批量刪除
delete from t_car where id
in(1,2,3,4,5,6,......這里的值是動態的,根據用戶選擇的id不同,值是不同的);
-
多條件查詢
select * from t_car where brand like '豐田%' and guide_price > 30 and .....;
創建模塊:mybatis-009-dynamic-sql
打包方式:jar 引入依賴:mysql驅動依賴、mybatis依賴、logback依賴、junit依賴。
引入配置文件:jdbc.properties、mybatis-config.xml、logback.xml
創建pojo類:Car
創建Mapper接口:CarMapper
創建Mapper接口對應的映射文件:CarMapper.xml
創建單元測試:CarMapperTest
拷貝工具類:SqlSessionUtil
1.if標簽
需求:多條件查詢。 可能的條件包括:品牌(brand)、指導價格(guide_price)、汽車類型(car_type)
CarMapper接口
package com.powernode.mybatis.mapper;import com.powernode.mybatis.pojo.Car;
import org.apache.ibatis.annotations.Param;import java.util.List;public interface CarMapper {/*** 多條件查詢* @param brand 品牌* @param guidePrice 指導價* @param carType 汽車類型* @return*/List<Car> selectByMultiCondition(@Param("brand") String brand, @Param("guidePrice") Double guidePrice, @Param("carType") String carType);}
<?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="org.example1.mapper.CarMapper"><select id="selectByMultiCondition" resultType="car"><!--1. if標簽中test屬性是必須的。2. if標簽中test屬性的值是false或者true。3. 如果test是true,則if標簽中的sql語句就會拼接。反之,則不會拼接。4. test屬性中可以使用的是:當使用了@Param注解,那么test中要出現的是@Param注解指定的參數名。@Param("brand"),那么這里只能使用brand當沒有使用@Param注解,那么test中要出現的是:param1 param2 param3 arg0 arg1 arg2....當使用了POJO,那么test中出現的是POJO類的屬性名。5. 在mybatis的動態SQL當中,不能使用&&,只能使用and。-->select * from t_car where<if test="brand != null and brand != ''">brand like #{brand}"%"</if><if test="guidePrice != null and guidePrice != ''">and guide_price >= #{guidePrice}</if><if test="carType != null and carType != ''">and car_type = #{carType}</if></select>
</mapper>
// 假設三個條件都不是空
@Testpublic void testSelectByMultiCondition(){SqlSession sqlSession = SqlSessionUtil.openSession();CarMapper mapper = sqlSession.getMapper(CarMapper.class);// 假設三個條件都不是空List<Car> cars = mapper.selectByMultiCondition("比亞迪", 2.00, "新能源");// 假設三個條件都是空//List<Car> cars = mapper.selectByMultiCondition("", null, "");// 假設后兩個條件不為空,第一個條件為空//List<Car> cars = mapper.selectByMultiCondition("", 2.0, "新能源");// 假設第一個條件不是空,后兩個條件是空//List<Car> cars = mapper.selectByMultiCondition("比亞迪", null, "");cars.forEach(car -> System.out.println(car));sqlSession.close();}
// 假設三個條件都是空
解決方法:
運行結果:
// 假設第一個條件不是空,后兩個條件是空
2.where標簽
where標簽的作用:讓where子句更加動態智能。
-
所有條件都為空時,where標簽保證不會生成where子句。
-
自動去除某些條件前面多余的and或or。
-
后面的and或or則無法去除
繼續使用if標簽中的需求。
package org.example1.mapper;import org.apache.ibatis.annotations.Param;
import org.example1.pojo.Car;import java.util.List;public interface CarMapper {/*** 使用where標簽,讓where子句更加的智能。* @param brand* @param guidePrice* @param carType* @return*/List<Car> selectByMultiConditionWithWhere(@Param("brand") String brand, @Param("guidePrice") Double guidePrice, @Param("carType") String carType);}
<select id="selectByMultiConditionWithWhere" resultType="Car">select * from t_car<!--where標簽是專門負責where子句動態生成的。--><where><if test="brand != null and brand != ''">and brand like "%"#{brand}"%"</if><if test="guidePrice != null and guidePrice != ''">and guide_price > #{guidePrice}</if><if test="carType != null and carType != ''">and car_type = #{carType}</if></where></select>
@Testpublic void testSelectByMultiConditionWithWhere(){SqlSession sqlSession = SqlSessionUtil.openSession();CarMapper mapper = sqlSession.getMapper(CarMapper.class);// 三個條件都不是空//List<Car> cars = mapper.selectByMultiConditionWithWhere("比亞迪", 2.0, "新能源");// 三個條件都是空//List<Car> cars = mapper.selectByMultiConditionWithWhere("", null, "");// 如果第一個條件是空//List<Car> cars = mapper.selectByMultiConditionWithWhere("", 2.0, "新能源");// 后面兩個條件是空List<Car> cars = mapper.selectByMultiConditionWithWhere("比亞迪", null, "");cars.forEach(car -> System.out.println(car));sqlSession.close();}
它可以自動去掉前面多余的and,那可以自動去掉后面多余的and嗎?
<select id="selectByMultiConditionWithWhere" resultType="car">select * from t_car<where><if test="brand != null and brand != ''">brand like #{brand}"%" and</if><if test="guidePrice != null and guidePrice != ''">guide_price >= #{guidePrice} and</if><if test="carType != null and carType != ''">car_type = #{carType}</if></where>
</select>
// 讓最后一個條件為空
List<Car> cars = mapper.selectByMultiConditionWithWhere("豐田", 20.0, "");
運行結果:
很顯然,后面多余的and是不會被去除的。
3.trim標簽
trim標簽的屬性:
-
prefix:在trim標簽中的語句前添加內容
-
suffix:在trim標簽中的語句后添加內容
-
prefixOverrides:前綴覆蓋掉(前綴去掉)
-
suffixOverrides:后綴覆蓋掉(后綴去掉)
/**
* 根據多條件查詢Car,使用trim標簽
* @param brand
* @param guidePrice
* @param carType
* @return
*/
List<Car> selectByMultiConditionWithTrim(@Param("brand") String brand, @Param("guidePrice") Double guidePrice, @Param("carType") String carType);
<select id="selectByMultiConditionWithTrim" resultType="car">select * from t_car<trim prefix="where" suffixOverrides="and|or"><if test="brand != null and brand != ''">brand like #{brand}"%" and</if><if test="guidePrice != null and guidePrice != ''">guide_price >= #{guidePrice} and</if><if test="carType != null and carType != ''">car_type = #{carType}</if></trim>
</select>
@Test
public void testSelectByMultiConditionWithTrim(){CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);List<Car> cars = mapper.selectByMultiConditionWithTrim("豐田", 20.0, "");System.out.println(cars);
}
如果所有條件為空,where會被加上嗎?
List<Car> cars = mapper.selectByMultiConditionWithTrim("", null, "");
執行結果:
4.set標簽
主要使用在update語句當中,用來生成set關鍵字,同時去掉最后多余的“,” 比如我們只更新提交的不為空的字段,如果提交的數據是空或者"",那么這個字段我們將不更新。
/**
* 更新信息,使用set標簽
* @param car
* @return
*/
int updateWithSet(Car car);
@Test
public void testUpdateWithSet(){CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);Car car = new Car(38L,"1001","豐田霸道2",10.0,"",null);int count = mapper.updateWithSet(car);System.out.println(count);SqlSessionUtil.openSession().commit();
}
執行結果:
5.choose when otherwise
這三個標簽是在一起使用的:
<choose><when></when><when></when><when></when><otherwise></otherwise>
</choose>
等同于:
if(){}else if(){}else if(){}else if(){}else{}
只有一個分支會被選擇!!!!
需求:先根據品牌查詢,如果沒有提供品牌,再根據指導價格查詢,如果沒有提供指導價格,就根據生產日期查詢。
/**
* 使用choose when otherwise標簽查詢
* @param brand
* @param guidePrice
* @param produceTime
* @return
*/
List<Car> selectWithChoose(@Param("brand") String brand, @Param("guidePrice") Double guidePrice, @Param("produceTime") String produceTime);
<select id="selectWithChoose" resultType="car">select * from t_car<where><choose><when test="brand != null and brand != ''">brand like #{brand}"%"</when><when test="guidePrice != null and guidePrice != ''">guide_price >= #{guidePrice}</when><otherwise>produce_time >= #{produceTime}</otherwise></choose></where>
</select>
@Test
public void testSelectWithChoose(){CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);//List<Car> cars = mapper.selectWithChoose("豐田霸道", 20.0, "2000-10-10");//List<Car> cars = mapper.selectWithChoose("", 20.0, "2000-10-10");//List<Car> cars = mapper.selectWithChoose("", null, "2000-10-10");List<Car> cars = mapper.selectWithChoose("", null, "");System.out.println(cars);
}
6.foreach標簽
循環數組或集合,動態生成sql,比如這樣的SQL:
delete from t_car where id in(1,2,3);
delete from t_car where id = 1 or id = 2 or id = 3;
insert into t_car values(null,'1001','凱美瑞',35.0,'2010-10-11','燃油車'),(null,'1002','比亞迪唐',31.0,'2020-11-11','新能源'),(null,'1003','比亞迪宋',32.0,'2020-10-11','新能源')
⑴.批量刪除
-
用in來刪除
/**
* 通過foreach完成批量刪除
* @param ids
* @return
*/
int deleteBatchByForeach(@Param("ids") Long[] ids);
<!--
collection:集合或數組
item:集合或數組中的元素(名字隨便取)
separator:分隔符
open:foreach標簽中所有內容的開始
close:foreach標簽中所有內容的結束
-->
<delete id="deleteBatchByForeach">delete from t_car where id in<foreach collection="ids" item="id" separator="," open="(" close=")">#{id}</foreach>
</delete>
@Test
public void testDeleteBatchByForeach(){CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);int count = mapper.deleteBatchByForeach(new Long[]{40L, 41L, 42L});System.out.println("刪除了幾條記錄:" + count);SqlSessionUtil.openSession().commit();
}
執行結果:
-
用or來刪除
/**
* 通過foreach完成批量刪除
* @param ids
* @return
*/
int deleteBatchByForeach2(@Param("ids") Long[] ids);
<delete id="deleteBatchByForeach2">delete from t_car where<foreach collection="ids" item="id" separator="or">id = #{id}</foreach>
</delete>
@Test
public void testDeleteBatchByForeach2(){CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);int count = mapper.deleteBatchByForeach2(new Long[]{40L, 41L, 42L});System.out.println("刪除了幾條記錄:" + count);SqlSessionUtil.openSession().commit();
}
執行結果:
⑵.批量添加
/**
* 批量添加,使用foreach標簽
* @param cars
* @return
*/
int insertBatchByForeach(@Param("cars") List<Car> cars);
<insert id="insertBatchByForeach">insert into t_car values <foreach collection="cars" item="car" separator=",">(null,#{car.carNum},#{car.brand},#{car.guidePrice},#{car.produceTime},#{car.carType})</foreach>
</insert>
@Test
public void testInsertBatchByForeach(){CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);Car car1 = new Car(null, "2001", "蘭博基尼", 100.0, "1998-10-11", "燃油車");Car car2 = new Car(null, "2001", "蘭博基尼", 100.0, "1998-10-11", "燃油車");Car car3 = new Car(null, "2001", "蘭博基尼", 100.0, "1998-10-11", "燃油車");List<Car> cars = Arrays.asList(car1, car2, car3);int count = mapper.insertBatchByForeach(cars);System.out.println("插入了幾條記錄" + count);SqlSessionUtil.openSession().commit();
}
執行結果:
7.sql標簽與include標簽
sql標簽用來聲明sql片段
include標簽用來將聲明的sql片段包含到某個sql語句當中
作用:代碼復用。易維護。
<sql id="carCols">id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType</sql><select id="selectAllRetMap" resultType="map">select <include refid="carCols"/> from t_car
</select><select id="selectAllRetListMap" resultType="map">select <include refid="carCols"/> carType from t_car
</select><select id="selectByIdRetMap" resultType="map">select <include refid="carCols"/> from t_car where id = #{id}
</select>
三、MyBatis的高級映射及延遲加載
模塊名:mybatis-0010-advanced-mapping
打包方式:jar
依賴:mybatis依賴、mysql驅動依賴、junit依賴、logback依賴
配置文件:mybatis-config.xml、logback.xml、jdbc.properties
拷貝工具類:SqlSessionUtil
準備數據庫表:一個班級對應多個學生。班級表:t_clazz。學生表:t_student
創建pojo:Student、Clazz
/*** 學生類*/
public class Student {private Integer sid;private String sname;//......
}
/*** 班級類*/
public class Clazz {private Integer cid;private String cname;//......
}
1.多對一
多種方式,常見的包括三種:
-
第一種方式:一條SQL語句,級聯屬性映射。
-
第二種方式:一條SQL語句,association。
-
第三種方式:兩條SQL語句,分步查詢。(這種方式常用:優點一是可復用。優點二是支持懶加載。)
⑴.第一種方式:級聯屬性映射
pojo類Student中添加一個屬性:Clazz clazz; 表示學生關聯的班級對象。
package org.example1.pojo;/*** 學生信息*/
public class Student { // Student是多的一方private Integer sid;private String sname;private Clazz clazz; // Clazz是一的一方。@Overridepublic String toString() {return "Student{" +"sid=" + sid +", sname='" + sname + '\'' +", clazz=" + clazz +'}';}public Clazz getClazz() {return clazz;}public void setClazz(Clazz clazz) {this.clazz = clazz;}public Integer getSid() {return sid;}public void setSid(Integer sid) {this.sid = sid;}public String getSname() {return sname;}public void setSname(String sname) {this.sname = sname;}public Student(Integer sid, String sname) {this.sid = sid;this.sname = sname;}public Student() {}
}
<!--多對一映射的第一種方式:一條SQL語句,級聯屬性映射。--><resultMap id="studentResultMap" type="Student"><id property="sid" column="sid"/><result property="sname" column="sname"/><result property="clazz.cid" column="cid"/><result property="clazz.cname" column="cname"/></resultMap><select id="selectById" resultMap="studentResultMap">selects.sid,s.sname,c.cid,c.cnamefromt_stu s left join t_clazz c on s.cid = c.cidwheres.sid = #{sid}</select>
StudentMapper接口
package org.example1.mapper;import org.example1.pojo.Student;import java.util.List;public interface StudentMapper {/*** 根據id獲取學生信息。同時獲取學生關聯的班級信息。* @param id 學生的id* @return 學生對象,但是學生對象當中含有班級對象。*/Student selectById(Integer id);}
test
@Testpublic void testSelectById(){SqlSession sqlSession = SqlSessionUtil.openSession();StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);Student student = mapper.selectById(1);System.out.println(student.getSid());System.out.println(student.getSname());System.out.println(student.getClazz().getCid());System.out.println(student.getClazz().getCname());System.out.println(student);sqlSession.close();}
運行結果:
⑵.第二種方式:association
其他位置都不需要修改,只需要修改resultMap中的配置:association即可。
StudentMapper接口
package org.example1.mapper;import org.example1.pojo.Student;import java.util.List;public interface StudentMapper {/*** 一條SQL語句,association* @param id* @return*/Student selectByIdAssociation(Integer id);}
association翻譯為:關聯。 學生對象關聯一個班級對象。
<!--association:翻譯為關聯。一個Student對象關聯一個Clazz對象property:提供要映射的POJO類的屬性名。javaType:用來指定要映射的java類型。--><association property="clazz" javaType="Clazz"><id property="cid" column="cid"/><result property="cname" column="cname"/></association></resultMap><select id="selectByIdAssociation" resultMap="studentResultMapAssociation">selects.sid,s.sname,c.cid,c.cnamefromt_stu s left join t_clazz c on s.cid = c.cidwheres.sid = #{sid}</select>
Test
@Testpublic void testSelectByIdAssociation(){SqlSession sqlSession = SqlSessionUtil.openSession();StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);Student student = mapper.selectByIdAssociation(4);System.out.println(student);sqlSession.close();}
⑶.第三種方式:分步查詢
其他位置不需要修改,只需要修改以及添加以下三處:
第一處:association中select位置填寫sqlId。sqlId=namespace+id。其中column屬性作為這條子sql語句的條件。
第一步:現在Student根據Id查詢學生信息
StudentMapper接口
package org.example1.mapper;import org.example1.pojo.Student;import java.util.List;public interface StudentMapper {/*** 分部查詢第一步:先根據學生的sid查詢學生的信息。* @param sid* @return*/Student selectByIdStep1(Integer sid);}
<!--兩條SQL語句,完成多對一的分步查詢。--><!--這里是第一步:根據學生的id查詢學生的所有信息。這些信息當中含有班級id(cid)--><resultMap id="studentResultMapByStep" type="Student"><id property="sid" column="sid"/><result property="sname" column="sname"/><association property="clazz"select=""column="cid"/></resultMap><select id="selectByIdStep1" resultMap="studentResultMapByStep">select sid,sname,cid from t_stu where sid = #{sid}</select>
分步查詢第二步:根據cid獲取班級信息。
ClazzMapper接口
package org.example1.mapper;import org.example1.pojo.Clazz;public interface ClazzMapper {/*** 分步查詢第二步:根據cid獲取班級信息。* @param cid* @return*/Clazz selectByIdStep2(Integer cid);
}
<!--分步查詢第二步:根據cid獲取班級信息。--><select id="selectByIdStep2" resultType="Clazz">select cid,cname from t_clazz where cid = #{cid}</select>
把第一查出來的cid傳給第二步(根據cid獲取班級信息)的寫法
test
@Testpublic void testSelectByIdStep1(){SqlSession sqlSession = SqlSessionUtil.openSession();StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);Student student = mapper.selectByIdStep1(5);System.out.println(student);// 只需要看學生的名字System.out.println(student.getSname());// 程序執行到這里了,我想看看班級的名字System.out.println(student.getClazz().getCname());sqlSession.close();}
分步優點:
-
第一個優點:代碼復用性增強。
-
第二個優點:支持延遲加載。【暫時訪問不到的數據可以先不查詢。提高程序的執行效率。】
2.多對一延遲加載
要想支持延遲加載,非常簡單,只需要在association標簽中添加fetchType="lazy"即可。 修改StudentMapper.xml文件:
@Testpublic void testSelectByIdStep1(){SqlSession sqlSession = SqlSessionUtil.openSession();StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);Student student = mapper.selectByIdStep1(5);// 只需要看學生的名字System.out.println(student.getSname());// 程序執行到這里了,我想看看班級的名字//System.out.println(student.getClazz().getCname());sqlSession.close();}
只需要看學生的名字
看班級的名字
<!--分步查詢的優點:第一:復用性增強。可以重復利用。(大步拆成N多個小碎步。每一個小碎步更加可以重復利用。)第二:采用這種分步查詢,可以充分利用他們的延遲加載/懶加載機制。什么是延遲加載(懶加載),有什么用?延遲加載的核心原理是:用的時候再執行查詢語句。不用的時候不查詢。作用:提高性能。盡可能的不查,或者說盡可能的少查。來提高效率。在mybatis當中怎么開啟延遲加載呢?association標簽中添加fetchType="lazy"注意:默認情況下是沒有開啟延遲加載的。需要設置:fetchType="lazy"這種在association標簽中配置fetchType="lazy",是局部的設置,只對當前的association關聯的sql語句起作用。在實際的開發中,大部分都是需要使用延遲加載的,所以建議開啟全部的延遲加載機制:在mybatis核心配置文件中添加全局配置:lazyLoadingEnabled=true實際開發中的模式:把全局的延遲加載打開。如果某一步不需要使用延遲加載,請設置:fetchType="eager" -->
開啟全局延遲加載之后,所有的sql都會支持延遲加載,如果某個sql你不希望它支持延遲加載怎么辦呢?將fetchType設置為eager:
<resultMap id="studentResultMap" type="Student"><id property="sid" column="sid"/><result property="sname" column="sname"/><association property="clazz"select="org.example1.mapper.ClazzMapper.selectByCid"column="cid"fetchType="eager"/>
</resultMap>
3.一對多
一對多的實現,通常是在一的一方中有List集合屬性。 在Clazz類中添加List<Student> stus; 屬性。
package org.example1.pojo;import java.util.List;/*** 班級信息*/
public class Clazz {private Integer cid;private String cname;private List<Student> stus;@Overridepublic String toString() {return "Clazz{" +"cid=" + cid +", cname='" + cname + '\'' +", stus=" + stus +'}';}public List<Student> getStus() {return stus;}public void setStus(List<Student> stus) {this.stus = stus;}public Integer getCid() {return cid;}public void setCid(Integer cid) {this.cid = cid;}public String getCname() {return cname;}public void setCname(String cname) {this.cname = cname;}public Clazz() {}public Clazz(Integer cid, String cname) {this.cid = cid;this.cname = cname;}
}
一對多的實現通常包括兩種實現方式:
-
第一種方式:collection
-
第二種方式:分步查詢
⑴.第一種方式:collection
package org.example1.mapper;import org.example1.pojo.Clazz;public interface ClazzMapper {/*** 根據班級編號查詢班級信息。* @param cid* @return*/Clazz selectByCollection(Integer cid);}
<resultMap id="clazzResultMap" type="Clazz"><id property="cid" column="cid"/><result property="cname" column="cname"/><!--一對多,這里是collection。collection是集合的意思。--><!--ofType 屬性用來指定集合當中的元素類型。--><collection property="stus" ofType="Student"><id property="sid" column="sid"/><result property="sname" column="sname"/></collection></resultMap><select id="selectByCollection" resultMap="clazzResultMap">select c.cid,c.cname,s.sid,s.sname from t_clazz c left join t_stu s on c.cid = s.cid where c.cid = #{cid}</select>
注意是ofType,表示“集合中的類型”。
Test
@Testpublic void testSelectByCollection(){SqlSession sqlSession = SqlSessionUtil.openSession();ClazzMapper mapper = sqlSession.getMapper(ClazzMapper.class);Clazz clazz = mapper.selectByCollection(1000);System.out.println(clazz);sqlSession.close();}
⑵.第二種方式:分步查詢
分步查詢。第一步:根據班級編號獲取班級信息。
ClazzMapper接口
package org.example1.mapper;import org.example1.pojo.Clazz;public interface ClazzMapper {/*** 分步查詢。第一步:根據班級編號獲取班級信息。* @param cid 班級編號* @return*/Clazz selectByStep1(Integer cid);}
<!--分步查詢第一步:根據班級的cid獲取班級信息。--><resultMap id="clazzResultMapStep" type="Clazz"><id property="cid" column="cid"/><result property="cname" column="cname"/><collection property="stus"select="org.example1.mapper.StudentMapper.selectByCidStep2"column="cid" fetchType="eager" /></resultMap><select id="selectByStep1" resultMap="clazzResultMapStep">select cid,cname from t_clazz where cid = #{cid}</select>
第二步:根據班級編號查詢學生信息。
package org.example1.mapper;import org.example1.pojo.Student;import java.util.List;public interface StudentMapper {/*** 根據班級編號查詢學生信息。* @param cid* @return*/List<Student> selectByCidStep2(Integer cid);}
<select id="selectByCidStep2" resultType="Student">select * from t_stu where cid = #{cid}</select>
Test
@Testpublic void testSelectByStep1(){SqlSession sqlSession = SqlSessionUtil.openSession();ClazzMapper mapper = sqlSession.getMapper(ClazzMapper.class);Clazz clazz = mapper.selectByStep1(1000);System.out.println(clazz);sqlSession.close();}
4.一對多延遲加載
一對多延遲加載機制和多對一是一樣的。同樣是通過兩種方式:
-
第一種:fetchType="lazy"
-
第二種:修改全局的配置setting,lazyLoadingEnabled=true,如果開啟全局延遲加載,想讓某個sql不使用延遲加載:fetchType="eager"