国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

MyBatis 源碼分析系列文章導(dǎo)讀

weizx / 3201人閱讀

摘要:本文速覽本篇文章是我為接下來(lái)的源碼分析系列文章寫的一個(gè)導(dǎo)讀文章。年該項(xiàng)目從基金會(huì)遷出,并改名為。同期,停止維護(hù)。符號(hào)所在的行則是表示的執(zhí)行結(jié)果。同時(shí),使用無(wú)需處理受檢異常,比如。另外,把寫在配置文件中,進(jìn)行集中管理,利于維護(hù)。

1.本文速覽

本篇文章是我為接下來(lái)的 MyBatis 源碼分析系列文章寫的一個(gè)導(dǎo)讀文章。本篇文章從 MyBatis 是什么(what),為什么要使用(why),以及如何使用(how)等三個(gè)角度進(jìn)行了說(shuō)明和演示。由于文章的篇幅比較大,這里特地拿出一章用于介紹本文的結(jié)構(gòu)和內(nèi)容。那下面我們來(lái)看一下本文的章節(jié)安排:

如上圖,本文的大部分篇幅主要集中在了第3章和第4章。第3章演示了幾種持久層技術(shù)的用法,并在此基礎(chǔ)上,分析了各種技術(shù)的使用場(chǎng)景。通過(guò)分析 MyBatis 的使用場(chǎng)景,說(shuō)明了為什么要使用 MyBatis 這個(gè)問(wèn)題。第4章主要用于介紹 MyBatis 的兩種不同的用法。在 4.1 節(jié),演示多帶帶使用 MyBatis 的過(guò)程,演示示例涉及一對(duì)一一對(duì)多的查詢場(chǎng)景。4.2 節(jié)則是介紹了 MyBatis 和 Spring 整合的過(guò)程,并在最后演示了如何在 Spring 中使用 MyBatis。除了這兩章內(nèi)容,本文的第2章和第5章內(nèi)容比較少,就不介紹了。

以上就是本篇文章內(nèi)容的預(yù)覽,如果這些內(nèi)容大家都掌握,那么就不必往下看了。當(dāng)然,如果沒(méi)掌握或者是有興趣,那不妨繼續(xù)往下閱讀。好了,其他的就不多說(shuō)了,咱們進(jìn)入正題吧。

2.什么是 MyBatis

MyBatis 的前身是 iBatis,其是 Apache 軟件基金會(huì)下的一個(gè)開源項(xiàng)目。2010年該項(xiàng)目從 Apache 基金會(huì)遷出,并改名為 MyBatis。同期,iBatis 停止維護(hù)。

MyBatis 是一種半自動(dòng)化的 Java 持久層框架(persistence framework),其通過(guò)注解或 XML 的方式將對(duì)象和 SQL 關(guān)聯(lián)起來(lái)。之所以說(shuō)它是半自動(dòng)的,是因?yàn)楹?Hibernate 等一些可自動(dòng)生成 SQL 的 ORM(Object Relational Mapping) 框架相比,使用 MyBatis 需要用戶自行維護(hù) SQL。維護(hù) SQL 的工作比較繁瑣,但也有好處。比如我們可控制 SQL 邏輯,可對(duì)其進(jìn)行優(yōu)化,以提高效率。

MyBatis 是一個(gè)容易上手的持久層框架,使用者通過(guò)簡(jiǎn)單的學(xué)習(xí)即可掌握其常用特性的用法。這也是 MyBatis 被廣泛使用的一個(gè)原因。

3.為什么要使用 MyBatis

我們?cè)谑褂?Java 程序訪問(wèn)數(shù)據(jù)庫(kù)時(shí),有多種選擇。比如我們可通過(guò)編寫最原始的 JDBC 代碼訪問(wèn)數(shù)據(jù)庫(kù),或是通過(guò) Spring 提供的 JdbcTemplate 訪問(wèn)數(shù)據(jù)庫(kù)。除此之外,我們還可以選擇 Hibernate,或者本篇的主角 MyBatis 等。在有多個(gè)可選項(xiàng)的情況下,我們?yōu)槭裁催x擇 MyBatis 呢?要回答這個(gè)問(wèn)題,我們需要將 MyBatis 與這幾種數(shù)據(jù)庫(kù)訪問(wèn)方式對(duì)比一下,高下立判。當(dāng)然,技術(shù)之間通常沒(méi)有高下之分。從應(yīng)用場(chǎng)景的角度來(lái)說(shuō),符合應(yīng)用場(chǎng)景需求的技術(shù)才是合適的選擇。那下面我會(huì)通過(guò)寫代碼的方式,來(lái)比較一下這幾種數(shù)據(jù)庫(kù)訪問(wèn)技術(shù)的優(yōu)缺點(diǎn),并會(huì)在最后說(shuō)明 MyBatis 的適用場(chǎng)景。

這里,先把本節(jié)所用到的一些公共類和配置貼出來(lái),后面但凡用到這些資源的地方,大家可以到這里進(jìn)行查看。本章所用到的類如下:

public class Article {
    private Integer id;
    private String title;
    private String author;
    private String content;
    private Date createTime;
    
    // 省略 getter/setter 和 toString
}

數(shù)據(jù)庫(kù)相關(guān)配置放在了 jdbc.properties 文件中,詳細(xì)內(nèi)容如下:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/coolblog?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE
jdbc.username=root
jdbc.password=****

表記錄如下:

下面先來(lái)演示 MyBatis 訪問(wèn)數(shù)據(jù)庫(kù)的過(guò)程。

3.1 使用 MyBatis 訪問(wèn)數(shù)據(jù)庫(kù)

前面說(shuō)過(guò),MyBatis 是一種半自動(dòng)化的 Java 持久化框架,使用 MyBatis 需要用戶自行維護(hù) SQL。這里,我們把 SQL 放在 XML 中,文件名稱為 ArticleMapper.xml。相關(guān)配置如下:


    
        
        
        
        
        
    
    
    

上面的 SQL 用于從article表中查詢出某個(gè)作者從某個(gè)時(shí)候到現(xiàn)在所寫的文章記錄。在 MyBatis 中,SQL 映射文件需要與數(shù)據(jù)訪問(wèn)接口對(duì)應(yīng)起來(lái),比如上面的配置對(duì)應(yīng)xyz.coolblog.dao.ArticleDao接口,這個(gè)接口的定義如下:

public interface ArticleDao {
    List
findByAuthorAndCreateTime(@Param("author") String author, @Param("createTime") String createTime); }

要想讓 MyBatis 跑起來(lái),還需要進(jìn)行一些配置。比如配置數(shù)據(jù)源、配置 SQL 映射文件的位置信息等。本節(jié)所使用到的配置如下:


    

    
        
            
            
                
                
                
                
            
        
    
    
    
        
    

到此,MyBatis 所需的環(huán)境就配置好了。接下來(lái)把 MyBatis 跑起來(lái)吧,相關(guān)測(cè)試代碼如下:

