MyBatis復(fù)雜Sql查詢實(shí)現(xiàn)示例介紹
resultMap 結(jié)果映射
resultMap 元素是 MyBatis 中最重要最強(qiáng)大的元素,之前所寫的 sql 語句,返回值都是簡(jiǎn)單的基本數(shù)據(jù)類型或者某一個(gè)實(shí)體類,比如下面這段 sql 返回的就是最簡(jiǎn)單的 User 類型。
<select id="getUserById" resultType="user" parameterType="int">
select * from user where id=#{id};
</select>
現(xiàn)在思考一下下面這種情況,如果實(shí)體類中定義的某一個(gè)字段和數(shù)據(jù)庫中的字段不一致怎么辦?
public class User {
private int id;
private String lastname;
//.....
}
比如我定義了一個(gè) User 類,包含 id 和 lastname 兩個(gè)屬性,而數(shù)據(jù)庫中這兩個(gè)字段的名字為 id 和 name。此時(shí)再執(zhí)行查詢時(shí)結(jié)果如下:lastname 這個(gè)字段直接為 null。
這時(shí)候我們就可以使用 resultMap 來解決這個(gè)問題,resultMap 可以將數(shù)據(jù)庫中的字段映射到實(shí)體類上。column 代表數(shù)據(jù)庫中的字段名,properties 代表實(shí)體類中的字段名,通過映射之后 Mybatis 就可以找到對(duì)應(yīng)的字段。
<resultMap id="UserMap" type="User">
<!--column代表數(shù)據(jù)庫中的字段名,properties代表實(shí)體類中的字段名-->
<result column="id" property="id"/>
<result column="name" property="lastname"/>
</resultMap>
<select id="getUserById" resultMap="UserMap" parameterType="int">
select * from user where id=#{id};
</select>
準(zhǔn)備數(shù)據(jù)
接下來結(jié)合學(xué)生與教室的案例模擬復(fù)雜場(chǎng)景:
//創(chuàng)建教室表 create table classroom ( id int not null AUTO_INCREMENT, classname VARCHAR(40) not null, PRIMARY KEY (id) ); //創(chuàng)建學(xué)生表 create table student ( id int not null AUTO_INCREMENT, name VARCHAR(40) not null, classid int not null, PRIMARY KEY (id), FOREIGN key (classid) REFERENCES classroom(id) ); //創(chuàng)建一些數(shù)據(jù) insert into classroom VALUES (1,'101班'); insert into classroom VALUES (2,'102班'); insert into student VALUES(1,'Amy',1); insert into student VALUES(2,'Bob',1); insert into student VALUES(3,'javayz',1);
多對(duì)一查詢(association)
現(xiàn)在要實(shí)現(xiàn)一個(gè)多對(duì)一的查詢需求,查詢所有的學(xué)生,并將每個(gè)學(xué)生所在的教室包含在內(nèi)。由于現(xiàn)在的情況是多學(xué)生和教室的關(guān)系是多對(duì)一,因此在構(gòu)建實(shí)體類時(shí)在 Student 類上要加上 ClassRoom 變量。
在 Java 的實(shí)體類代碼中分別建立 Student 和 ClassRoom 的類:
package com.cn.pojo;
public class ClassRoom {
private int id;
private String classname;
public ClassRoom(){}
public int getId() {
return id;
}
public String getClassname() {
return classname;
}
public void setId(int id) {
this.id = id;
}
public void setClassname(String classname) {
this.classname = classname;
}
@Override
public String toString() {
return "ClassRoom{" +
"id=" + id +
", classname='" + classname + '\'' +
'}';
}
}package com.cn.pojo;
public class Student {
private int id;
private String name;
private ClassRoom classRoom;
public Student(){}
public int getId() {
return id;
}
public String getName() {
return name;
}
public ClassRoom getClassRoom() {
return classRoom;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setClassRoom(ClassRoom classRoom) {
this.classRoom = classRoom;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", classRoom=" + classRoom +
'}';
}
}定義一個(gè) StudentMapper 接口:
package com.cn.mapper;
import java.util.List;
import com.cn.pojo.Student;
public interface StudentMapper {
List<Student> selectAllStudent();
}
編寫 StudentMapper.xml
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cn.mapper.StudentMapper">
<select id="selectAllStudent" resultMap="StudentAndClassRoom">
select s.id sid,s.name sname,c.id cid,c.classname cname
from student s,classroom c
where s.classid=c.id
</select>
<resultMap id="StudentAndClassRoom" type="com.cn.pojo.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="classRoom" javaType="com.cn.pojo.ClassRoom">
<result property="id" column="cid"/>
<result property="classname" column="cname"/>
</association>
</resultMap>
</mapper>上面的這種 sql 編寫模式稱為結(jié)果嵌套查詢,首先通過一段 sql 查詢語句將需要的信息查詢出來,接著通過 resultMap 對(duì)結(jié)果進(jìn)行拼接。這里使用 association 將 classRoom 的信息拼接到了 classRoom 類中,實(shí)現(xiàn)多對(duì)一查詢。
別忘了在配置類里把 mapper 映射加上,編寫測(cè)試類:
public class StudentMapperTest {
public static void main(String[] args) {
// 獲取SqlSession
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.selectAllStudent();
System.out.println(students);
}
}一對(duì)多查詢(collection)
一個(gè)教室里有多個(gè)學(xué)生,如果想要查詢每個(gè)教室中的所有學(xué)生,就會(huì)用到一對(duì)多查詢。
修改兩個(gè)實(shí)體類,命名為 Student2 和 ClassRoom2,因?yàn)橐粋€(gè)教室中有多個(gè)學(xué)生,因此在教室類中通過 List<Student2> 的方式引入 Student2 類
public class Student2 {
private int id;
private String name;
private int classId;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", classId=" + classId +
'}';
}
}import java.util.List;
public class ClassRoom2 {
private int id;
private String classname;
private List<Student2> students;
@Override
public String toString() {
return "ClassRoom{" +
"id=" + id +
", classname='" + classname + '\'' +
", students=" + students +
'}';
}
}接著編寫 Mapper 接口和對(duì)應(yīng)的 Mapper.xml
import java.util.List;
import com.cn.pojo.ClassRoom2;
public interface ClassRoomMapper {
List<ClassRoom2> getClassRoomByid( int id);
}<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cn.mapper.ClassRoomMapper">
<select id="getClassRoomByid" resultMap="ClassRoomAndStudent" parameterType="int">
select c.id cid,c.classname cname,s.id sid,s.name sname,s.classid classid
from student s,classroom c
where s.classid=c.id and c.id=#{id}
</select>
<resultMap id="ClassRoomAndStudent" type="com.cn.pojo.ClassRoom2">
<result property="id" column="cid"/>
<result property="classname" column="cname"/>
<!--對(duì)于集合屬性,需要使用collection來實(shí)現(xiàn)-->
<collection property="students" ofType="com.cn.pojo.Student2">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="classId" column="classid"/>
</collection>
</resultMap>
</mapper>
依舊是通過結(jié)果嵌套查詢的方式,通過 sql 語句查詢出結(jié)果,再通過 resultMap 進(jìn)行組裝,一對(duì)多查詢用的是 collection。
在配置文件中增加 mapper 映射器之后,編寫一個(gè)測(cè)試類:
public class TeacherMapperTest {
public static void main(String[] args) {
// 獲取SqlSession
SqlSession sqlSession = MyBatisUtils.getSqlSession();
ClassRoomMapper mapper = sqlSession.getMapper(ClassRoomMapper.class);
List<ClassRoom2> classRoom = mapper.getClassRoomByid(1);
System.out.println(classRoom);
}
}
總結(jié)成一點(diǎn):對(duì)象的關(guān)聯(lián)(多對(duì)一)使用 association,集合的關(guān)聯(lián)(一對(duì)多)使用 collection。
懶加載
在上面的兩個(gè)例子中,一次 sql 查詢就將兩個(gè)表的數(shù)據(jù)一次性查詢了出來,這種方式就是即時(shí)加載。但是在某些業(yè)務(wù)場(chǎng)景下,可能只需要學(xué)生的信息或者教室的信息,而不需要兩者的聯(lián)表數(shù)據(jù),這種時(shí)候就可以使用懶加載。
以上邊的 association 案例解釋懶加載的實(shí)現(xiàn)。
上邊的例子中,通過聯(lián)表查詢一次性就查詢出了學(xué)生信息和教室信息:
select s.id sid,s.name sname,c.id cid,c.classname cname from student s,classroom c where s.classid=c.id
如果想要通過懶加載實(shí)現(xiàn),就需要把 sql 語句轉(zhuǎn)換為:
select * from student;
select * from classroom where id = #{classid}
按照這個(gè)思路,建立 StudentLazyMapper 類:
package com.cn.mapper;
import java.util.List;
import com.cn.pojo.Student;
public interface StudentLazyMapper {
List<Student> selectAllStudent();
}創(chuàng)建對(duì)應(yīng)的 StudentLazyMapper.xml 文件,將原先的一條 sql 轉(zhuǎn)換為兩條 sql:
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cn.mapper.StudentLazyMapper">
<select id="selectAllStudent" resultMap="studentAndClassRoom">
select * from student
</select>
<resultMap id="studentAndClassRoom" type="com.cn.pojo.Student">
<id property="id" column="id"/>
<result property="name" column="name"/>
<association property="classRoom" javaType="com.cn.pojo.ClassRoom" column="classid" select="selectClassRoomById">
<result property="id" column="id"/>
<result property="classname" column="classname"/>
</association>
</resultMap>
<select id="selectClassRoomById" resultType="com.cn.pojo.ClassRoom">
select * from classroom where id = #{classid}
</select>
</mapper>在 mybatis-config.xml 中增加 mapper 映射,為了更好地看到懶加載效果,開啟控制臺(tái)日志輸出,完整 xml 如下:
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<!--每個(gè)mapper.xml都需要在mybatis配置文件中進(jìn)行配置-->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
<mapper resource="mapper/StudentMapper.xml"/>
<mapper resource="mapper/StudentLazyMapper.xml"/>
</mappers>
</configuration>新建一個(gè)測(cè)試類 StudentMapperLazyTest:
public class StudentMapperLazyTest {
public static void main(String[] args) {
// 獲取SqlSession
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentLazyMapper mapper = sqlSession.getMapper(StudentLazyMapper.class);
List<Student> students = mapper.selectAllStudent();
System.out.println(students.get(0).getId());
}
}
這個(gè)時(shí)候是還沒開啟懶加載的,從運(yùn)行結(jié)果可以看出,雖然代碼中只需要得到 student 的 id,但是卻查詢了兩張表:

