1. 程式人生 > 實用技巧 >真沒想到,入門mybatis這麼簡單

真沒想到,入門mybatis這麼簡單

本文主要內容:

傳統JDBC

傳統JDBC編碼格式

public class DataBaseUtil {
    public static final String URL = "jdbc:mysql://localhost:3306/mblog";
    public static final String USER = "root";
    public static final String PASSWORD = "123456";

    public static void main(String[] args) throws Exception {
        
        Class.forName("com.mysql.jdbc.Driver");
        //2. 
        Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
        //3.
        Statement stmt = conn.createStatement();
        //4.
        ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1");
        //如果有資料,rs.next()返回true
        while(rs.next()){
            System.out.println("name: "+rs.getString("name")+" 年齡:"+rs.getInt("age"));
        }
    }
}

上面程式碼中知識為了展示JDBC整個過程(異常和資源是簡單粗暴的處理了,我們關注的點不在這兩個)。

大致可以分為六個步驟:

  1. 載入驅動程式
  2. 獲得資料庫連線
  3. 建立一個Statement物件
  4. 操作資料庫,實現增刪改查
  5. 獲取結果集
  6. 關閉資源

從使用層面來說,採用原生態的JDBC在專案中使用起來成本還是很高的。如果我們的專案中的業務相對比較複雜,資料庫表也相對較多,各種操作資料庫的增刪改查的方法也會隨之多起來,那麼這樣的程式碼重複次數會非常之多。

傳統JDBC的問題

  • 建立資料庫的連線存在大量的硬編碼,
  • 執行statement時存在硬編碼.
  • 頻繁的開啟和關閉資料庫連線,會嚴重影響資料庫的效能,浪費資料庫的資源.
  • 存在大量的重複性編碼

為了解決以上問題,就誕生了各種各樣替換JDBC的產品。即就是ORM框架。

什麼是ORM?

全稱為Object Relational Mapping。物件-對映-關係型資料庫。物件關係對映(,簡稱ORM,或O/RM,或O/R mapping),用於實現面向物件程式語言裡不同型別系統的資料之間的轉換。簡單的說,ORM是通過使用描述物件和資料庫之間對映的元資料,將程式中的物件與關係資料庫相互對映。

ORM提供了實現持久化層的另一種模式,它採用對映元資料來描述物件關係的對映,使得ORM中介軟體能在任何一個應用的業務邏輯層和資料庫層之間充當橋樑。

比如說:Apache DbUtils、Spring JDBC、 Hibernate、Ibatis

(Mybatis的前生)、Spring Data Jpa 等等。

目前最為流行的MybatisSpring Data Jpa 。本系列我們先講解MybatisJpa後面再講。

ORM的優缺點

優點
1.提高了開發效率。由於ORM可以自動對Entity物件與資料庫中的Table進行欄位與屬性的對映,所以我們實際可能已經不需要一個專用的、龐大的資料訪問層。
2.ORM提供了對資料庫的對映,不用sql直接編碼,能夠像操作物件一樣從資料庫獲取資料。

缺點
犧牲程式的執行效率和會固定思維模式,降低了開發的靈活性。

從系統結構上來看,採用ORM的系統一般都是多層系統,系統的層次多了,效率就會降低。ORM是一種完全的面向物件的做法,而面向物件的做法也會對效能產生一定的影響。
在我們開發系統時,一般都有效能問題。效能問題主要產生在演算法不正確和與資料庫不正確的使用上。ORM所生成的程式碼一般不太可能寫出很高效的演算法,在資料庫應用上更有可能會被誤用,主要體現在對持久物件的提取和和資料的加工處理上,如果用上了ORM,程式設計師很有可能將全部的資料提取到記憶體物件中,然後再進行過濾和加工處理,這樣就容易產生效能問題。
在對物件做持久化時,ORM一般會持久化所有的屬性,有時,這是不希望的。
但ORM是一種工具,工具確實能解決一些重複,簡單的勞動。這是不可否認的。但我們不能指望工具能一勞永逸的解決所有問題,有些問題還是需要特殊處理的,但需要特殊處理的部分對絕大多數的系統,應該是很少的。

MyBatis 是什麼?

如果在面試的時候被問到,只要你說出下面三種即可。

MyBatis 是一款優秀的持久層框架,它支援自定義 SQL、儲存過程以及高階對映。

MyBatis 免除了幾乎所有的 JDBC 程式碼以及設定引數和獲取結果集的工作。

MyBatis 可以通過簡單的 XML 或註解來配置和對映原始型別、介面和 Java POJO(Plain Old Java Objects,普通老式 Java 物件)為資料庫中的記錄。

來自官網。

MyBatis的優點和缺點

優點:

(1)基於SQL語句程式設計,相當靈活,不會對應用程式或者資料庫的現有設計造成任何影響,SQL寫在XML裡,解除sql與程式程式碼的耦合,便於統一管理;提供XML標籤,支援編寫動態SQL語句,並可重用。

(2)與JDBC相比,減少了50%以上的程式碼量,消除了JDBC大量冗餘的程式碼,不需要手動開關連線;

(3)很好的與各種資料庫相容(因為MyBatis使用JDBC來連線資料庫,所以只要JDBC支援的資料庫MyBatis都支援)。

(4)能夠與Spring很好的整合;

(5)提供對映標籤,支援物件與資料庫的ORM欄位關係對映;提供物件關係對映標籤,支援物件關係元件維護。