public class MyBatisTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void prepare() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        inputStream.close();
    }

    @Test
    public void testMyBatis() throws IOException {
        SqlSession session = sqlSessionFactory.openSession();
        try {
            ArticleDao articleDao = session.getMapper(ArticleDao.class);
            List
articles = articleDao.findByAuthorAndCreateTime("coolblog.xyz", "2018-06-10"); } finally { session.commit(); session.close(); } } }

在上面的測(cè)試代碼中,prepare 方法用于創(chuàng)建SqlSessionFactory工廠,該工廠的用途是創(chuàng)建SqlSession。通過(guò) SqlSession,可為我們的數(shù)據(jù)庫(kù)訪問(wèn)接口ArticleDao接口生成一個(gè)代理對(duì)象。MyBatis 會(huì)將接口方法findByAuthorAndCreateTime和 SQL 映射文件中配置的 SQL 關(guān)聯(lián)起來(lái),這樣調(diào)用該方法等同于執(zhí)行相關(guān)的 SQL。

上面的測(cè)試代碼運(yùn)行結(jié)果如下:

如上,大家在學(xué)習(xí) MyBatis 框架時(shí),可以配置一下 MyBatis 的日志,這樣可把 MyBatis 的調(diào)試信息打印出來(lái),方便觀察 SQL 的執(zhí)行過(guò)程。在上面的結(jié)果中,==>符號(hào)所在的行表示向數(shù)據(jù)庫(kù)中輸入的 SQL 及相關(guān)參數(shù)。<==符號(hào)所在的行則是表示 SQL 的執(zhí)行結(jié)果。上面輸入輸出不難看懂,這里就不多說(shuō)了。

關(guān)于 MyBatis 的優(yōu)缺點(diǎn),這里先不進(jìn)行總結(jié)。后面演示其他的框架時(shí)再進(jìn)行比較說(shuō)明。

演示完 MyBatis,下面,我們來(lái)看看通過(guò)原始的 JDBC 直接訪問(wèn)數(shù)據(jù)庫(kù)過(guò)程是怎樣的。

3.2 使用 JDBC 訪問(wèn)數(shù)據(jù)庫(kù) 3.2.1 JDBC 訪問(wèn)數(shù)據(jù)庫(kù)的過(guò)程演示

在初學(xué) Java 編程階段,多數(shù)朋友應(yīng)該都是通過(guò)直接寫 JDBC 代碼訪問(wèn)數(shù)據(jù)庫(kù)。我這么說(shuō),大家應(yīng)該沒(méi)異議吧。這種方式的代碼流程一般是加載數(shù)據(jù)庫(kù)驅(qū)動(dòng),創(chuàng)建數(shù)據(jù)庫(kù)連接對(duì)象,創(chuàng)建 SQL 執(zhí)行語(yǔ)句對(duì)象,執(zhí)行 SQL 和處理結(jié)果集等,過(guò)程比較固定。下面我們?cè)偈謱懸槐?JDBC 代碼,回憶一下初學(xué) Java 的場(chǎng)景。

public class JdbcTest {

    @Test
    public void testJdbc() {
        String url = "jdbc:mysql://localhost:3306/myblog?user=root&password=1234&useUnicode=true&characterEncoding=UTF8&useSSL=false";

        Connection conn = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection(url);

            String author = "coolblog.xyz";
            String date = "2018.06.10";
            String sql = "SELECT id, title, author, content, create_time FROM article WHERE author = "" + author + "" AND create_time > "" + date + """;

            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(sql);
            List
articles = new ArrayList<>(rs.getRow()); while (rs.next()) { Article article = new Article(); article.setId(rs.getInt("id")); article.setTitle(rs.getString("title")); article.setAuthor(rs.getString("author")); article.setContent(rs.getString("content")); article.setCreateTime(rs.getDate("create_time")); articles.add(article); } System.out.println("Query SQL ==> " + sql); System.out.println("Query Result: "); articles.forEach(System.out::println); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

代碼比較簡(jiǎn)單,就不多說(shuō)了。下面來(lái)看一下測(cè)試結(jié)果:

上面代碼的步驟比較多,但核心步驟只有兩部,分別是執(zhí)行 SQL 和處理查詢結(jié)果。從開發(fā)人員的角度來(lái)說(shuō),我們也只關(guān)心這兩個(gè)步驟。如果每次為了執(zhí)行某個(gè) SQL 都要寫很多額外的代碼。比如打開驅(qū)動(dòng),創(chuàng)建數(shù)據(jù)庫(kù)連接,就顯得很繁瑣了。當(dāng)然我們可以將這些額外的步驟封裝起來(lái),這樣每次調(diào)用封裝好的方法即可。這樣確實(shí)可以解決代碼繁瑣,冗余的問(wèn)題。不過(guò),使用 JDBC 并非僅會(huì)導(dǎo)致代碼繁瑣,冗余的問(wèn)題。在上面的代碼中,我們通過(guò)字符串對(duì) SQL 進(jìn)行拼接。這樣做會(huì)導(dǎo)致兩個(gè)問(wèn)題,第一是拼接 SQL 可能會(huì)導(dǎo)致 SQL 出錯(cuò),比如少了個(gè)逗號(hào)或者多了個(gè)單引號(hào)等。第二是將 SQL 寫在代碼中,如果要改動(dòng) SQL,就需要到代碼中進(jìn)行更改。這樣做是不合適的,因?yàn)楦膭?dòng) Java 代碼就需要重新編譯 Java 文件,然后再打包發(fā)布。同時(shí),將 SQL 和 Java 代碼混在一起,會(huì)降低代碼的可讀性,不利于維護(hù)。關(guān)于拼接 SQL,是有相應(yīng)的處理方法。比如可以使用 PreparedStatement,同時(shí)還可解決 SQL 注入的問(wèn)題。

除了上面所說(shuō)的問(wèn)題,直接使用 JDBC 訪問(wèn)數(shù)據(jù)庫(kù)還會(huì)有什么問(wèn)題呢?這次我們將目光轉(zhuǎn)移到執(zhí)行結(jié)果的處理邏輯上。從上面的代碼中可以看出,我們需要手動(dòng)從 ResultSet 中取出數(shù)據(jù),然后再設(shè)置到 Article 對(duì)象中。好在我們的 Article 屬性不多,所以這樣做看起來(lái)也沒(méi)什么。假如 Article 對(duì)象有幾十個(gè)屬性,再用上面的方式接收查詢結(jié)果,會(huì)非常的麻煩。而且可能還會(huì)因?yàn)閷傩蕴啵瑢?dǎo)致忘記設(shè)置某些屬性。以上的代碼還有一個(gè)問(wèn)題,用戶需要自行處理受檢異常,這也是導(dǎo)致代碼繁瑣的一個(gè)原因。哦,還有一個(gè)問(wèn)題,差點(diǎn)忘了。用戶還需要手動(dòng)管理數(shù)據(jù)庫(kù)連接,開始要手動(dòng)獲取數(shù)據(jù)庫(kù)連接。使用好后,又要手動(dòng)關(guān)閉數(shù)據(jù)庫(kù)連接。不得不說(shuō),真麻煩。