在配置文件的 setting 節(jié)點(diǎn)下開啟懶加載的配置:
<setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/>
再次運(yùn)行測(cè)試代碼:

可以看到,只有 student 一張表被查詢,實(shí)現(xiàn)了懶加載
到此這篇關(guān)于MyBatis復(fù)雜Sql查詢實(shí)現(xiàn)示例介紹的文章就介紹到這了,更多相關(guān)MyBatis Sql查詢內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單的圖片加載器
這篇文章主要為大家詳細(xì)介紹了如何使用Java?2D?API(Java的繪圖和圖像API)來實(shí)現(xiàn)圖片加載和顯示,文中的示例代碼講解詳細(xì),需要的小伙伴可以參考一下2023-11-11
Java中websocket消息推送的實(shí)現(xiàn)代碼
這篇文章主要介紹了Java中websocket消息推送的實(shí)現(xiàn)代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-02-02
SpringBoot ThreadLocal 簡(jiǎn)單介紹及使用詳解
ThreadLocal 叫做線程變量,意思是 ThreadLocal 中填充的變量屬于當(dāng)前線程,該變量對(duì)其他線程而言是隔離的,也就是說該變量是當(dāng)前線程獨(dú)有的變量,這篇文章主要介紹了SpringBoot ThreadLocal 的詳解,需要的朋友可以參考下2024-01-01
Java案例使用集合方法實(shí)現(xiàn)統(tǒng)計(jì)任意字符串中字符出現(xiàn)的次數(shù)
這篇文章主要介紹了Java案例使用集合方法實(shí)現(xiàn)統(tǒng)計(jì)任意字符串中字符出現(xiàn)的次數(shù),下面我們將用兩種方法實(shí)現(xiàn),需要的小伙伴可以參考一下文章具體內(nèi)容2022-04-04
Springboot+Flowable?快速實(shí)現(xiàn)工作流的開發(fā)流程
這篇文章主要介紹了Springboot+Flowable?快速實(shí)現(xiàn)工作流的開發(fā)流程,本文通過實(shí)例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02
在controller中如何設(shè)置接收參數(shù)的默認(rèn)值
這篇文章主要介紹了在controller中如何設(shè)置接收參數(shù)的默認(rèn)值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Java實(shí)現(xiàn)Json字符串與Object對(duì)象相互轉(zhuǎn)換的方式總結(jié)
這篇文章主要介紹了Java實(shí)現(xiàn)Json字符串與Object對(duì)象相互轉(zhuǎn)換的方式,結(jié)合實(shí)例形式總結(jié)分析了java基于Json-Lib、Org.Json、Jackson、Gson、FastJson五種方式轉(zhuǎn)換json類型相關(guān)操作技巧,需要的朋友可以參考下2019-03-03
教你如何在IDEA?中添加?Maven?項(xiàng)目的?Archetype(解決添加不起作用的問題)
這篇文章主要介紹了如何在?IDEA?中添加?Maven?項(xiàng)目的?Archetype(解決添加不起作用的問題),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08
Java與C++分別用遞歸實(shí)現(xiàn)漢諾塔詳解
漢諾塔問題是一個(gè)經(jīng)典的問題。漢諾塔(Hanoi Tower),又稱河內(nèi)塔,源于印度一個(gè)古老傳說。本文將用Java與C++分別實(shí)現(xiàn),感興趣的可以學(xué)習(xí)一下2022-05-05
Java多線程常見案例分析線程池與單例模式及阻塞隊(duì)列
這篇文章主要介紹了多線程的常見案例,線程池(重點(diǎn))、單例模式、阻塞隊(duì)列,本文通過圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05

