MyBatis入門
大家可都有一個(gè)架構(gòu)師的夢(mèng),所以這次為大家?guī)砹烁唠A教程,有多高階呢?依然是可樂的老風(fēng)格,從淺入深,通過源碼解析,圖例結(jié)合,抽絲剝繭,讓大家看的不吃力,并且能夠深刻理解 Mybatis 這個(gè)框架的底層實(shí)現(xiàn)原理,讓大家學(xué)到的不僅僅是這個(gè)框架用法,而是通過這個(gè)框架理解其設(shè)計(jì)思想。
1、JDBC
可樂相信所有開發(fā)者第一次與數(shù)據(jù)庫打交道時(shí),就是通過 JDBC 來實(shí)現(xiàn)的,第一次通過程序獲取到數(shù)據(jù)庫中的數(shù)據(jù)時(shí),那種高興,那種吃驚,反正可樂依稀歷歷在目。
言歸正傳,什么是 JDBC:JDBC 是Java與數(shù)據(jù)庫交互的API,用來規(guī)范客戶端程序如何來訪問數(shù)據(jù)庫的應(yīng)用程序接口。
通常分為兩組API:
①、面向Java應(yīng)用程序開發(fā)人員的API,是一個(gè)標(biāo)準(zhǔn)的API,且不同數(shù)據(jù)庫具有不同的實(shí)現(xiàn)。
②、面向數(shù)據(jù)庫驅(qū)動(dòng)開發(fā)人員,是前者的底層支持。
因?yàn)榇蠹叶际荍ava開發(fā)人員,這里我們就介紹第一組API。
JDBC API主要位于JDK中的java.sql包中(之后擴(kuò)展的內(nèi)容位于javax.sql包中)
下面介紹幾個(gè)關(guān)鍵的接口,注意這都是JDK內(nèi)部提供的一些接口,具體實(shí)現(xiàn)得看具體的數(shù)據(jù)庫驅(qū)動(dòng),比如MySQL,我們通常會(huì)在maven或gradle配置mysql-connectior-java ,下面接口的具體實(shí)現(xiàn)就在其中。
①、Driver:驅(qū)動(dòng)程序,會(huì)將自身加載到DriverManager中去.
②、DriverManager:負(fù)責(zé)加載各種不同驅(qū)動(dòng)程序(Driver),并根據(jù)不同的請(qǐng)求,向調(diào)用者返回相應(yīng)的數(shù)據(jù)庫連接(Connection)。
③、Connection:數(shù)據(jù)庫連接,負(fù)責(zé)與進(jìn)行數(shù)據(jù)庫間通訊,SQL執(zhí)行以及事務(wù)處理都是在某個(gè)特定Connection環(huán)境中進(jìn)行的??梢援a(chǎn)生用以執(zhí)行SQL的Statement。
④、Statement:用來執(zhí)行SQL查詢和更新(針對(duì)靜態(tài)SQL語句和單次執(zhí)行)。
⑤、PreparedStatement:用來執(zhí)行包含動(dòng)態(tài)參數(shù)的SQL查詢和更新(預(yù)編譯,在服務(wù)器端編譯,允許重復(fù)執(zhí)行以提高效率)。
⑥、CallableStatement:用來調(diào)用數(shù)據(jù)庫中的存儲(chǔ)過程。
⑦、ResultSet:用來存儲(chǔ)數(shù)據(jù)庫查詢操作返回的結(jié)果。
⑧、SQLException:表示在數(shù)據(jù)庫連接的建立、SQL語句的執(zhí)行、關(guān)閉等過程中發(fā)生了異常。
2、完整的交互過程
下面,我們通過JDBC來完成一次數(shù)據(jù)庫查詢操作,步驟如下:
①、加載數(shù)據(jù)庫驅(qū)動(dòng)
②、獲取數(shù)據(jù)庫連接(通過數(shù)據(jù)庫URL,用戶名,密碼)
③、定義SQL語句
④、通過數(shù)據(jù)庫連接,創(chuàng)建 Statement 對(duì)象或者 PreparedStatement 對(duì)象
⑤、通過 Statement 對(duì)象執(zhí)行 SQL 語句,得到結(jié)果集 ResultSet 對(duì)象
⑥、讀取 ResultSet 對(duì)象,轉(zhuǎn)換成我們要的 JavaBean
⑦、關(guān)閉數(shù)據(jù)庫連接、Statement、ResultSet等對(duì)象,釋放相關(guān)資源
代碼實(shí)現(xiàn)如下:
package?com.itcoke.bean;
public?class?Person?{
????private?Long?pid;
????private?String?pname;
????private?Integer?page;
????public?Long?getPid()?{
????????return?pid;
????}
????public?void?setPid(Long?pid)?{
????????this.pid?=?pid;
????}
????public?String?getPname()?{
????????return?pname;
????}
????public?void?setPname(String?pname)?{
????????this.pname?=?pname;
????}
????public?Integer?getPage()?{
????????return?page;
????}
????public?void?setPage(Integer?page)?{
????????this.page?=?page;
????}
????
????public?String?toString()?{
????????return?"Person{"?+
????????????????"pid="?+?pid?+
????????????????",?pname='"?+?pname?+?'\''?+
????????????????",?page="?+?page?+
????????????????'}';
????}
}package?com.itcoke.jdbc;
import?com.itcoke.bean.Person;
import?java.sql.*;
import?java.util.ArrayList;
import?java.util.List;
public?class?JDBCUtils?{
????//MySQL數(shù)據(jù)庫驅(qū)動(dòng),注意多了一個(gè)cj,com.mysql.jdbc.Driver和mysql-connector-java?5一起用
????public?static?String?driverClass?=?"com.mysql.cj.jdbc.Driver";
????//MySQL用戶名
????public?static?String?userName?=?"root";
????//MySQL密碼
????public?static?String?passWord?=?"root1234";
????//MySQL?URL
????public?static?String?url?=?"jdbc:mysql://localhost:3306/mybatis-study";
????//定義數(shù)據(jù)庫連接
????public?static?Connection?conn?=?null;
????//定義聲明數(shù)據(jù)庫語句,使用?預(yù)編譯聲明?PreparedStatement提高數(shù)據(jù)庫執(zhí)行性能
????public?static?PreparedStatement?ps?=?null;
????//定義返回結(jié)果集
????public?static?ResultSet?rs?=?null;
????public?static?List<Person>?selectPersonByName(String?pname){
????????List<Person>?personList?=?new?ArrayList<>();
????????try?{
????????????//?1、加載數(shù)據(jù)庫驅(qū)動(dòng)
????????????Class.forName(driverClass);
????????????//?2、獲取數(shù)據(jù)庫連接
????????????conn?=?DriverManager.getConnection(url,userName,passWord);
????????????//?3、定義?sql?語句,?表示占位符
????????????String?sql?=?"select?*?from?person?where?pname=?";
????????????//?4、獲取預(yù)編譯處理的statement
????????????ps?=?conn.prepareStatement(sql);
????????????//設(shè)置sql語句中的參數(shù),第一個(gè)為sql語句中的參數(shù)的?(從1開始),第二個(gè)為設(shè)置的參數(shù)值
????????????ps.setString(1,?"itcoke");
????????????//?5、向數(shù)據(jù)庫發(fā)出?sql?語句查詢,并返回結(jié)果集
????????????rs?=?ps.executeQuery();
????????????while(rs.next()){
????????????????//?6、讀取ResultSet,轉(zhuǎn)換成我們要的對(duì)象
????????????????Person?person?=?new?Person();
????????????????person.setPid(rs.getLong("pid"));
????????????????person.setPname(rs.getString("pname"));
????????????????personList.add(person);
????????????}
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????}finally{
????????????//?7、關(guān)閉數(shù)據(jù)庫連接
????????????if(rs!=null){
????????????????try?{
????????????????????rs.close();
????????????????}?catch?(SQLException?e)?{
????????????????????e.printStackTrace();
????????????????}
????????????}
????????????if(ps!=null){
????????????????try?{
????????????????????ps.close();
????????????????}?catch?(SQLException?e)?{
????????????????????e.printStackTrace();
????????????????}
????????????}
????????????if(conn!=null){
????????????????try?{
????????????????????conn.close();
????????????????}?catch?(SQLException?e)?{
????????????????????e.printStackTrace();
????????????????}
????????????}
????????}
????????return?personList;
????}
????public?static?void?main(String[]?args){
????????List<Person>?personList?=?JDBCUtils.selectPersonByName("itcoke");
????????System.out.println(personList);
????}
}
PS:數(shù)據(jù)庫名稱為 mybatis-study,表為 person
3、缺點(diǎn)分析
①、問題一:每執(zhí)行一次增刪改查,就要建立連接,關(guān)閉連接,頻繁的獲取連接和關(guān)閉連接,會(huì)造成數(shù)據(jù)庫資源浪費(fèi),影響數(shù)據(jù)庫性能。
設(shè)想解決:使用數(shù)據(jù)庫連接池管理數(shù)據(jù)庫連接
②、問題二:將 sql 語句硬編碼到程序中,如果sql語句修改了,那么需要重新編譯 Java 代碼,不利于系統(tǒng)維護(hù)
設(shè)想解決:將 sql 語句可配置化,比如設(shè)置到 xml 文件中,這樣即使 sql 語句變化了,我們也不需要對(duì) Java 代碼進(jìn)行修改
③、問題三:在 PreparedStatement 中設(shè)置參數(shù),對(duì)占位符設(shè)置值都是硬編碼在Java代碼中,不利于系統(tǒng)維護(hù)
設(shè)想解決:將 sql 語句以及占位符和參數(shù)都配置到 xml 文件中
④、問題四:從 resultset 中遍歷結(jié)果集時(shí),對(duì)表的字段存在硬編碼,不利于系統(tǒng)維護(hù)
設(shè)想解決:將查詢的結(jié)果集自動(dòng)映射為 Java 對(duì)象
⑤、問題五:重復(fù)性代碼特別多,包括建立建立,加載驅(qū)動(dòng)等
設(shè)想解決:抽取封裝公共代碼
⑥、問題六:沒有緩存,如果存在數(shù)據(jù)量大,且頻繁查詢的情況,這種方式性能特別低
設(shè)想解決:集成緩存框架去操作數(shù)據(jù)庫
⑦、問題七:sql 的移植性不好,如果換個(gè)數(shù)據(jù)庫,那么sql 語句可能要重寫
設(shè)想解決:在 JDBC 和 數(shù)據(jù)庫之間插入第三方框架,用第三方去生成 sql 語句,屏蔽數(shù)據(jù)庫的差異
4、ORM框架
為了解決上面這些缺點(diǎn),ORM(Object Relational Mapping,對(duì)象-關(guān)系映射)框架應(yīng)運(yùn)而生。
ORM 模型就是數(shù)據(jù)庫的表和Java對(duì)象的映射關(guān)系模型,它主要解決數(shù)據(jù)庫數(shù)據(jù)和Java對(duì)象的相互映射,通過映射關(guān)系,我們可以簡(jiǎn)單而迅速的把數(shù)據(jù)庫數(shù)據(jù)轉(zhuǎn)換成Java對(duì)象,從而讓開發(fā)人員無需對(duì)數(shù)據(jù)庫相關(guān)知識(shí)深入了解,便可以操作數(shù)據(jù)庫數(shù)據(jù)。
當(dāng)前市面上比較主流的ORM框架包括 Hibernate、Mybatis、Spring JDBC 等等,各有優(yōu)缺點(diǎn),這里可樂不做詳細(xì)介紹,本系列文章主要是介紹當(dāng)前使用最廣泛的 ORM框架——Mybatis。
其更輕量、更可控的特性,比較適合當(dāng)前主流業(yè)務(wù),特別是大數(shù)據(jù)量、高并發(fā)的場(chǎng)景。
5、小結(jié)
本文我們介紹了JDBC直接操作數(shù)據(jù)庫的一些問題,引申出解決此類問題的ORM框架,其中 Mybatis 是其中的佼佼者,
那么為什么 Mybatis 能夠解決 JDBC 直接操作數(shù)據(jù)庫的痛點(diǎn)?它又是如何實(shí)現(xiàn)的呢?
大家好,我是 可樂,操千曲而后曉聲,觀千劍而后識(shí)器。感謝各位人才的:點(diǎn)贊、收藏和評(píng)論。
不要走開,我們下篇文章更精彩!