沒(méi)想到直接使用 JDBC 訪問(wèn)數(shù)據(jù)庫(kù)會(huì)有這么多的問(wèn)題。如果在生產(chǎn)環(huán)境直接使用 JDBC,怕是要被 Leader 打死了。當(dāng)然,視情況而定。如果項(xiàng)目非常小,且對(duì)數(shù)據(jù)庫(kù)依賴比較低。直接使用 JDBC 也很方便,不用像 MyBatis 那樣搞一堆配置了。

3.2.2 MyBatis VS JDBC

上面說(shuō)了一大堆 JDBC 的壞話,有點(diǎn)過(guò)意不去,所以下面來(lái)吐槽一下 MyBatis 吧。與 JDBC 相比,MyBatis 缺點(diǎn)比較明顯,它的配置比較多,特別是 SQL 映射文件。如果一個(gè)大型項(xiàng)目中有幾十上百個(gè) Dao 接口,就需要有同等數(shù)量的 SQL 映射文件,這些映射文件需要用戶自行維護(hù)。不過(guò)與 JDBC 相比,維護(hù)映射文件不是什么問(wèn)題。不然如果把同等數(shù)量的 SQL 像 JDBC 那樣寫在代碼中,那維護(hù)的代價(jià)才叫大,搞不好還會(huì)翻車。除了配置文件的問(wèn)題,大家會(huì)發(fā)現(xiàn)使用 MyBatis 訪問(wèn)數(shù)據(jù)庫(kù)好像過(guò)程也很繁瑣啊。它的步驟大致如下:

讀取配置文件

創(chuàng)建 SqlSessionFactoryBuilder 對(duì)象

通過(guò) SqlSessionFactoryBuilder 對(duì)象創(chuàng)建 SqlSessionFactory

通過(guò) SqlSessionFactory 創(chuàng)建 SqlSession

為 Dao 接口生成代理類

調(diào)用接口方法訪問(wèn)數(shù)據(jù)庫(kù)

如上,如果每次執(zhí)行一個(gè) SQL 要經(jīng)過(guò)上面幾步,那和 JDBC 比較起來(lái),也沒(méi)什優(yōu)勢(shì)了。不過(guò)這里大家需要注意,SqlSessionFactoryBuilder 和 SqlSessionFactory 以及 SqlSession 等對(duì)象的作用域和生命周期是不一樣的,這一點(diǎn)在 MyBatis 官方文檔中說(shuō)的比較清楚,我這里照搬一下。SqlSessionFactoryBuilder 對(duì)象用于構(gòu)建 SqlSessionFactory,只要構(gòu)建好,這個(gè)對(duì)象就可以丟棄了。SqlSessionFactory 是一個(gè)工廠類,一旦被創(chuàng)建就應(yīng)該在應(yīng)用運(yùn)行期間一直存在,不應(yīng)該丟棄或重建。SqlSession 不是線程安全的,所以不應(yīng)被多線程共享。官方推薦的使用方式是有按需創(chuàng)建,用完即銷毀。因此,以上步驟中,第1、2和第3步只需執(zhí)行一次。第4和第5步需要進(jìn)行多次創(chuàng)建。至于第6步,這一步是必須的。所以比較下來(lái),MyBatis 的使用方式還是比 JDBC 簡(jiǎn)單的。同時(shí),使用 MyBatis 無(wú)需處理受檢異常,比如 SQLException。另外,把 SQL 寫在配置文件中,進(jìn)行集中管理,利于維護(hù)。同時(shí)將 SQL 從代碼中剝離,在提高代碼的可讀性的同時(shí),也避免拼接 SQL 可能會(huì)導(dǎo)致的錯(cuò)誤。除了上面所說(shuō)這些,MyBatis 會(huì)將查詢結(jié)果轉(zhuǎn)為相應(yīng)的對(duì)象,無(wú)需用戶自行處理 ResultSet。

總的來(lái)說(shuō),MyBatis 在易用性上要比 JDBC 好太多。不過(guò)這里拿 MyBatis 和 JDBC 進(jìn)行對(duì)比并不太合適。JDBC 作為 Java 平臺(tái)的數(shù)據(jù)庫(kù)訪問(wèn)規(guī)范,它僅提供一種訪問(wèn)數(shù)據(jù)庫(kù)的能力。至于使用者覺(jué)得 JDBC 流程繁瑣,還要自行處理異常等問(wèn)題,這些還真不怪 JDBC。比如 SQLException 這個(gè)異常,JDBC 沒(méi)法處理啊,拋給調(diào)用者處理也是理所應(yīng)當(dāng)?shù)摹V劣诜彪s的步驟,這僅是從使用者的角度考慮的,從 JDBC 的角度來(lái)說(shuō),這里的每個(gè)步驟對(duì)于完成一個(gè)數(shù)據(jù)訪問(wèn)請(qǐng)求來(lái)說(shuō)都是必須的。至于 MyBatis,它是構(gòu)建在 JDBC 技術(shù)之上的,對(duì)訪問(wèn)數(shù)據(jù)庫(kù)的操作進(jìn)行了簡(jiǎn)化,方便用戶使用。綜上所述,JDBC 可看做是一種基礎(chǔ)服務(wù),MyBatis 則是構(gòu)建在基礎(chǔ)服務(wù)之上的框架,它們的目標(biāo)是不同的。

3.3 使用 Spring JDBC 訪問(wèn)數(shù)據(jù)庫(kù)

上一節(jié)演示了 JDBC 訪問(wèn)數(shù)據(jù)的過(guò)程,通過(guò)演示及分析,大家應(yīng)該感受到了直接使用 JDBC 的一些痛點(diǎn)。為了解決其中的一些痛點(diǎn),Spring JDBC 應(yīng)運(yùn)而生。Spring JDBC 在 JDBC 基礎(chǔ)上,進(jìn)行了比較薄的包裝,易用性得到了不少提升。那下面我們來(lái)看看如何使用 Spring JDBC。

我們?cè)谑褂?Spring JDBC 之前,需要進(jìn)行一些配置。這里我把配置信息放在了 application.xml 文件中,后面寫測(cè)試代碼時(shí),讓容器去加載這個(gè)配置。配置內(nèi)容如下:




    
    
    
    


    

如上,JdbcTemplate封裝了一些訪問(wèn)數(shù)據(jù)庫(kù)的方法,下面我們會(huì)通過(guò)此對(duì)象訪問(wèn)數(shù)據(jù)庫(kù)。演示代碼如下:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class SpringJdbcTest {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    public void testSpringJdbc() {
        String author = "coolblog.xyz";
        String date = "2018.06.10";
        String sql = "SELECT id, title, author, content, create_time FROM article WHERE author = "" + author + "" AND create_time > "" + date + """;
        List
articles = jdbcTemplate.query(sql, (rs, rowNum) -> { Article article = new Article(); article.setId(rs.getInt("id")); article.setTitle(rs.getString("title")); article.setAuthor(rs.getString("author")); article.setContent(rs.getString("content")); article.setCreateTime(rs.getDate("create_time")); return article; }); System.out.println("Query SQL ==> " + sql); System.out.println("Spring JDBC Query Result: "); articles.forEach(System.out::println); } }