缺點

(1)SQL語句的編寫工作量較大,尤其當欄位多、關聯表多時,對開發人員編寫SQL語句的功底有一定要求。

(2)SQL語句依賴於資料庫,導致資料庫移植性差,不能隨意更換資料庫。

Mybatis環境搭建及簡單例項

建立一張資料庫表

建立一張m_user表使用MySQL資料庫。

CREATE TABLE `m_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

新增依賴

<dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
            <scope>runtime</scope>
        </dependency>
</dependencies>

專案結構如下:

建立一個mybatis-config.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"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mblog?useUnicode=true"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

實體類User

ublic class User {
    private Integer id;
    private String name;
    private Integer age;
    //set get
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

建立UserMapper.java

import com.tian.mybatis.entity.User;

public interface UserMapper {
    User selectUserById(Integer id);
}

建立UserMapper.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">
<mapper namespace="com.tian.mybatis.mapper.UserMapper">
    <select id="selectUserById" resultType="com.tian.mybatis.entity.User">
        select * from m_user where id = #{id}
    </select>
</mapper>

建立一個測試類

import com.tian.mybatis.entity.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MybatisApplication {

	public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        SqlSession sqlSession =null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
            //工廠模式
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            //sql操作會話
            sqlSession = sqlSessionFactory.openSession();
            //獲取資料並解析成User物件
            User user = sqlSession.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById", 1);
            System.out.println(user);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            sqlSession.close();
        }
    }

}

輸出結果:

User{id=1, name='tian', age=22}

整體步驟:

另外一種啟動方式

import com.tian.mybatis.entity.User;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class MybatisApplication {

    public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //解析xml檔案
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, null, null);
        //構建一個SqlSessionFactory工廠類
        SqlSessionFactory sqlSessionFactory = build(parser.parse());
        //建立一個SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //獲取資料並解析成User物件
        User user = sqlSession.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById", 1);
        System.out.println(user);
    }

    public static SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }
}

輸出和上面一樣。還可以直接使用Java程式碼而不用mybatis-config.xml

//建立資料來源
DataSource dataSource = getDataSource();

TransactionFactory transactionFactory = new JdbcTransactionFactory();

Environment environment = new Environment("development", transactionFactory, dataSource);

Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

小總結

從上面這個案例中我們大致能猜到,Mybatis中最主要幾個元件:

SqlSessionFactoryBuilder

SqlSessionFactory

SqlSession

Mapper

SqlSessionFactoryBuilder
這個類可以被初始、使用和丟棄,如果你已經建立好了一個 SqlSessionFactory 後就不用再保留它。因此 ,
SqlSessionFactoryBuilder 的最好作用域是方法體內,比如說定義一個方法變數。

你可以重複使用SqlSessionFactoryBuilder 生成多個 SqlSessionFactory 例項,但是最好不要強行保留,因為 XML 的解析資源要用來做其它更重要的事。

SqlSessionFactory
一旦建立,SqlSessionFactory 就會在整個應用過程中始終存在。所以沒有理由去銷燬和再建立它,一個
應用執行中也不建議多次建立 SqlSessionFactory。如果真的那樣做,會顯得很拙劣。

因此 SqlSessionFactory最好的作用域是 Application。可以有多種方法實現。最簡單的方法是單例模式或者是靜態單例模式。然而這既不是廣泛贊成和好用的。反而,使用 Google Guice 或 Spring 來進行依賴反射會更好。這些框架允
許你生成管理器來管理 SqlSessionFactory 的單例生命週期

SqlSession
每個執行緒都有自己的 SqlSession 例項,SqlSession 例項是不能被共享,也是不是執行緒安全的。因此最好
使用 Request 作用域或者方法體作用域。

不要使用類的靜態變數來引用一個 SqlSession 例項,甚至不要使用類的一個例項變更來引用。永遠不要在一個被管理域中引用 SqlSession,比如說在 Servlet 中的HttpSession 中。如果你正在使用 WEB 框架,應該讓 SqlSession 跟隨 HTTP 請求的相似作用域。

也就是說,在收到一個 HTTP 請求過後,開啟 SqlSession,等返回一個迴應以後,立馬關掉這個 SqlSession。關閉 SqlSession 是非常重要的。你必須要確保 SqlSession 在 finally 方法體中正常關閉。

SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}

使用這種模式來貫穿你的所有程式碼,以確保所有資料庫資源都被完全關閉。

Mapper

Mapper 是一種你建立的用於繫結對映語句的介面。Mapper 介面的例項是用 SqlSession 來獲得的。同樣 ,
從技術上來說,最廣泛的 Mapper 例項作用域像 SqlSession 一樣,使用請求作用域。確切地說,在方法
被呼叫的時候呼叫 Mapper 例項,然後使用後,就自動銷燬掉。不需要使用明確的登出。當一個請求執
行正確無誤的時候,像 SqlSession 一樣,你可以輕而易舉地操控這一切。保持簡單性,保持 Mapper 在
方法體作用域內。

 //獲取資料並解析成User物件
 User user = sqlSession.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById", 1);
 System.out.println(user);

這裡對映涉及到四個主體:

  1. 實體類User。
  2. 介面UaerMapper
  3. xml配置檔案UserMapper
  4. 資料庫表m_user

Mybatis的五部曲:

總結

回顧JDBC的demo,JDBC中的問題,建立一個Mybatis示例,總結出重要的四個元件,以及每個元件的作用。