為什么要使用MyBatis?
我們都知道,在學習mybatis之前,要在Java中操作數據庫,需要用到JDBC,但是在使用JDBC時會有許多缺陷。
比如:
1、使用時需要先進行數據庫連接,不用后要立即釋放連接,這樣對數據庫進行頻繁連接和關閉,會造成數據庫資源浪費,同時並發量較大時,會影響數據庫性能。
解決方案:為了達到連接復用,使用數據庫連接池管理數據庫連接。
2、將sql語句硬編碼到java代碼中,使得代碼耦合度高,如果sql 語句修改,就需要重新編譯java代碼,不利於系統維護。
解決方案:將sql語句配置在xml配置文件中,即使sql變化,不需要對java代碼進行重新編譯,只要在配置文件中修改。
3、向preparedStatement中設置參數,對占位符號位置和設置參數值,硬編碼在java代碼中,不利於系統維護。
解決方案:將sql語句及占位符號和參數全部配置在xml中。
4、從resutSet中遍歷結果集獲取數據時,必須保證屬性名正確,否則無法取出數據,因此將獲取表的字段進行硬編碼,不利於系統維護。
解決方案:將查詢的結果集,自動映射成java對象。
下面是使用JBDC操作時的代碼:
為了方便我使用的是maven項目來編寫的,所以在剛開始時需要在pom.xml文件中添加數據庫的依賴
<!--添加數據庫依賴--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> </dependency>
下面就是代碼部分。