測(cè)試結(jié)果如下:

從上面的代碼中可以看得出,Spring JDBC 還是比較容易使用的。不過(guò)它也是存在一定缺陷的,比如 SQL 仍是寫在代碼中。又比如,對(duì)于較為復(fù)雜的結(jié)果(數(shù)據(jù)庫(kù)返回的記錄包含多列數(shù)據(jù)),需要用戶自行處理 ResultSet 等。不過(guò)與 JDBC 相比,使用 Spring JDBC 無(wú)需手動(dòng)加載數(shù)據(jù)庫(kù)驅(qū)動(dòng),獲取數(shù)據(jù)庫(kù)連接,以及創(chuàng)建 Statement 對(duì)象等操作。總的來(lái)說(shuō),易用性上得到了不少的提升。

這里就不對(duì)比 Spring JDBC 和 MyBatis 的優(yōu)缺點(diǎn)了。Spring JDBC 僅對(duì) JDBC 進(jìn)行了一層比較薄的封裝,相關(guān)對(duì)比可以參考上一節(jié)的部分分析,這里不再贅述。

3.4 使用 Hibernate 訪問(wèn)數(shù)據(jù)庫(kù)

本節(jié)會(huì)像之前的章節(jié)一樣,我會(huì)先寫代碼進(jìn)行演示,然后再對(duì)比 Hibernate 和 MyBatis 的區(qū)別。需要特別說(shuō)明的是,我在工作中沒(méi)有用過(guò) Hibernate,對(duì) Hibernate 也僅停留在了解的程度上。本節(jié)的測(cè)試代碼都是現(xiàn)學(xué)現(xiàn)賣的,可能有些地方寫的會(huì)有問(wèn)題,或者不是最佳實(shí)踐。所以關(guān)于測(cè)試代碼,大家看看就好。若有不妥之處,也歡迎指出。

3.4.1 Hibernate 訪問(wèn)數(shù)據(jù)庫(kù)的過(guò)程演示

使用 Hibernate,需要先進(jìn)行環(huán)境配置,主要是關(guān)于數(shù)據(jù)庫(kù)方面的配置。這里為了演示,我們簡(jiǎn)單配置一下。如下:


    
        com.mysql.cj.jdbc.Driver
        jdbc:mysql://localhost:3306/myblog?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE
        root
        ****
        org.hibernate.dialect.MySQL5Dialect
        true

        
    

下面再配置一下實(shí)體類和表之間的映射關(guān)系,也就是上面配置中出現(xiàn)的Article.hbm.xml。不過(guò)這個(gè)配置不是必須的,可用注解進(jìn)行替換。


    
        
            
        
        
        
        
        
    

測(cè)試代碼如下:

public class HibernateTest {

    private SessionFactory buildSessionFactory;

    @Before
    public void init() {
        Configuration configuration = new Configuration();
        configuration.configure("hibernate.cfg.xml");
        buildSessionFactory = configuration.buildSessionFactory();
    }

    @After
    public void destroy() {
        buildSessionFactory.close();
    }

    @Test
    public void testORM() {
        System.out.println("-----------------------------? ORM Query ?--------------------------");

        Session session = null;
        try {
            session = buildSessionFactory.openSession();
            int id = 6;
            Article article = session.get(Article.class, id);
            System.out.println("ORM Query Result: ");
            System.out.println(article);
            System.out.println();
        } finally {
            if (Objects.nonNull(session)) {
                session.close();
            }
        }

    }

    @Test
    public void testHQL() {
        System.out.println("-----------------------------? HQL Query ?+--------------------------");
        Session session = null;
        try {
            session = buildSessionFactory.openSession();
            String hql = "from Article where author = :author and create_time > :createTime";
            Query query = session.createQuery(hql);
            query.setParameter("author", "coolblog.xyz");
            query.setParameter("createTime", "2018.06.10");

            List
articles = query.list(); System.out.println("HQL Query Result: "); articles.forEach(System.out::println); System.out.println(); } finally { if (Objects.nonNull(session)) { session.close(); } } } @Test public void testJpaCriteria() throws ParseException { System.out.println("---------------------------? JPA Criteria ?------------------------"); Session session = null; try { session = buildSessionFactory.openSession(); CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder(); CriteriaQuery
criteriaQuery = criteriaBuilder.createQuery(Article.class); // 定義 FROM 子句 Root
article = criteriaQuery.from(Article.class); // 構(gòu)建查詢條件 SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd"); Predicate greaterThan = criteriaBuilder.greaterThan(article.get("createTime"), sdf.parse("2018.06.10")); Predicate equal = criteriaBuilder.equal(article.get("author"), "coolblog.xyz"); // 通過(guò)具有語(yǔ)義化的方法構(gòu)建 SQL,等價(jià)于 SELECT ... FROM article WHERE ... AND ... criteriaQuery.select(article).where(equal, greaterThan); Query
query = session.createQuery(criteriaQuery); List
articles = query.getResultList(); System.out.println("JPA Criteria Query Result: "); articles.forEach(System.out::println); } finally { if (Objects.nonNull(session)) { session.close(); } } } }

這里我寫了三種不同的查詢方法,對(duì)于比較簡(jiǎn)單的查詢,可以通過(guò)OID的方式進(jìn)行,也就是testORM方法中對(duì)應(yīng)的代碼。這種方式不需要寫 SQL,完全由 Hibernate 去生成。生成的 SQL 如下:

select 
    article0_.id as id1_0_0_, 
    article0_.title as title2_0_0_, 
    article0_.author as author3_0_0_, 
    article0_.content as content4_0_0_, 
    article0_.create_time as create_t5_0_0_ 
from 
    article article0_ 
where 
    article0_.id=?

第二種方式是通過(guò)HQL進(jìn)行查詢,查詢過(guò)程對(duì)應(yīng)測(cè)試類中的testHQL方法。這種方式需要寫一點(diǎn) HQL,并為其設(shè)置相應(yīng)的參數(shù)。最終生成的 SQL 如下:

select 
    article0_.id as id1_0_, 
    article0_.title as title2_0_, 
    article0_.author as author3_0_, 
    article0_.content as content4_0_, 
    article0_.create_time as create_t5_0_ 
from 
    article article0_ 
where 
    article0_.author=? and create_time>?

