在我們的日常開發中MyBatis是使用的比較多的orm框架之一,以前本患者僅僅停留在會用的層面上,但是對於一些原理性的東西基本都是知其然不知其所以然。所以最近這段時間打算和大家一起討論一下MyBatis的一些核心原理。
首先今天先和大家分享一下本人使用MyBatis框架的兩種方法,一種是基於註解的方式,另外一種是編寫xml映射文件,首次我們來到官網:https://mybatis.org/mybatis-3/ 進入之後是如下圖所示的界面:
從上圖中可以看到MyBatis的基本介紹,但是最重要的一點是下方的五星紅旗,激動吧,MyBatis官方網站上支持中文版本的,因此我們點擊五星紅旗,切換到中文版本,如下圖所示:
我們首先點擊上圖中的項目的GIT代碼庫 進入到MyBatis 的下載頁面,如下圖所示:
首先我們點擊下載最新的版本,進入到如下圖所示的界面:
點擊下方的zip包的超鏈接,下載MyBatis框架的包, 其中mybatis-3.5.3這個是可運行的文件,下面的兩個是源碼文件包。下載完成之後我們解壓mybatis-3.5.3這個包,解壓後的目錄如下所示:
其中我們pdf就是MyBatis的使用手冊,mybatis-3.5.3.jar就是我們需要導入到程序中的jar包,lib目錄下存放的是我們可能要用到的一些jar包,並不是Mybatis依賴的jar包.
好了,關於mybatis 的下載就先到這裏了,我們先來寫一個入門級的demo。
首先打開我們的開發工具,創建一個新的Java工程,並創建相應的包結構。如下圖所示(開發工具暫時用的sts):
其中我們在lib目錄下導入數據庫驅動包和Mybatis包,同時我們還需要一個日誌的工具,這裏我們使用log4g,日誌包可以在Mybatis框架的lib目錄中找到。conf目錄是存放配置文件的目錄。創建好了之後我們在pojo中新建一個實體類,Fruit,該類中易功有四個屬性,分別是id,name,area,color,同時我們爲這四個屬性創建set/get方法,以及toString方法。具體代碼如下圖所示:
private Integer id;
private String fruitName;
private String area;
private String fruitColor;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFruitName() {
return fruitName;
}
public void setFruitName(String fruitName) {
this.fruitName = fruitName;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getFruitColor() {
return fruitColor;
}
public void setFruitColor(String fruitColor) {
this.fruitColor = fruitColor;
}
@Override
public String toString() {
return "Fruit [id=" + id + ", fruitName=" + fruitName + ", area=" + area + ", fruitColor=" + fruitColor + "]";
}
接着我們需要準備數據了,打開mysql ,新建一個數據庫mybatis,然後再新建一張fruit表,表結構如下所示:
好了,數據庫環境準備完成之後我們就回到開發工具中,準備我們的第一個demo了。
emmmm.........有點不知道怎麼下手,還是先打開我們下載的zip包中的pdf文檔吧,我們打開文檔之後選擇 Getting Started
然後可以看到如下內容 :
這裏說的意思就是從xml來構建SqlSessionFactory這個對象,並且給了一段示例的代碼 ,這段代碼的意思就是獲取SqlSessionFactory這個對象,首先我們把這段代碼複製到我們的工程中,我們知道這段代碼肯定是會產生一個SqlSessionFactory對象的,因此我們新建一個util包,在該包中新建一個工具類,將上述示例代碼封裝成一個方法,代碼如下:
public class CommonsUtils {
public static SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
}
可以看到,上述代碼實在加載一個xml文件,而SqlSessionFactory對象是根據這個配置文件來創建 的,所以我們也需要一個mybatis-config.xml的配置文件,好了,回到官方文檔上,我們可以看到這樣一句話:
我們可以看到 上面說從 XML 文件中構建 SqlSessionFactory 的實例非常簡單,我們可以用任意的InputStream實例來加載該文件 同時可以用字符串形式的xml或者在文件系統中使用任意的 URL 形式指定路徑的文本文件都可以。但是他們建議我們在類路徑下創建該文件,大概的意思看懂了就可以,這裏就不逐字逐句的給大家翻譯了 (英語能力實在有限,詳細的中文意思推薦去官網上看)。 所以我們同從官方的建議,在類路徑conf下創建一個mybatis-config.xml文件,這裏解釋一下conf默認和類路徑一致。
那麼創建好了之後我們需要在該配置文件中寫一些什麼東西呢? 好了 ,我們回到文檔上,會發現文檔上提供了一個樣例。如下圖所示:
我們將上述的示例複製到我們創建的xml中去。如下圖所示:
按照我們以往的開發經驗來猜測,dataSource標籤中的內容應該是和數據源相關的,因此這裏我們將自己數據庫的信息寫上去即可。那麼還有一個mapper標籤的中的內容應該寫什麼呢? 我們看到官方給出的也是一個xml文件,好了,我們繼續看文檔,發現文檔中還有一種方式構建SqlSessionFactory,不使用xml配置文件:
這裏先放一放,稍後再給大家演示這種方式。我麼繼續往下看文檔,發現後面是告訴我們怎麼從SqlSessionFactory中獲取 SqlSession對象,同樣的給我們提供了一段簡單的代碼示例,如下所示:
我們繼續將上述示例代碼拷貝到我們的工具類中,封裝成一個方法,如下圖所示:
到這裏我們應該有一種直覺,Blog肯定是一個pojo對象,理論上應該對應一張數據表,好了,我們繼續往下看文檔,發現下面還有一段示例代碼,如下圖所示:
可以看到官方更推薦下面這種方式的用法,使用一個接口來完成查詢的操作,該接口定義了查詢語句的參數和查詢的結果類型。好了,我們繼續將上述代碼拷貝到工具類中單獨封裝成方法,稍後我們逐一來演示這兩種方式。接着我們嘗試將Blog修改成本次我們自己寫的實體類Fruit,修改完成之後我們的工具類CommomUtils完整的代碼如下:
public class CommonsUtils {
public static SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
public static SqlSession getSqlSession(SqlSessionFactory sqlSessionFactory) {
try (SqlSession session = sqlSessionFactory.openSession()) {
Fruit blog = session.selectOne(
"org.mybatis.example.BlogMapper.selectBlog", 101);
}
return null;
}
public static SqlSession getSqlSession2(SqlSessionFactory sqlSessionFactory){
try (SqlSession session = sqlSessionFactory.openSession()) {
FruitMapper mapper = session.getMapper(FruitMapper.class);
Fruit fruit = mapper.selectFruit(101);
}
return null;
}
}
好了,爲我們繼續往下看文檔,發現了下面還有一段基於xml映射語句的示例,我們發現了該xml中有一條sql語句,如下所示:
我們再接着往下看,下面有兩段話用來解釋上述xml文件:
官方說我們在一個sql映射文件中可以聲明多個sql語句,我們上面的示例代碼的意思就是通過一個namespace(名稱空間)指定一個xml映射文件,映射文件中定義了一條id是selectBlog的sql語句,該sql語句的傳入參數是id。我們調用的方式有兩種: 第一種就是通過SqlSession 對象的selectOne方法,該方法有兩個參數,第一個參數是 namespace+sql語句的id組成字符串的形式,第二個參數是sql語句的執行條件參數。 第二種方式就是通過一個接口來指定查詢的sql語句,接口的所在的包對應namespace,接口中的方法名對應sql映射文件中的sql語句的id ,方法的參數就是sql語句的執行條件。好了,我們仔細看一下文檔中這個地方,這裏描述的兩種方式不就是上述提供的示例代碼嘛。因此我們將這段示例的映射文件拷貝,再類路徑下新建xml文件,並且將內容稍作修改如下圖所示:
這裏需要注意的是我們的namespance要和包名以及映射文件的路徑保持一致。並且我們將示例文件中的內容改成我們自己定義的內容。好了,接着我們再回到mybatis-config.xml文件中,再mapper標籤中指定一下FruitMapper.xml的路徑,代碼如下圖所示:
這裏需要注意的是如果是使用的resourcel屬性的話我們需要寫相對路徑,即從類路徑開始寫。到這裏基本的準備工作已經差不多了,我們將CommonsUtils類在稍作一些修該,修改之後的代碼如下所示:
public class CommonsUtils {
public static SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
public static Fruit getFruit(SqlSessionFactory sqlSessionFactory) {
Fruit fruit = null;
try (SqlSession session = sqlSessionFactory.openSession()) {
fruit = session.selectOne(
"com.wcan.poet.mybatis.mapper.FruitMapper.selectFruit", 1);
}
return fruit;
}
public static Fruit getFruit2(SqlSessionFactory sqlSessionFactory){
Fruit fruit = null;
try (SqlSession session = sqlSessionFactory.openSession()) {
FruitMapper mapper = session.getMapper(FruitMapper.class);
fruit = mapper.selectFruit(1);
}
return fruit;
}
}
修改完成之後我們在測試包test下新建一個測試類MybatisTest,並且在該類中調用utils中的getFruit方法,代碼如下:
public class MyBatisTest {
@Test
public void test01() throws IOException {
SqlSessionFactory sessionFactory = CommonsUtils.getSqlSessionFactory();
Fruit fruit = CommonsUtils.getFruit(sessionFactory);
System.out.println(fruit);
}
}
在該方法中,我們先獲取一個SqlSessionFactory對象,再將該對象傳入到getfruit方法中,最後返回一個Fruit對象。我們執行該方法,發現報出來一個異常,該異常信息如下所示:
org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in com/wcan/poet/mybatis/mapper/FruitMapper.xml
### Cause: org.apache.ibatis.builder.BuilderException:
Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException:
Error parsing Mapper XML. The XML location is 'com/wcan/poet/mybatis/mapper/FruitMapper.xml'.
Cause: org.apache.ibatis.builder.BuilderException: Error resolving class.
Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'Fruit'.
Cause: java.lang.ClassNotFoundException: Cannot find class: Fruit
... 35 more
上述的異常信息就是說我們在 com/wcan/poet/mybatis/mapper/FruitMapper.xml文件中定義的Fruit類找不到,這裏我們在回到Fruit類中,發現resultType屬性我們寫了一個Fruit ,但是沒有加包名,因此Mybatis在運行的時候找不到這個類,於是我們先來加上包名,將resultType屬性的值改成: com.wcan.poet.mybatis.pojo.Fruit 即可,接着我們繼續執行,可以發現在控制檯打印出了sql語句,但是查詢結果是空的。如下圖所示:
好了,到這裏說明我們的代碼應該沒有問題了,我們在數據庫中先放一條數據,
接着再次執行一遍test01方法,發現還是返回的null對象,我們再來看一下映射文件中的SQL語句,查詢條件是* 也就是說默認查詢所有的字段,但是有一個問題,我們在程序中的變量名是駝峯命名方式,而數據庫中則是採用下劃線連接。兩個並不是對應的,所以即使查詢到了數據也無法封裝到對象中,熟悉sql 的同學此時肯定想到了別名,是的,別名可以解決這個問題,我們修改sql語句,爲每個字段加上別名,如下所示:
select id,fruit_name fruitName,area,fruit_color fruitColor from fruit where id = #{id}
再次運行即可查詢到一條結果了:
好了,接下來我們繼續調用第二種方法來查詢數據,編寫test02方法,代碼如下:
@Test
public void test02() throws IOException {
SqlSessionFactory sessionFactory = CommonsUtils.getSqlSessionFactory();
Fruit fruit = CommonsUtils.getFruit2(sessionFactory);
System.out.println(fruit);
}
發現同樣的可以查詢到數據。好了,入門程序就先介紹到這裏了。明天可繼續給大家講解通過註解的方式來使用Mybatis框架,小編要去睡覺了。晚安