MyBatis查詢語句專題、動態SQL、MyBatis的高級映射及延遲加載

一、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"

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/900403.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/900403.shtml
英文地址,請注明出處:http://en.pswp.cn/news/900403.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Visual Studio Code SSH 連接超時對策( keep SSH alive)

文章目錄 問題解決方法一&#xff1a;配置服務端關于ClientAliveInterval和ClientAliveCountMax1、打開終端&#xff0c;打開SSH配置文件&#xff1a;輸入以下命令&#xff1a;2、打開配置文件后&#xff0c;添加以下內容&#xff1a;3、添加后&#xff0c;Esc按 <Enter>…

學透Spring Boot — 014. Spring MVC的自動配置

這是學透Spring Boot的第14篇文章&#xff0c;更多文章請移步我的專欄&#xff1a; 學透 Spring Boot_postnull咖啡的博客-CSDN博客 目錄 沒有Spring Boot時的Spring MVC 使用Spring Boot后的Spring MVC Spring MVC的自動配置解析 明確目標 入口類 Spring容器的啟動 S…

SQL語句(三)—— DQL

目錄 基本語法 一、基礎查詢 1、查詢多個字段 2、字段設置別名 3、去除重復記錄 4、示例代碼 二、條件查詢 1、語法 2、條件列表常用的運算符 3、示例代碼 三、分組查詢 &#xff08;一&#xff09;聚合函數 1、介紹 2、常見的聚合函數 3、語法 4、示例代碼 &…

LENOVO聯想ThinkBook 16 G6 ABP(21KK)恢復預裝OEM原廠Win11系統鏡像

適用機型&#xff1a;【21KK】 鏈接&#xff1a;https://pan.baidu.com/s/1lbvIh4KTbqm8EZQZfxvNIQ?pwd7vp0 提取碼&#xff1a;7vp0 聯想原裝系統自帶所有驅動、出廠主題壁紙、系統屬性聯機支持標志、Office辦公軟件、聯想瀏覽器、聯想電腦管家、聯想軟件商店、聯想智能引…

# 基于人臉關鍵點的多表情實時檢測系統

基于人臉關鍵點的多表情實時檢測系統 在計算機視覺領域&#xff0c;人臉表情識別技術已經取得了顯著的進展。它不僅可以用于娛樂應用&#xff08;如動態表情包生成&#xff09;&#xff0c;還能在心理健康監測、智能安防、人機交互等領域發揮重要作用。今天&#xff0c;我將分…

在 Ubuntu24.04 LTS 上 Docker Compose 部署基于 Dify 重構二開的開源項目 Dify-Plus

一、安裝環境信息說明 硬件資源&#xff08;GB 和 GiB 的主要區別在于它們的換算基數不同&#xff0c;GB 使用十進制&#xff0c;GiB 使用二進制&#xff0c;導致相同數值下 GiB 表示的容量略大于 GB&#xff1b;換算關系&#xff1a;1 GiB ≈ 1.07374 GB &#xff1b;1 GB ≈ …

SQL Server存儲過程和觸發器的使用

存儲過程 &#xff08;1&#xff09;創建存儲過程&#xff0c;使用Employees表中的員工人數來初始化一個局部變量&#xff0c;并調用這個存儲過程。 1. Create PROCEDURE test number1 int output --輸出參數&#xff0c;可以從程序中返回信息 2. As 3. begin 4. D…

子類是否能繼承

繼承 父類&#xff1a; 子 類 構造方法 非私有 不能繼承 私有&#xff08;private&#xff09;不能繼承 成員變量 非私有 能繼承 私有&…

2025年【山東省安全員C證】考試題及山東省安全員C證考試內容

在當今建筑行業蓬勃發展的背景下&#xff0c;安全生產已成為企業生存與發展的基石。安全員作為施工現場安全管理的直接責任人&#xff0c;其專業能力和資質認證顯得尤為重要。山東省安全員C證作為衡量安全員專業水平的重要標準&#xff0c;不僅關乎個人職業發展&#xff0c;更直…

Spring 中的 bean 生命周期