第三種方式是通過(guò) JPA Criteria 進(jìn)行查詢,JPA Criteria 具有類型安全、面向?qū)ο蠛驼Z(yǔ)義化的特點(diǎn)。使用 JPA Criteria,我們可以用寫 Java 代碼的方式進(jìn)行數(shù)據(jù)庫(kù)操作,無(wú)需手寫 SQL。第二種方式和第三種方式進(jìn)行的是同樣的查詢,所以生成的 SQL 區(qū)別不大,這里就不貼出來(lái)了。

下面看一下測(cè)試代碼的運(yùn)行結(jié)果:

3.4.2 MyBatis VS Hibernate

在 Java 中,就持久層框架來(lái)說(shuō),MyBatis 和 Hibernate 都是很熱門的框架。關(guān)于這兩個(gè)框架孰好孰壞,在網(wǎng)上也有很廣泛的討論。不過(guò)就像我前面說(shuō)到那樣,技術(shù)之間通常沒(méi)有高低之分,適不適合才是應(yīng)該關(guān)注的點(diǎn)。這兩個(gè)框架之前的區(qū)別是比較大的,下面我們來(lái)聊聊。

從映射關(guān)系上來(lái)說(shuō),Hibernate 是把實(shí)體類(POJO)和表進(jìn)行了關(guān)聯(lián),是一種完整的 ORM (O/R mapping) 框架。而MyBatis 則是將數(shù)據(jù)訪問(wèn)接口(Dao)與 SQL 進(jìn)行了關(guān)聯(lián),本質(zhì)上算是一種 SQL 映射。從使用的角度來(lái)說(shuō),使用 Hibernate 通常不需要寫 SQL,讓框架自己生成就可以了。但 MyBatis 則不行,再簡(jiǎn)單的數(shù)據(jù)庫(kù)訪問(wèn)操作都需要有與之對(duì)應(yīng)的 SQL。另一方面,由于 Hibernate 可自動(dòng)生成 SQL,所以進(jìn)行數(shù)據(jù)庫(kù)移植時(shí),代價(jià)要小一點(diǎn)。而由于使用 MyBatis 需要手寫 SQL,不同的數(shù)據(jù)庫(kù)在 SQL 上存在著一定的差異。這就導(dǎo)致進(jìn)行數(shù)據(jù)庫(kù)移植時(shí),可能需要更改 SQL 的情況。不過(guò)好在移植數(shù)據(jù)庫(kù)的情況很少見(jiàn),可以忽略。

上面我從兩個(gè)維度對(duì) Hibernate 和 MyBatis 進(jìn)行了對(duì)比,但目前也只是說(shuō)了他們的一些不同點(diǎn)。下面我們來(lái)分析一下這兩個(gè)框架的適用場(chǎng)景。

Hibernate 可自動(dòng)生成 SQL,降低使用成本。但同時(shí)也要意識(shí)到,這樣做也是有代價(jià)的,會(huì)損失靈活性。比如,如果我們需要手動(dòng)優(yōu)化 SQL,我們很難改變 Hibernate 生成的 SQL。因此對(duì)于 Hibernate 來(lái)說(shuō),它適用于一些需求比較穩(wěn)定,變化比較小的項(xiàng)目,譬如 OA、CRM 等。

與 Hibernate 相反,MyBatis 需要手動(dòng)維護(hù) SQL,這會(huì)增加使用成本。但同時(shí),使用者可靈活控制 SQL 的行為,這為改動(dòng)和優(yōu)化 SQL 提供了可能。所以 MyBatis 適合應(yīng)用在一些需要快速迭代,需求變化大的項(xiàng)目中,這也就是為什么 MyBatis 在互聯(lián)網(wǎng)公司中使用的比較廣泛的原因。除此之外,MyBatis 還提供了插件機(jī)制,使用者可以按需定制插件。這也是 MyBatis 靈活性的一個(gè)體現(xiàn)。

分析到這里,大家應(yīng)該清楚了兩個(gè)框架之前的區(qū)別,以及適用場(chǎng)景。樓主目前在一家汽車相關(guān)的互聯(lián)網(wǎng)公司,公司發(fā)展的比較快,項(xiàng)目迭代的也比較快,各種小需求也比較多。所以,相比之下,MyBatis 是一個(gè)比較合適的選擇。

3.5 本章小結(jié)

本節(jié)用了大量的篇幅介紹常見(jiàn)持久層框架的用法,并進(jìn)行了較為詳細(xì)的分析和對(duì)比。看完這些,相信大家對(duì)這些框架應(yīng)該也有了更多的了解。好了,其他的就不多說(shuō)了,我們繼續(xù)往下看吧。

4.如何使用 MyBatis

本章,我們一起來(lái)看一下 MyBatis 是如何使用的。在上一章,我簡(jiǎn)單演示了一下 MyBatis 的使用方法。不過(guò),那個(gè)太簡(jiǎn)單了,本章我們來(lái)演示一個(gè)略為復(fù)雜的例子。不過(guò),這個(gè)例子復(fù)雜度和真實(shí)的項(xiàng)目還是有差距,僅做演示使用。

本章包含兩節(jié)內(nèi)容,第一節(jié)演示多帶帶使用 MyBatis 的過(guò)程,第二節(jié)演示 MyBatis 是如何和 Spring 進(jìn)行整合的。那其他的就不多說(shuō)了,下面開始演示。

4.1 多帶帶使用

本節(jié)演示的場(chǎng)景是個(gè)人網(wǎng)站的作者和文章之間的關(guān)聯(lián)場(chǎng)景。在一個(gè)網(wǎng)站中,一篇文章對(duì)應(yīng)一名作者,一個(gè)作者對(duì)應(yīng)多篇文章。下面我們來(lái)看一下作者文章的定義,如下:

public class AuthorDO implements Serializable {
    private Integer id;
    private String name;
    private Integer age;
    private SexEnum sex;
    private String email;
    private List articles;

    // 省略 getter/setter 和 toString
}

public class ArticleDO implements Serializable {
    private Integer id;
    private String title;
    private ArticleTypeEnum type;
    private AuthorDO author;
    private String content;
    private Date createTime;

    // 省略 getter/setter 和 toString
}

如上,AuthorDO 中包含了對(duì)一組 ArticleDO 的引用,這是一對(duì)多的關(guān)系。ArticleDO 中則包含了一個(gè)對(duì) AuthorDO 的引用,這是一對(duì)一的關(guān)系。除此之外,這里使用了兩個(gè)常量,一個(gè)用于表示性別,另一個(gè)用于表示文章類型,它們的定義如下:

public enum SexEnum {
    MAN,
    FEMALE,
    UNKNOWN;
}

public enum ArticleTypeEnum {
    JAVA(1),
    DUBBO(2),
    SPRING(4),
    MYBATIS(8);

    private int code;

    ArticleTypeEnum(int code) {
        this.code = code;
    }

    public int code() {
        return code;
    }

    public static ArticleTypeEnum find(int code) {
        for (ArticleTypeEnum at : ArticleTypeEnum.values()) {
            if (at.code == code) {
                return at;
            }
        }

        return null;
    }
}

本篇文章使用了兩張表,分別用于存儲(chǔ)文章和作者信息。這兩種表的內(nèi)容如下:

