簡單查詢-resultType
數據準備
表 Student
字段 註釋 SNO 學號 SNAME 學生名字 SSEX 性別 SBIRITHDAY 生日 CLASS 班級
create table TEST.STUDENT
(
SNO varchar(3) not null,
SNAME varchar(4) not null,
SSEX varchar(2) not null,
SBIRTHDAY datetime null,
CLASS varchar(5) null
)
public class Student {
private String SNO;
private String SNAME;
private String SSEX;
private Date SBIRTHDAY;
private String CLASS;
...
}
例子
按照返回數據類型大致分為基礎數據類型,JavaBean 和 Map。其中雖然返回的結果行數有單條也有多條,對應的接口返回類型是集合或者單個對象,但是在 xml 映射文件中,resultType 的值是相同的。
- 指定字段-基礎數據類型
接口類:
String querySingleStudent();
List
Mapper 文件:
SELECT SNAME FROM TEST.STUDENT LIMIT 1
SELECT SNAME FROM TEST.STUDENT
- Map,一般為 hashMap
接口類:
Map
List
Mapper 文件:
SELECT SNAME FROM TEST.STUDENT LIMIT 1
SELECT SNAME FROM TEST.STUDENT
其中:
- hashmap 為簡寫,也可以使用 java.util.HashMap 全稱
- 默認情況下,結果集中值為 null 時, 不會增加映射對象的 setter 方法, (map 對象時為 put)。該行為可以在 mybatis-config.xml 配置文件中設置
覆蓋默認設定。
- JavaBean
接口類:
Student querySingleStudentBean();
List
Mapper 文件:
SELECT SNAME FROM TEST.STUDENT LIMIT 1
SELECT SNAME FROM TEST.STUDENT
- resultType="student" 為 Student.java 的別名,也可以是全限定名。別名在 mybatis-config.xml 配置文件中設置:
...
但是如果 JavaBean 文件很多,不想一個個指定,也可以使用 package 標籤 設置mybatis自動掃描,別名即為類名的小寫。
複雜查詢 resultMap
對於一般的查詢語句,resultType 足夠了。對於多表查詢等情況,就要請出 resultMap 了。
數據庫字段和 java 數據類型映射關係
數據庫字段類型 jdbcType 和 java 數據類型 並不是一一對應的關係,而且不同數據庫類型也不盡相同。而 mybatis 將 TypeHandler 作為兩者之間的映射關係。大部分情況下都是沒有問題的,但是並非能覆蓋所有的情況,特殊情況下可以使用 resultMap 自定義這種映射關係。
舉個例子,數據庫 LongVarchar 字段類型對應 java 中的 String 類型。但是在 DB2 數據庫中,查詢的 LongVarchar 類型的字段,在 mybatis 中被識別成 jdbcType 為 BLOB。有兩種解決方法,第一種是在 SQL 中對該字段使用 CAST 轉換為 VARCHAR(長度)類型。另一種是使用 resultMap:
...
標籤
- id 和 select 標籤指定映射關係
- type 和 resultType 一樣為返回類型的全限定名或者別名
- autoMapping 自動映射關係,在這裡目的只是修改一個字段,其他自動採用自動完成映射關係
標籤
- property 為 java 變量名
- column 為數據庫字段名
- jdbcType 這裡指定為 VARCHAR
id
字段的映射關係的標籤即有,也有,在 mybatis 文檔中指出不使用id,會造成性能下降,因此將主鍵字段使用 id 標籤是推薦的做法。但是如果不存在主鍵呢,當你在 ResultMap 只提供了部分字段而不是全部字段,即使使用了 autoMapping 屬性,那麼 mybatis 會按照你提供的字段名進行去重。那麼在使用 resultMap 的時候,最優選擇是:
- 如果表存在主鍵,就使用id標籤指定
- 如果表不存在主鍵,要麼不配置字段的映射關係,使用 autoMapping 屬性自動映射;或者不使用 autoMapping 將所有字段羅列。
多表關聯查詢
在 resultType 的例子中都只涉及到一張表,如果涉及多張表關聯查詢呢。我們可以簡單的將所有列映射到 hashmap 的鍵值上。
但是 HashMap 不是一個很好的領域模型。 你的程序更可能會使用 JavaBean 或 POJO(Plain Old Java Objects,普通 Java 對象)作為領域模型。
因此這裡均採用 JavaBean 作為領域模型。增加一個成績表 Score
字段 註釋 SNO 學號 CNO 課程編號 DEGREE 成績
create table SCORE
(
SNO varchar(3) not null,
CNO varchar(5) not null,
DEGREE decimal(10, 1) not null
)
public class Score {
private String SNO;
private String CNO;
private Double DEGREE;
...
}
一對一關係
這裡的一對多關係是兩個表字段一一對應,一個學生的某門課的成績是唯一確定的。 在一一對應的情況下要在 resultMap 中使用 標籤。
在 Student.java 中增加字段 Score
private Score score;
public Score getScore() {
return score;
}
public void setScore(Score score) {
this.score = score;
}
有兩種使用情況,第一種為嵌套查詢,即前一個 SQL 查詢結果集中的字段作為參數傳遞給下一個 SQL。第二種情況為嵌套結果集,即兩個表做關聯查詢,將結果集映射到多個 JavaBean 文件。
- 嵌套查詢
select SNO,SNAME
from test.STUDENT
select degree from test.SCORE
where sno = #{sno}
在標籤中
- property 指向了 Student.java 中新增的 score 字段。
- column 指定了作為參數傳遞給下一個查詢SQL的字段,需要注意的是對於傳遞單個字段的情況,mybatis 只是簡單的將 #{參數} 替換為佔位符 ?, 然後執行 resultSet.getString(columnName),沒有進行參數匹配,因此第二個 SQL 中 #{} 中寫任何字符都可以;如果需要傳遞多個字段,使用 column = " {prop1=col1,prop2=col2} ",這種情況下會以參數對象的形式來傳遞。
- select 指定了下一個 SELECT 語句
另外需要注意的是這種嵌套查詢對於大型結果集和列名並友好,存在
N+1 的問題,因為下一條 SQL 會執行 N 次去循環查詢,使用關聯查詢更合適。再者也可以開啟 mybatis 的懶查詢功能,嵌套的 SQL 不是一口氣順序執行完,而是在使用的時候才會執行下一條 SQL。例如執行student.getScore().getSNO()才會執行queryScore的 SQL。默認情況下沒有開啟,需要在配置文件中設置設置參數 描述 默認值 lazyLoadingEnabled 延遲加載的全局開關,特定關聯關係中可通過設置 fetchType 屬性來覆蓋該項的開關狀態 false aggressiveLazyLoading 當開啟時,任何方法的調用都會加載該對象的所有屬性。否則,每個屬性會按需加載 false (true in ≤3.4.1)
也可以在標籤中設置 fetchType = “lazy” 開啟懶加載,會覆蓋全局的參數設置。
- 嵌套結果集
對於多表關聯查詢,一般在 SQL 中使用別名來避免字段名的重複。mybatis 要做的是將別名正確的映射到 JavaBean 屬性上。
SELECT SNAME, SSEX, CLASS, ST.SNO, SC.SNO AS SC_SNO
FROM test.student st INNER JOIN test.score sc
ON st.sno = sc.sno
where CNO = '3-105';
通過設置標籤指定了表列名和屬性之間的映射關係。但這樣如果字段很多,會需要一一指定,標籤提供了columnPrefix屬性,指定別名的前綴,這樣可以重用resultMap
一對多關係
除了一對一的關係,還有一對多的關係,比如這裡一個學生Student 對應多門課的成績。 一對多對應的情況下要在 resultMap 中使用 標籤。首先需要調整 JavaBean 文件中兩個表之間的關係。
private List
public List
return score;
}
public void setScore(List
this.score = score;
}
以嵌套結果集為例
SELECT SNAME,SSEX,CLASS,ST.SNO,SC.SNO AS SC_SNO
FROM test.student st INNER JOIN test.score sc
ON st.sno = sc.sno
注意到相比 association 多了一個屬性ofType,是用來表示 List 集合中的類型的。其他屬性的用法同 association 是一樣的。
閱讀更多 Java架構解析 的文章