MyBatis——多對一查詢

1 實驗環境

本文以 Student (many)——Class (one) 爲例,介紹 MyBatis 中,多對一查詢。

(1)導入 JAR 包

其中,前2個 jar 包下載地址見 → log4j-1.2.17.jar、 mybatis-3.4.1.jar,將 jar 包放入 lib 目錄下,並選中所有 jar 包,右鍵,選擇【Add to Build Path】。

(2) 工作目錄 

注意:src 和 conf 目錄下的 com.mapper 包必須同名;ClassMapper.java 和 ClassMapper.xml 僅在2.3節中使用到。

(3)配置文件 

log4j.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
 
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
 
	<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
		<param name="Encoding" value="UTF-8" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L)\n" />
		</layout>
	</appender>
 
	<logger name="java.sql">
		<level value="debug" />
	</logger>
 
	<logger name="org.apache.ibatis">
		<level value="info" />
	</logger>
 
	<root>
		<priority value="debug" />
		<appender-ref ref="STDOUT" />
	</root>
</log4j:configuration>

注意:log4j.xml文件名不能隨意更改。   

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
 
<configuration>
	<!-- 設置或引入資源文件 -->
	<properties resource="jdbc.properties"></properties>
	
	<!-- 設置連接數據庫的環境,default用於設置默認使用的數據庫環境 -->
	<environments default="mysql">
		<!-- 設置某個具體的數據庫環境 -->
		<environment id="mysql">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	
	<!-- 引入映射文件 -->
	<mappers>
		<package name="com.mapper"/>
	</mappers>
</configuration>

jdbc.properties

# K = V
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/users
jdbc.username=root
jdbc.password=0.

2 案例分析

首先在 MySQL 中創建數據庫:users,再在此數據庫中創建表:students 和 classes。

students 包含 sid(int)、sname(varchar)和 cid(int) 3個字段,其中,sid 設置了自增,cid 爲外鍵,指向 classes 表中的 cid,students 表中數據如下:

students

classes 包含 cid(int)、cname(varchar)2個字段,其中,cid 設置了自增,classes 表中數據如下: 

classes

首先介紹下公共的文件,主要包含 Student.java、Class.java、StudentClassMapper.java、Test.java,不同的是 StudentClassMapper.xml,將在各章節分別介紹。

Student.java

package com.bean;
 
public class Student {
	private Integer sid;
	private String sname;
	private Class cla; 
	
	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 Class getCla() {
		return cla;
	}

	public void setCla(Class cla) {
		this.cla = cla;
	}

	@Override
	public String toString() {
		return "Student [sid=" + sid + ", sname=" + sname +  ", cla=" + cla + "]";
	}
}

Class.java

package com.bean;

public class Class {
	private Integer cid;
	private String cname;
	
	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;
	}

	@Override
	public String toString() {
		return "Class [cid=" + cid + ", cname=" + cname + "]";
	}
}

StudentClassMapper.java

package com.mapper;
 
import com.bean.Student;
 
public interface StudentClassMapper {
	public Student getStudentById(Integer sid);
}

Test.java

package com.test;
 
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
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 com.bean.Student;
import com.mapper.StudentClassMapper;
 
public class Test {
	
	public static void main(String[] args) throws IOException {
		InputStream is=Resources.getResourceAsStream("mybatis-config.xml");
		SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
		SqlSession sqlSession=sqlSessionFactory.openSession(true); //自動提交事務
		//getMapper:會通過動態代理動態生成StudentMapper的代理實現類
		StudentClassMapper mapper=sqlSession.getMapper(StudentClassMapper.class);
		
		Student student=mapper.getStudentById(1001);
		System.out.println(student);
	}
}

2.1 方法一

StudentClassMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
<mapper namespace="com.mapper.StudentClassMapper">
	<resultMap type="com.bean.Student" id="studentMap">
		<id column="sid" property="sid"/>
		<result column="sname" property="sname"/>
		<result column="cid" property="cla.cid"/>
		<result column="cname" property="cla.cname"/>
	</resultMap>
	
	<!-- public Student getStudentById(Integer sid); -->
	<select id="getStudentById" resultMap="studentMap">
		select s.sid,s.sname,s.cid,c.cname from students s left join classes c on s.cid=c.cid where s.sid = #{sid}
	</select>
</mapper>

注意:<resultMap> 用於定義從數據庫查詢出來的數據與對象的映射關係,即:記錄屬性→對象屬性, <id> 用於定義主鍵映射關係,<result> 用於定義其他屬性映射關係。

運行結果:

DEBUG 06-13 18:43:11,039 ==>  Preparing: select s.sid,s.sname,s.cid,c.cname from students s left join classes c on s.cid=c.cid where s.sid=?
Student [sid=1001, sname=張三, cla=Class [cid=1, cname=一班]]

2.2 方法二(association標籤 )

StudentClassMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
<mapper namespace="com.mapper.StudentClassMapper">
	<resultMap type="com.bean.Student" id="studentMap">
		<id column="sid" property="sid"/>
		<result column="sname" property="sname"/>
		<result column="cid" property="cla.cid"/>
		<association property="cla" javaType="com.bean.Class">
			<id column="cid" property="cid"/>
			<result column="cname" property="cname"/>
		</association>
	</resultMap>
	
	<!-- public Student getStudentById(Integer sid); -->
	<select id="getStudentById" resultMap="studentMap">
		select s.sid,s.sname,s.cid,c.cname from students s left join classes c on s.cid=c.cid where s.sid=#{sid}
	</select>
</mapper>

注意:<association>  標籤定義了多對一映射關係。

運行結果:

DEBUG 06-13 18:44:24,441 ==>  Preparing: select s.sid,s.sname,s.cid,c.cname from students s left join classes c on s.cid=c.cid where s.sid=?
Student [sid=1001, sname=張三, cla=Class [cid=1, cname=一班]]

2.3 方法三(分步查詢)

分兩步:首先查詢學生信息,再查詢班級信息。

ClassMapper.java

package com.mapper;
 
import com.bean.Class;
 
public interface ClassMapper {
	public Class getClassById(Integer cid);
}

ClassMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
<mapper namespace="com.mapper.ClassMapper">
	<!-- public Class getClassById(Integer cid); -->
	<select id="getClassById" resultType="com.bean.Class">
		select cid,cname from classes where cid = #{cid}
	</select>
</mapper>

StudentClassMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
<mapper namespace="com.mapper.StudentClassMapper">
	<resultMap type="com.bean.Student" id="studentMap">
		<id column="sid" property="sid"/>
		<result column="sname" property="sname"/>
		<result column="cid" property="cla.cid"/>
		<association property="cla" select="com.mapper.ClassMapper.getClassById" column="cid"/>
	</resultMap>
	
	<!-- public Student getStudentById(Integer sid); -->
	<select id="getStudentById" resultMap="studentMap">
		select sid,sname,cid from students where sid=#{sid}
	</select>
</mapper>

注意:<association> 標籤中,select 爲第二步查詢的 SQL id,即接口的全限定名,column 爲第二步查詢的條件,此條件必須是從數據庫中查詢得到。

運行結果:

DEBUG 06-13 18:48:59,783 ==>  Preparing: select sid,sname,cid from students where sid=? 
DEBUG 06-13 18:48:59,829 ====>  Preparing: select cid,cname from classes where cid = ? 
Student [sid=1001, sname=張三, cla=Class [cid=1, cname=一班]]

2.4 分步查詢的延遲加載

 延遲加載也稱爲懶加載,是指使用時才加載,不使用就不加載,只有分步查詢才能實現延遲加載。爲查看並比較延遲加載的效果,進行了如下3個測試實驗。

測試一(沒有設置延遲加載,使用第一步查詢的數據)

Test.java

package com.test;
 
import java.io.IOException;
import java.io.InputStream;
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 com.bean.Student;
import com.mapper.StudentClassMapper;
 
public class Test {
	
	public static void main(String[] args) throws IOException {
		InputStream is=Resources.getResourceAsStream("mybatis-config.xml");
		SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
		SqlSession sqlSession=sqlSessionFactory.openSession(true); //自動提交事務
		//getMapper:會通過動態代理動態生成StudentMapper的代理實現類
		StudentClassMapper mapper=sqlSession.getMapper(StudentClassMapper.class);
		
		Student student=mapper.getStudentById(1001);
		System.out.println(student.getSname());
	}
}

運行結果:

DEBUG 06-13 19:19:03,149 ==>  Preparing: select sid,sname,cid from students where sid=?  
DEBUG 06-13 19:19:03,195 ====>  Preparing: select cid,cname from classes where cid = ?  
張三

 可以看到:在沒有設置延遲加載時,只使用第一步查詢的數據,仍然會執行第二步查詢語句。

測試二(設置延遲加載,使用第一步查詢的數據)

本節 Test.java 同測試一。設置延遲加載,需要在 mybatis-config.xml 的<settings>標籤中配置,如下。

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
	<!-- 設置或引入資源文件 -->
	<properties resource="jdbc.properties"></properties>
	
	<settings>
		<!-- 開啓延遲加載 -->
		<setting name="lazyLoadingEnabled" value="true"/>
		<!-- 延遲加載中,不查詢所有數據 -->
		<setting name="aggressiveLazyLoading" value="flase"/>
	</settings>
	
	<!-- 設置連接數據庫的環境,default用於設置默認使用的數據庫環境 -->
	<environments default="mysql">
		<!-- 設置某個具體的數據庫環境 -->
		<environment id="mysql">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	
	<!-- 引入映射文件 -->
	<mappers>
		<package name="com.mapper"/>
	</mappers>
</configuration>

注意: settings 標籤必須放在 properties 標籤之後,否則會報錯:

The content of element type "configuration" must match 
 "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".

運行結果:

DEBUG 06-13 19:33:21,338 ==>  Preparing: select sid,sname,cid from students where sid=?  
張三

可以看到:在設置了延遲加載時,只使用第一步查詢的數據,不會執行第二步查詢語句。

測試三(設置延遲加載,使用第二步查詢的數據)

mybatis-config.xml 同測試二。

Test.java

package com.test;
 
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
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 com.bean.Student;
import com.mapper.StudentClassMapper;
 
public class Test {
	
	public static void main(String[] args) throws IOException {
		InputStream is=Resources.getResourceAsStream("mybatis-config.xml");
		SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
		SqlSession sqlSession=sqlSessionFactory.openSession(true); //自動提交事務
		//getMapper:會通過動態代理動態生成StudentMapper的代理實現類
		StudentClassMapper mapper=sqlSession.getMapper(StudentClassMapper.class);
		
		Student student=mapper.getStudentById(1001);
		System.out.println("==========================");
		System.out.println(student.getCla());
	}
}

運行結果:

DEBUG 06-13 19:42:08,299 ==>  Preparing: select sid,sname,cid from students where sid=?  
==========================
DEBUG 06-13 19:42:08,394 ==>  Preparing: select cid,cname from classes where cid = ?  
Class [cid=1, cname=一班]

可以看到:在設置了延遲加載時,使用了第二步查詢的數據,纔會執行第二步查詢語句。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章