下面來(lái)看一下數(shù)據(jù)庫(kù)訪問(wèn)層的接口定義,如下:

public interface ArticleDao {
    ArticleDO findOne(@Param("id") int id);
}

public interface AuthorDao {
    AuthorDO findOne(@Param("id") int id);
}

與這兩個(gè)接口對(duì)應(yīng)的 SQL 被配置在了下面的兩個(gè)映射文件中。我們先來(lái)看一下第一個(gè)映射文件 AuthorMapper.xml 的內(nèi)容。




    
        
        
        
        
        
    

    
        
        
        
        
        
        
    

    

注意看上面的配置,這個(gè)標(biāo)簽中包含了一個(gè)一對(duì)多的配置,這個(gè)配置引用了一個(gè) id 為articleResult。除了要注意一對(duì)多的配置,這里還要下面這行配置:

前面說(shuō)過(guò) AuthorDO 的sex屬性是一個(gè)枚舉,但這個(gè)屬性在數(shù)據(jù)表中是以整型值進(jìn)行存儲(chǔ)的。所以向數(shù)據(jù)表寫入或者查詢數(shù)據(jù)時(shí),要進(jìn)行類型轉(zhuǎn)換。寫入時(shí),需要將SexEnum轉(zhuǎn)成int。查詢時(shí),則需要把int轉(zhuǎn)成SexEnum。由于這兩個(gè)是完全不同的類型,不能通過(guò)強(qiáng)轉(zhuǎn)進(jìn)行轉(zhuǎn)換,所以需要使用一個(gè)中間類進(jìn)行轉(zhuǎn)換,這個(gè)中間類就是 EnumOrdinalTypeHandler。這個(gè)類會(huì)按照枚舉順序進(jìn)行轉(zhuǎn)換,比如在SexEnum中,MAN的順序是0。存儲(chǔ)時(shí),EnumOrdinalTypeHandler 會(huì)將MAN替換為0。查詢時(shí),又會(huì)將0轉(zhuǎn)換為MAN。除了EnumOrdinalTypeHandler,MyBatis 還提供了另一個(gè)枚舉類型處理器EnumTypeHandler。這個(gè)則是按照枚舉的字面值進(jìn)行轉(zhuǎn)換,比如該處理器將枚舉MAN和字符串 "MAN" 進(jìn)行相互轉(zhuǎn)換。

上面簡(jiǎn)單分析了一下枚舉類型處理器,接下來(lái),繼續(xù)往下看。下面是 ArticleMapper.xml 的配置內(nèi)容:




    
        
        
        
        
        
    

    
        
        
        
        
        
        
    

    

如上,ArticleMapper.xml 中包含了一個(gè)一對(duì)一的配置,這個(gè)配置引用了另一個(gè) id 為authorResult。除了一對(duì)一的配置外,這里還有一個(gè)自定義類型處理器ArticleTypeHandler需要大家注意。這個(gè)自定義類型處理器用于處理ArticleTypeEnum枚舉類型。大家如果注意看前面貼的ArticleTypeEnum的源碼,會(huì)發(fā)現(xiàn)每個(gè)枚舉值有自己的編號(hào)定義。比如JAVA的編號(hào)為1DUBBO的編號(hào)為2SPRING的編號(hào)為8。所以這里我們不能再使用EnumOrdinalTypeHandler對(duì)ArticleTypeHandler進(jìn)行類型轉(zhuǎn)換,需要自定義一個(gè)類型轉(zhuǎn)換器。那下面我們來(lái)看一下這個(gè)類型轉(zhuǎn)換器的定義。

public class ArticleTypeHandler extends BaseTypeHandler {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, ArticleTypeEnum parameter, JdbcType jdbcType)
        throws SQLException {
        // 獲取枚舉的 code 值,并設(shè)置到 PreparedStatement 中
        ps.setInt(i, parameter.code());
    }

    @Override
    public ArticleTypeEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 從 ResultSet 中獲取 code
        int code = rs.getInt(columnName);
        // 解析 code 對(duì)應(yīng)的枚舉,并返回
        return ArticleTypeEnum.find(code);
    }

    @Override
    public ArticleTypeEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        int code = rs.getInt(columnIndex);
        return ArticleTypeEnum.find(code);
    }

    @Override
    public ArticleTypeEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        int code = cs.getInt(columnIndex);
        return ArticleTypeEnum.find(code);
    }
}

對(duì)于自定義類型處理器,可繼承 BaseTypeHandler,并實(shí)現(xiàn)相關(guān)的抽象方法。上面的代碼比較簡(jiǎn)單,我也進(jìn)行了一些注釋。應(yīng)該比較好理解,這里就不多說(shuō)了。

前面貼了實(shí)體類,數(shù)據(jù)訪問(wèn)類,以及 SQL 映射文件。最后還差一個(gè) MyBatis 的配置文件,這里貼出來(lái)。如下:



    

    
        
        
    

    
        
    

    
        
            
            
                
                
                
                
            
        
    

    
        
        
    

下面通過(guò)一個(gè)表格簡(jiǎn)單解釋配置中出現(xiàn)的一些標(biāo)簽。

標(biāo)簽名稱 用途
properties 用于配置全局屬性,這樣在配置文件中,可以通過(guò)占位符 ${} 進(jìn)行屬性值配置
typeAliases 用于定義別名。如上所示,這里把xyz.coolblog.model.ArticleDO的別名定義為Article,這樣在 SQL 映射文件中,就可以直接使用別名,而不用每次都輸入長(zhǎng)長(zhǎng)的全限定類名了
typeHandlers 用于定義全局的類型處理器,如果這里配置了,SQL 映射文件中就不需要再次進(jìn)行配置。前面為了講解需要,我在 SQL 映射文件中也配置了 ArticleTypeHandler,其實(shí)是多余的
environments 用于配置事務(wù),以及數(shù)據(jù)源
mappers 用于配置 SQL 映射文件的位置信息

以上僅介紹了一些比較常用的配置,更多的配置信息,建議大家去閱讀MyBatis 官方文檔。

到這里,我們把所有的準(zhǔn)備工作都做完了。那么接下來(lái),寫點(diǎn)測(cè)試代碼測(cè)試一下。

public class MyBatisTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void prepare() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        inputStream.close();
    }
    
    @Test
    public void testOne2One() {
        SqlSession session = sqlSessionFactory.openSession();
        try {
            ArticleDao articleDao = session.getMapper(ArticleDao.class);
            ArticleDO article = articleDao.findOne(1);

            AuthorDO author = article.getAuthor();
            article.setAuthor(null);

            System.out.println();
            System.out.println("author info:");
            System.out.println(author);
            System.out.println();
            System.out.println("articles info:");
            System.out.println(article);
        } finally {
            session.close();
        }
    }

    @Test
    public void testOne2Many() {
        SqlSession session = sqlSessionFactory.openSession();
        try {
            AuthorDao authorDao = session.getMapper(AuthorDao.class);
            AuthorDO author = authorDao.findOne(1);

            List arts = author.getArticles();
            List articles = Arrays.asList(arts.toArray(new ArticleDO[arts.size()]));
            arts.clear();

            System.out.println();
            System.out.println("author info:");
            System.out.println(author);
            System.out.println();
            System.out.println("articles info:");
            articles.forEach(System.out::println);
        } finally {
            session.close();
        }
    }
}