&#x1f331; 一、什么是 Bean 生命周期&#xff1f; 在 Spring 容器中&#xff0c;一個 Bean 從“創建 → 初始化 → 使用 → 銷毀”&#xff0c;經歷了完整的生命周期。 Spring 提供了 多個擴展點 讓你可以在這些階段做事情&#xff0c;比如注入資源、日志記錄、連接資源、清…

Media streaming mental map

Media streaming is a huge topic with a bunch of scattered technologies, protocols, and formats. You may feel like hearing fragments without seeing the big picture. Let’s build that mental map together — here’s a high-level overview that connects everyt…

AIDD-深度學習 MetDeeCINE 破譯代謝調控機制

深度學習 MetDeeCINE 破譯代謝調控機制 目錄 使用 FEP/REMD 和 DFT 方法準確預測藥物多靶點絕對結合自由能的新途徑。Scorpio 框架利用對比學習優化核苷酸序列表示&#xff0c;提升基因組分析效率&#xff0c;尤其在未知序列的分類和泛化能力上表現出色。LPM 模型整合多模態擾…

【2】搭建k8s集群系列(二進制)之安裝etcd數據庫集群

一、etcd服務架構 Etcd 是一個分布式鍵值存儲系統&#xff0c;Kubernetes 使用 Etcd 進行數據存儲&#xff0c;所以先 準備一個 Etcd 數據庫&#xff0c;為解決 Etcd 單點故障&#xff0c;應采用集群方式部署&#xff0c;這里使用 3 臺組建集群&#xff0c;可容忍 1 臺機器故障…

fastGPT—前端開發獲取api密鑰調用機器人對話接口(HTML實現)

官網文檔鏈接&#xff1a;OpenAPI 介紹 | FastGPT 首先按照文檔說明創建api密鑰 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-sca…

cpp自學 day19(多態)

一、基本概念 同一操作作用于不同的對象&#xff0c;產生不同的執行結果 &#x1f449; 就像「按F1鍵」&#xff1a;在Word彈出幫助文檔&#xff0c;在PS彈出畫筆設置&#xff0c;?同一個按鍵觸發不同功能 &#xff08;1&#xff09;多態類型 類型實現方式綁定時機?靜態多態…

Java 大視界 -- Java 大數據在航天遙測數據分析中的技術突破與應用(177)

&#x1f496;親愛的朋友們&#xff0c;熱烈歡迎來到 青云交的博客&#xff01;能與諸位在此相逢&#xff0c;我倍感榮幸。在這飛速更迭的時代&#xff0c;我們都渴望一方心靈凈土&#xff0c;而 我的博客 正是這樣溫暖的所在。這里為你呈上趣味與實用兼具的知識&#xff0c;也…

人臉考勤管理一體化系統(人臉識別系統,簽到打卡)

人臉考勤管理一體化系統 項目介紹 本項目是基于Flask、SQLAlchemy、face_recognition庫的人臉考勤管理一體化系統。 系統通過人臉識別技術實現員工考勤打卡、人臉信息采集、人臉模型訓練等功能。 項目采用前后端分離的技術框架&#xff0c;基于Flask輕量級Web框架搭建后端服務…

單調棧學習C++

目錄 一&#xff0c;每日溫度 二&#xff0c;下一個更大的元素I 三&#xff0c;下一個更大的元素II 四&#xff0c;接雨水 小結&#xff1a; 單調棧是一種特殊的棧結構&#xff0c;里面的元素按照單調遞增或者遞減的順序排列。常用于解決元素左邊或者右邊比它大或者小的問…

網絡釣魚攻擊的威脅和執法部門的作用(第一部分)

在當今的數字世界中&#xff0c;網絡犯罪分子不斷開發新技術來利用個人、企業和政府機構。 最普遍和最具破壞性的網絡犯罪形式之一是網絡釣魚——一種社會工程手段&#xff0c;用于欺騙人們提供敏感信息&#xff0c;例如登錄憑據、財務數據和個人詳細信息。 隨著網絡釣魚攻擊…

左值與右值,空間與數據

左值是空間&#xff0c;右值是數據 編程總是對“數據”&#xff0c;對"存放數據的空間"操作 a返回一個當前的數據&#xff0c;存放到一個臨時空間中&#xff0c;自身的空間中的數據再進行運算 a直接對自身空間中的數據進行運算 其余知識&#xff1a; 1.變量名的意…