public class JDBC { public static void main(String[] args) { // 數據庫連接 Connection connection = null; // 預編譯的Statement,使用預編譯的Statement提高數據庫性能 PreparedStatement preparedStatement = null; // 結果集 ResultSet resultSet = null; try{ //加載mysql驅動 Class.forName("com.mysql.jdbc.Driver"); String URL="jdbc:mysql://localhost:3306/test"; String USER="root"; String PASS="123456"; // 通過驅動管理類獲取數據庫鏈接 connection=DriverManager.getConnection(URL,USER,PASS); // 定義sql語句 ?表示占位符 String sql="select * from student where SID=?"; // 獲取預處理statement preparedStatement= connection.prepareStatement(sql); // 設置參數,第一個參數為sql語句中參數的序號(從1開始),第二個參數為設置的參數值 preparedStatement.setString(1,"2"); //返回結果集 resultSet=preparedStatement.executeQuery(); // 遍歷查詢結果集 while (resultSet.next()) { System.out.println(resultSet.getString("Sname")+" "+ resultSet.getString("Sage")); } }catch (Exception e){ e.getMessage(); }finally { //關閉連接 try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
通過上述代碼可以看出,sql語句和Java語句是混合在一塊編碼的,這大大更加了代碼耦合度,這樣在修改sql語句時,都需要重新編譯Java代碼,不利於系統維護。而且在實際開發中也經常會出現修改或優化sql語句的情況,所以之后人們便更多的使用MyBatis來進行操作數據庫。
一,MyBatis概述
MyBatis 本是apache的一個開源項目iBatis,2010年6月由apache software foundation 遷移到了google code,並且改名為MyBatis,因為當時iBatis已經發布到3.x版本了,所以也可以理解為Mybatis實際就是ibatis 3.x的后續版。
Mybatis通過xml或注解的方式將要執行的各種statement(statement、preparedStatemnt、CallableStatement)配置起來,並通過java對象和statement中的sql進行映射生成最終執行的sql語句,最后由mybatis框架執行sql並將結果映射成java對象並返回。



<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!--添加mysql數據庫依賴--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> </dependency> <!--添加mybatis依賴--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.6</version> </dependency> <!-- 添加log4j 日志依賴--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency> </dependencies>
2.添加全局配置文件(mybatis-cfg.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> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/StudentMapper.xml"></mapper> </mappers> </configuration>
3.根據數據庫中要訪問的表的信息創建POJO類(Student.java)

public class Student { private int SID; private String Sname; private int Sage; private String Ssex; @Override public String toString() { return "Student{" + "SID=" + SID + ", Sname='" + Sname + '\'' + ", Sage=" + Sage + ", Ssex='" + Ssex + '\'' + '}'; } public void setSID(int SID) { this.SID = SID; } public void setSname(String sname) { Sname = sname; } public void setSage(int sage) { Sage = sage; } public void setSsex(String ssex) { Ssex = ssex; } public int getSID() { return SID; } public String getSname() { return Sname; } public int getSage() { return Sage; } public String getSsex() { return Ssex; } }
4.創建Mapper接口文件(StudentMapper.java)
這是一個接口類,里面封裝了一些空方法,而方法的具體內容一般是在它的映射文件xml中用sql的語法來編寫的。
JDBC: Dao(接口) ------> DaoImpl(實現類)
MyBatis: Mapper(接口)-----> xxxMapper.xml

public interface StudentMapper { public Student selectStudentById(int sid); }
5.添加Mapper.xml映射文件(StudentMapper.xml)
注意寫好的Mapper.xml映射文件一定要注冊在全局配置文件(mybatis-cfg.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"> <!-- namespace:名稱空間 id:唯一標識 resultType:返回值類型 #{id}:從傳過來的參數中取出id值 --> <mapper namespace="MyBatisDemo.StudentMapper"> <!--查詢標簽:select 注意此處id為該方法的方法名--> <select id="selectStudentById" resultType="MyBatisDemo.Student"> select * from student where SID=#{sid} </select> </mapper>
6.添加日志配置文件

## debug 級別
log4j.rootLogger=DEBUG,Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target=System.out
log4j.appender.Console.layout = org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d{yyyy-MM-dd-HH\:mm\:ss,SSS} [%t] [%c] [%p] - %m%n
log4j.logger.com.mybatis=DEBUG /
##輸出sql 語句
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
7.測試類
public class MyBatisTest { public static void main( String[] args ) { SqlSession openSession = null; try { //mybatis配置文件 String resourse="mybatis-cfg.xml"; //通過 Resources 工具類將 mybatis-config.xm 配置文件讀入 Reader InputStream inputStream=Resources.getResourceAsStream(resourse); //通過 SqlSessionFactoryBuilder 建造類使用 Reader 創建 SqlSessionFactory工廠對象 SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream); //通過SqlSessionFactory工廠得到SqlSession openSession = sqlSessionFactory.openSession(); //通過反射機制來獲取對應的Mapper實例 StudentMapper mapper=openSession.getMapper(StudentMapper.class); //通過mapper調用實例下的方法 Student student=mapper.selectStudentById(6); System.out.println(student); } catch (IOException e) { e.printStackTrace(); }finally { //最后一定不要忘記關閉 SqlSession ,否則會因為連接沒有關閉導致數據庫連接數過多,造成系統崩旗 openSession.close(); } } }
8.運行結果
四.MyBatis-全局配置文件
1.properties標簽
mybatis可以通過properties標簽來引入外部properties配置文件的內容,包括兩個路徑下的資源,
- resource:引入類路徑下的資源
- url:引入網絡路徑或者磁盤路徑下的資源
在使用properties標簽之前,配置數據庫是用下面的方法,由於是使用硬編碼的方式,修改value參數值都需要動代碼。在使用properties標簽后,就可以將參數值封裝到一個配置文件中,每次修改參數只需在配置文件中修改即可。
使用properties的步驟:
1.在mybatis-cfg.xml的同目錄下創建一個db.properties文件,文件中存放數據庫連接使用的一些值,下面是文件中的內容。
2.在mybaties-cfg.xml中添加properties標簽,雙引號之間是db.properties文件名
3.將mybaties-cfg.xml中的值替換成變量
最后需要注意:如果屬性在不止一個地方進行了配置,那么MyBatis將按照下面順序來加載
- 在property元素體內指定的屬性首先被讀取
- 然后根據properties元素中的resource屬性讀取類路徑下屬性文件或根據url屬性指定的路徑讀取屬性文件,並覆蓋已讀取的同名屬性
- 最后讀取作為方法參數傳遞的屬性,並覆蓋已讀取的同名屬性
2.settings標簽
下圖是MyBatis中重要的調整設置,他們會改變Mybatis的運行時行為
舉例:駝峰命名法
為了保證查詢的數據可以正常顯示出來,我們在編寫POJO類時,必須保證它內部定義屬性名和數據庫中的屬性名必須相同,但我們在數據庫中定義屬性時一般習慣采用駝峰命名法A_B,而Java中又習慣采用aB這種格式命名,所以許多人在編寫代碼時都會因為格式不相同而無法正確查出數據,所以就有了駝峰命名法,這樣即使格式不一樣(屬性名相同,僅格式不同),也可以正確查出數據。
駝峰命名法的使用方式:
在mybatis-cfg.xml文件中添加settings標簽即可。
settings用來設置每一個設置項,name設置項名,value設置項取值
3.typeAliases標簽(別名處理器)
在mybatis中可以使用typeAliases來給變量取別名,這樣對於一些變量名較長的變量,我們就可直接使用它的別名。(別名不區分大小寫)
比如:我們每次在StudentMapper.xml中編寫查詢數據的代碼時,都要指定數據返回類型,但又因為返回值類型的名字太長,每次編寫時都不方便,所以我們就可以給這個返回值類型起一個簡單易編寫的別名。
起別名的方法:
1.在mybatis-cfg.xml文件中添加typeAliases標簽
2.將StudentMapper.xml映射文件中使用到MyBatisDemo.Student的地方都替換成TMS
批量起別名的方法:
1.在mybatis-cfg.xml文件中添加typeAliases標簽
2.在使用該包下的某個類時,就可直接使用類名(大小寫都可),(不僅適應於返回值類型,也可適應參數類型)
但是只用這種方法有個缺陷,就是當該A包底下還存在一個B包,該B包與A包存在相同的類名的類時,使用這種方法就無法區分要使用的是哪個類,所以為了避免這種情況,可結合@Alias注解
3.給MyBatisDemo包下的類名沖突的子類添加注解
4.將StudentMapper.xml映射文件中使用到MyBatisDemo.Student的地方都替換成TMS
4.typeHandlers標簽(類型處理器)
由於java類型與數據庫類型之間的差異,無論是 MyBatis 在預處理語句(PreparedStatement)中設置一個參數時,還是從結果集中取出一個值時,都需要使用系統提供的類型處理器(也可以自定義類型處理器), 將獲取的值以合適的方式轉換成 Java 類型。下表描述了一些默認的類型處理器。
5.objectFactory標簽
6.plugins標簽
7.environments標簽
8.databaseidProvider標簽
可以支持多個數據庫廠商,使用該標簽,可以在多個不同的數據庫中進行查詢數據。
1.在mybatis-cfg.xml文件中添加databaseidProvider標簽
2.在StudentMapper.xml映射文件中修改select語句
3.切換當前所使用的數據庫環境
9.mappers標簽
1.注冊一個sql映射,其中resource來指定引用類路徑下的sql映射,url用來引用網絡路徑或者磁盤路徑下的sql映射
2.注冊接口,使用注冊接口的方法和注冊sql映射的方法的作用是一樣的。
但是使用注冊接口的方法,要保證接口的類名和sql映射的文件名相同,並且兩個必須放到同一個包路徑下。
另外如果沒有映射文件,也可使用注冊接口的方法,與上面不同的是,sql語句是利用注解寫在接口上的,雖然這樣利用注解也可以實現,但是sql和java代碼的耦合度會更高。所以還是建議利用創建sql映射文件的方式
10.總結
在mybatis-cfg.xml中配置上面的這些標簽時,必須按照上面1-9的排列順序配置,可以缺少某一個標簽,但不允許順序發生變化,否則會報錯。
五.XML映射文件的編寫
由於xml映射文件和接口文件是綁定的關系,所以每次在xml映射文件中添加sql語句,在接口中也就要添加響應的Java語句。
1.查詢操作
接口文件:
sql映射文件:
測試類:
注意此查詢僅支持返回一個結果集,如果返回有多個會報TooManyExection異常。所以如果返回結果有多條就需要將接口文件中方法返回值改為List/Map類型,但sql映射文件不變,如下所示。
1.1.List類型
接口文件:
sql映射文件:不變(注意返回類型仍為Student,不為List<Student>)
測試類:
1.2.Map類型(只返回一條記錄)
列名作為key,值就是查詢的值。
接口文件:
sql映射文件:(返回多天記錄時,返回值類型就為記錄的類型,但返回一條就可寫為map)
測試類:
1.3 Map類型(返回多條記錄)
將多條記錄封裝在一個Map中,並要求指定屬性作為Key值,此處是以名字作為key。
接口文件:
sql映射文件:
測試類:
2.添加操作
接口文件:
sql映射文件:
注意:此處student()中的參數格式必須和數據庫中的參數格式相同,values()中的#{}中參數必須和POJO類中的參數名相同。
測試類:
此處方法是無返回類型的,所以在返回值類型那塊寫void即可,如果想改為Boolean/int類型,可直接將void改為Boolean/int即可,其他sql映射代碼無需在修改。
3.更新操作
接口文件:
sql映射文件:
注意:此處set后面的參數格式必須和數據庫中的參數格式相同,#{}中的參數必須和POJO類中的參數名相同。
測試類:
4.刪除操作
接口文件:
sql映射文件:
因為此處是使用(int)sid來查詢刪除的,參數不為student,所以#{}中可任意寫。
測試類:
5.多參數查詢
1.使用param來傳參。因為在傳參數時出現多個參數的情況時,這多個參數會被封裝成Map集合,其中key存儲為param1.param2....paramn,value存儲就是參數的值,所以在sql映射文件中就需要使用param來取值。
接口文件:
sql映射文件:
2.命名參數。使用param來傳參難免會有些不便,當參數多了,就會混淆,為了更好區分,我們可以在傳參時,使用注解重新命名。這時候key存儲為@Param(變量名1).@Param(變量名2)......,value存儲就是參數的值,所以在sql映射文件中就需要使用param重新命的名來取值。
接口文件:
sql映射文件:
測試類:
3.如果多個參數正好都是POJO類中的變量,也可以使用POJO類來傳參。
4.補充:
6.${}取值與#{}取值的區別
1.#{}是預編譯處理,$ {}是字符串替換。
2.MyBatis在處理#{}時,會將SQL中的#{}替換為?號,使用PreparedStatement的set方法來賦值;MyBatis在處理 $ { } 時,就是把 ${ } 替換成變量的值。
3.使用 #{} 可以有效的防止SQL注入,提高系統安全性。
7.自定義結果集映射(resultMap)
1.當POJO類中定義的屬性名與數據庫中的屬性名格式不同時,我們除了可以開啟駝峰命名法,還可采用resultMap來解決。
2. 當有兩張表Student表和Course表,在學生表中存在課程表的id號的列,那如果想通過Student表即查出學生信息又查出專業信息,我們知道使用resultType是實現不了的,這時候就可使用resultMap來實現。
除了上面這種使用方式,還可以借助association標簽來實現。
sql映射文件:
3.使用association標簽來實現分布查詢
步驟:使用select方法先查出Student的信息,其中包括Cid,然后又根據Cid在Course表中查出課程名。
測試類:
4.延遲加載
通過查看打印結果可知,在上面的分步查詢過程中,不過有沒有調用student.getCourse()方法,在底層都會進行數據庫的兩次查詢,這樣就會降低執行效率,但是使用延遲加載,就可以保證當我們使用了student.getCourse()方法,底層才會取數據庫進行查詢課程。
開啟延遲加載的方法:在全局配置文件(mybatis-cfg.xml)中,添加setting標簽。
5.collection標簽
有一張teacher表,里面既有教師信息,也有教師授課的信息,如果想通教師號查到該教師所授的課程信息,由於是一對多的關系,因此就需要使用collection標簽
六.MyBatis動態SQL
1.if標簽
舉例:根據傳入的學生信息在數據庫中查找,如果輸入的姓名不為null,就根據姓名查找,如果輸入的id不為null,就根據id查找,如果都不為空,就查找兩個信息都匹配的學生。
映射文件:
測試類:
通過仔細觀察上述代碼,就會發現上述代碼是存在問題的,如果傳入的Sid為空,那么查詢就變成了select * from student where and Sname=?,這明顯是有語法問題的,那該怎么解決呢?
1.在where后添加1=1
2.將if標簽放到where標簽中,這樣在查詢時,就會自動省and,但必須要求and是在賦值語句前面的,如果在后面,必須使用trim標簽。
3.在update時后邊可能會多一個逗號,可以將更新語句放到set標簽里,當然也可以使用trim標簽。
2.trim(where,set)
prefix:給整個字符串添加一個前綴
prefixOverrides:去掉整個字符串前面多余的字符
suffix:給整個字符串去掉一個后綴
suffixOverrides:去掉整個字符串后面多余的字符
3.choose(when,otherwise)
對於if標簽中舉例的那個問題同樣可以使用choose解決。
4.foreach 標簽
separator:每個元素之間的分隔符。
open:給遍歷所有的結果拼接一個開始的字符
close:給遍歷出所有的結果拼接一個結束的字符
index:索引。遍歷list的時候,index是索引,item就是當前值。遍歷map的時候,index就是map的key,item就是map的值。
舉例1:給一些id號,然后在數據庫中查找與這些id號匹配的學生的信息。
接口文件:
sql映射文件:
測試類:
舉例2:批量添加學生信息
接口文件:
sql映射文件:
測試類:
5.bind標簽(可以給一個變量綁定上一些字符串)
舉例:想通過字符串匹配的方法,通過輸入' h ' 找到名字中包含h的學生。
接口文件:
sql映射文件:
測試類:
6.抽取重復的sql語句