第一個(gè)測(cè)試方法用于從數(shù)據(jù)庫(kù)中查詢某篇文章,以及相應(yīng)作者的信息。它的運(yùn)行結(jié)果如下:

第二個(gè)測(cè)試方法用于查詢某位作者,及其所寫的所有文章的信息。它的運(yùn)行結(jié)果如下:

到此,MyBatis 的使用方法就介紹完了。由于我個(gè)人在平時(shí)的工作中,也知識(shí)使用了 MyBatis 的一些比較常用的特性,所以本節(jié)的內(nèi)容也比較淺顯。另外,由于演示示例比較簡(jiǎn)單,這里也沒(méi)有演示 MyBatis 比較重要的一個(gè)特性 -- 動(dòng)態(tài) SQL。除了以上所述,有些特性由于沒(méi)有比較好的場(chǎng)景去演示,這里也就不介紹了。比如 MyBatis 的插件機(jī)制,緩存等。對(duì)于一些較為生僻的特性,比如對(duì)象工廠,鑒別器。如果不是因?yàn)殚喿x了 MyBatis 的文檔和一些書籍,我還真不知道它們的存在,孤陋寡聞了。所以,對(duì)于這部分特性,本文也不會(huì)進(jìn)行說(shuō)明。

綜上所述,本節(jié)所演示的是一個(gè)比較簡(jiǎn)單的示例,并非完整示例,望周知。

4.2 在 Spring 中使用

在上一節(jié),我演示了多帶帶使用 MyBatis 的過(guò)程。在實(shí)際開發(fā)中,我們一般都會(huì)將 MyBatis 和 Spring 整合在一起使用。這樣,我們就可以通過(guò) bean 注入的方式使用各種 Dao 接口。MyBatis 和 Spring 原本是兩個(gè)完全不相關(guān)的框架,要想把兩者整合起來(lái),需要一個(gè)中間框架。這個(gè)框架一方面負(fù)責(zé)加載和解析 MyBatis 相關(guān)配置。另一方面,該框架還會(huì)通過(guò) Spring 提供的拓展點(diǎn),把各種 Dao 接口及其對(duì)應(yīng)的對(duì)象放入 bean 工廠中。這樣,我們才可以通過(guò) bean 注入的方式獲取到這些 Dao 接口對(duì)應(yīng)的 bean。那么問(wèn)題來(lái)了,具有如此能力的框架是誰(shuí)呢?答案是mybatis-spring。那其他的不多說(shuō)了,下面開始演示整合過(guò)程。

我的測(cè)試項(xiàng)目是基于 Maven 構(gòu)建的,所以這里先來(lái)看一下 pom 文件的配置。


    

    
        4.3.17.RELEASE
    

    
        
            org.mybatis
            mybatis
            3.4.6
        
        
            org.mybatis
            mybatis-spring
            1.3.2
        

        
            org.springframework
            spring-core
            ${spring.version}
        
        
            org.springframework
            spring-beans
            ${spring.version}
        
        
            org.springframework
            spring-context
            ${spring.version}
        
        
            org.springframework
            spring-jdbc
            ${spring.version}
        
        
            org.springframework
            spring-test
            ${spring.version}
            test
        

        
    

為了減少配置文件所占的文章篇幅,上面的配置經(jīng)過(guò)了一定的簡(jiǎn)化,這里只列出了 MyBatis 和 Spring 相關(guān)包的坐標(biāo)。繼續(xù)往下看,下面將 MyBatis 中的一些類配置到 Spring 的配置文件中。



    

    
    
        
        
        
        
    

    
    
        
        
        
        
        
        
    

    
    
        
        
    

如上,上面就是將 MyBatis 整合到 Spring 中所需的一些配置。這里,我們將數(shù)據(jù)源配置到 Spring 配置文件中。配置完數(shù)據(jù)源,接下來(lái)配置 SqlSessionFactory,SqlSessionFactory 的用途大家都知道,不用過(guò)多解釋了。再接下來(lái)是配置 MapperScannerConfigurer,這個(gè)類顧名思義,用于掃描某個(gè)包下的數(shù)據(jù)訪問(wèn)接口,并將這些接口注冊(cè)到 Spring 容器中。這樣,我們就可以在其他的 bean 中注入 Dao 接口的實(shí)現(xiàn)類,無(wú)需再?gòu)?SqlSession 中獲取接口實(shí)現(xiàn)類。至于 MapperScannerConfigurer 掃描和注冊(cè) Dao 接口的細(xì)節(jié),這里先不說(shuō)明,后續(xù)我會(huì)專門寫一篇文章分析。

將 MyBatis 配置到 Spring 中后,為了讓我們的程序正常運(yùn)行,這里還需要為 MyBatis 提供一份配置。相關(guān)配置如下:



    
        
    
    
    
        
        
    

    
        
    

這里的 mybatis-config.xml 和上一節(jié)的配置不太一樣,移除了數(shù)據(jù)源和 SQL 映射文件路徑的配置。需要注意的是,對(duì)于 必須配置在 mybatis-config.xml 中。其他的配置都不是必須項(xiàng),可放在 Spring 的配置文件中,這里偷了個(gè)懶。

到此,Spring 整合 MyBatis 的配置工作就完成了,接下來(lái)寫點(diǎn)測(cè)試代碼跑跑看。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application-mybatis.xml")
public class SpringWithMyBatisTest implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    /** 自動(dòng)注入 AuthorDao,無(wú)需再通過(guò) SqlSession 獲取 */ 
    @Autowired
    private AuthorDao authorDao;

    @Autowired
    private ArticleDao articleDao;

    @Before
    public void printBeanInfo() {
        ListableBeanFactory lbf = applicationContext;
        String[] beanNames = lbf.getBeanDefinitionNames();
        Arrays.sort(beanNames);

        System.out.println();
        System.out.println("----------------☆ bean name ☆---------------");
        Arrays.asList(beanNames).subList(0, 5).forEach(System.out::println);
        System.out.println();

        AuthorDao authorDao = (AuthorDao) applicationContext.getBean("authorDao");
        ArticleDao articleDao = (ArticleDao) applicationContext.getBean("articleDao");

        System.out.println("-------------☆ bean class info ☆--------------");
        System.out.println("AuthorDao  Class: " + authorDao.getClass());
        System.out.println("ArticleDao Class: " + articleDao.getClass());
        System.out.println("
--------xxxx---------xxxx---------xxx---------
");
    }


    @Test
    public void testOne2One() {
        ArticleDO article = articleDao.findOne(1);

        AuthorDO author = article.getAuthor();
        article.setAuthor(null);

        System.out.println();
        System.out.println("author info:");
        System.out.println(author);
        System.out.println();
        System.out.println("articles info:");
        System.out.println(article);
    }

    @Test
    public void testOne2Many() {
        AuthorDO author = authorDao.findOne(1);

        System.out.println();
        System.out.println("author info:");
        System.out.println(author);
        System.out.println();
        System.out.println("articles info:");
        author.getArticles().forEach(System.out::println);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

如上代碼,為了證明我們的整合配置生效了,上面專門寫了一個(gè)方法,用于輸出ApplicationContextbean的信息。下面來(lái)看一下testOne2One測(cè)試方法的輸出結(jié)果。

如上所示,bean name 的前兩行就是我們的 Dao 接口的名稱,它們的實(shí)現(xiàn)類則是 JDK 的動(dòng)態(tài)代理生成的。然后testOne2One方法也正常運(yùn)行了,由此可知,我們的整合配置生效了。

5.總結(jié)

到此,本篇文章就接近尾聲了。本篇文章對(duì) MyBatis 是什么,為何要使用,以及如何使用等三個(gè)方面進(jìn)行闡述和演示。總的來(lái)說(shuō),本文的篇幅應(yīng)該說(shuō)清楚了這三個(gè)問(wèn)題。本篇文章的篇幅比較大,讀起來(lái)應(yīng)該比較辛苦。不過(guò)好在內(nèi)容不難,理解起來(lái)應(yīng)該沒(méi)什么問(wèn)題。本篇文章的篇幅超出了我之前的預(yù)期,文章太大,出錯(cuò)的概率也會(huì)隨之上升。所以如果文章有錯(cuò)誤的地方,希望大家能夠指明。

好了,本篇文章就到這里了,感謝大家的閱讀。

參考

MyBatis 官方文檔

MyBatis從入門到精通 - 劉增輝

MyBatis和Hibernate相比,優(yōu)勢(shì)在哪里?- 知乎

mybatis 與 hibernate 的區(qū)別和應(yīng)用場(chǎng)景 - 無(wú)法確定文章作者,就不貼鏈接了,請(qǐng)自行搜索

附錄:MyBatis 源碼分析系列文章列表
更新時(shí)間 標(biāo)題
2018-07-16 MyBatis 源碼分析系列文章導(dǎo)讀
2018-07-20 MyBatis 源碼分析 - 配置文件解析過(guò)程
本文在知識(shí)共享許可協(xié)議 4.0 下發(fā)布,轉(zhuǎn)載需在明顯位置處注明出處
作者:coolblog.xyz
本文同步發(fā)布在我的個(gè)人博客:http://www.coolblog.xyz


本作品采用知識(shí)共享署名-非商業(yè)性使用-禁止演繹 4.0 國(guó)際許可協(xié)議進(jìn)行許可。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/71623.html

相關(guān)文章

  • Spring AOP 源碼分析系列文章導(dǎo)讀

    摘要:在寫完容器源碼分析系列文章中的最后一篇后,沒(méi)敢懈怠,趁熱打鐵,花了天時(shí)間閱讀了方面的源碼。從今天開始,我將對(duì)部分的源碼分析系列文章進(jìn)行更新。全稱是,即面向切面的編程,是一種開發(fā)理念。在中,切面只是一個(gè)概念,并沒(méi)有一個(gè)具體的接口或類與此對(duì)應(yīng)。 1. 簡(jiǎn)介 前一段時(shí)間,我學(xué)習(xí)了 Spring IOC 容器方面的源碼,并寫了數(shù)篇文章對(duì)此進(jìn)行講解。在寫完 Spring IOC 容器源碼分析系列...

    張春雷 評(píng)論0 收藏0
  • MyBatis 源碼分析系列文章合集

    摘要:簡(jiǎn)介我從七月份開始閱讀源碼,并在隨后的天內(nèi)陸續(xù)更新了篇文章。考慮到超長(zhǎng)文章對(duì)讀者不太友好,以及拆分文章工作量也不小等問(wèn)題。經(jīng)過(guò)兩周緊張的排版,一本小小的源碼分析書誕生了。我在寫系列文章中,買了一本書作為參考,這本書是技術(shù)內(nèi)幕。 1.簡(jiǎn)介 我從七月份開始閱讀MyBatis源碼,并在隨后的40天內(nèi)陸續(xù)更新了7篇文章。起初,我只是打算通過(guò)博客的形式進(jìn)行分享。但在寫作的過(guò)程中,發(fā)現(xiàn)要分析的代碼...

    Crazy_Coder 評(píng)論0 收藏0
  • Spring IOC 容器源碼分析 - 余下的初始化工作

    摘要:簡(jiǎn)介本篇文章是容器源碼分析系列文章的最后一篇文章,本篇文章所分析的對(duì)象是方法,該方法用于對(duì)已完成屬性填充的做最后的初始化工作。后置處理器是拓展點(diǎn)之一,通過(guò)實(shí)現(xiàn)后置處理器接口,我們就可以插手的初始化過(guò)程。 1. 簡(jiǎn)介 本篇文章是Spring IOC 容器源碼分析系列文章的最后一篇文章,本篇文章所分析的對(duì)象是 initializeBean 方法,該方法用于對(duì)已完成屬性填充的 bean 做最...

    Alfred 評(píng)論0 收藏0
  • Spring IOC 容器源碼分析系列文章導(dǎo)讀

    摘要:本文是容器源碼分析系列文章的第一篇文章,將會(huì)著重介紹的一些使用方法和特性,為后續(xù)的源碼分析文章做鋪墊。我們可以通過(guò)這兩個(gè)別名獲取到這個(gè)實(shí)例,比如下面的測(cè)試代碼測(cè)試結(jié)果如下本小節(jié),我們來(lái)了解一下這個(gè)特性。 1. 簡(jiǎn)介 Spring 是一個(gè)輕量級(jí)的企業(yè)級(jí)應(yīng)用開發(fā)框架,于 2004 年由 Rod Johnson 發(fā)布了 1.0 版本。經(jīng)過(guò)十幾年的迭代,現(xiàn)在的 Spring 框架已經(jīng)非常成熟了...

    NSFish 評(píng)論0 收藏0
  • 寫這么多系列博客,怪不得找不到女朋友

    摘要:前提好幾周沒(méi)更新博客了,對(duì)不斷支持我博客的童鞋們說(shuō)聲抱歉了。熟悉我的人都知道我寫博客的時(shí)間比較早,而且堅(jiān)持的時(shí)間也比較久,一直到現(xiàn)在也是一直保持著更新狀態(tài)。 showImg(https://segmentfault.com/img/remote/1460000014076586?w=1920&h=1080); 前提 好幾周沒(méi)更新博客了,對(duì)不斷支持我博客的童鞋們說(shuō)聲:抱歉了!。自己這段時(shí)...

    JerryWangSAP 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<