天天看點

MyBatis源碼解析(一):MyBatis使用示例

MyBatis源碼解析系列參考:http://www.mybatis.org/mybatis-3/zh/index.html和源碼調試,加上畫圖了解

首先在掌握一門技術前,先得知道它是是什麼,怎麼用。之後再掌握底層原理

1、使用傳統JDBC程式設計:

public static void test() {
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null;
    try {
      // 加載資料庫驅動
      Class.forName("com.mysql.jdbc.Driver");
      // 擷取資料庫連接配接
      connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/table_v200?characterEncoding=utf-8", "root", "1234");
      //定義sql語句
      String sql = "select * from user_weixin where id>? limit 5";
      //使用preparedStatement預編譯,防止sql注入
      preparedStatement = connection.prepareStatement(sql);
      //設定參數
      preparedStatement.setInt(1, 2);
      /**
       * PreparedStatement prepareStatement(String var1, int var2, int var3)
       * var1:sql語句
       * var2:結果集類型:決定ResultSet對象是否可以滾動,是否對資料庫中的修改敏感。
       * var3:結果集并發性 決定Rs 是否可以修改資料庫中的行。
       */
      preparedStatement = connection.prepareStatement(sql);
//       執行sql,擷取結果
      resultSet = preparedStatement.executeQuery();
      // 周遊遊标解析結果集
      while (resultSet.next()) {
        System.out.println("id:" + resultSet.getString("id"));
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      // 釋放資源
      if (resultSet != null) {
        try {
          resultSet.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
      if (preparedStatement != null) {
        try {
          preparedStatement.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
      if (connection != null) {
        try {
          connection.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
    }
  }
           

2、JDBC相關知識引申:

(1)sql注入:

所謂sql注入是将傳入的參數包含有sql指令,或幹擾sql指令執行的語句。or '1=1'   達到欺騙資料庫。影響原意圖執行的語句。

預編譯是如何防止sql注入的?

程式在操作資料庫之前,SQL語句已經被資料庫分析,編譯和優化,執行計劃也會緩存下來并允許資料庫以參數化的形式進行查詢,

PreprareStatement收到傳入的參數如or '1=1',隻會将其當成參數的形式進來處理而不會作為一個SQL指令。

(2)結果集遊标:

ResultSet對外部修改資料是否敏感:例如我們在執行select id,name ,age from user where age>32 limit 10時,将從資料庫查詢到的結果緩存到JVM中,正在周遊取資料時,這時候資料庫的記錄發生了更新。更新的資料,ResultSet是否可見。

遊标周遊:支援向前、向後、最後一條,第一條。随機等都是在取資料時的api操作

ResultSet.TYPE_FORWARD_ONLY(預設)

   僅僅支援向前forward

ResultSet.TYPE_SCROLL_INSENSITIVE

   支援backforward,random,last,first操作,對外部資料修改不可見。其實也比較好了解。當我們将資料庫符合條件的完整資料查詢緩存到ResultSet中時,對結果集周遊等相關操作,隻是基于記憶體中資料操作而已。跟資料庫無關。是以資料庫的值更新它是不知道的。

ResultSet.TYPE_SCROLL_SENSITIVE

   支援backforward,random,last,first操作。對外部資料修改是可見。這裡與TYPE_SCROLL_INSENSITIVE不同的是,select id,name ,age from user where age>32 limit 10 sql語句用TYPE_SCROLL_SENSITIVE的Statement來執行,會轉化

select id from user where age>32 limit 10的結果先緩存起來。而真正在取資料的時候再通過select id from user where id=?

去資料庫實時查詢資料。是以對資料庫的修改是可見的(隻是修改,添加還是不可見。因為添加的id,第一次并未緩存起來)。

(3)批處理:

可以向資料庫發送多條不同的SQL語句。

String sql1 = "insert into user_weixin(id,openid) values(?,?)";
      preparedStatement = connection.prepareStatement(sql);
      for(int i=1;i<1000;i++){
        preparedStatement.setInt(1, i);
        preparedStatement.setString(2, "openid" + i);
        preparedStatement.addBatch();
        if(i%100==0){
          preparedStatement.executeBatch();
          preparedStatement.clearBatch();
        }
      }
      preparedStatement.executeBatch();
           

JDBC小結: 

  1. 資料庫連接配接頻繁建立、釋放頻繁造成系統資源浪費如果使用資料庫連接配接池可解決此問題。
  2. Sql語句在代碼中寫死
  3. 對結果集解析存在寫死(查詢列名),需要手動将結果集映射到實體中(取出每列的值,set到實體)。

3、MyBatis

MyBatis再神奇也就是對JDBC程式設計的封裝,通過用 XML 或注解來配置和映射實體。 避免了 JDBC 代碼和手動設定參數以及擷取結果集。

使用MyBatis

(1)、引入依賴,在啟動配置檔案中配置資料庫連接配接資訊

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.3</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.30</version>
</dependency>
           
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
           

(2)、建立MyBatis配置檔案:mybatis-configuration.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>

    <!-- 加載資料庫屬性檔案 -->
    <properties resource="jdbc.properties">
    </properties>
    <!-- 可以配置多個運作環境,但是每個 SqlSessionFactory 執行個體隻能選擇一個運作環境 一、development:開發模式 二、work:工作模式 -->
    <environments default="development">
        <!--id屬性必須和上面的default一樣 -->
        <environment id="development">
            <transactionManager type="JDBC" />
            <!--dataSource 元素使用标準的 JDBC 資料源接口來配置 JDBC 連接配接對象源 -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}" />
                <property name="url" value="${jdbc.url}" />
                <property name="username" value="${jdbc.username}" />
                <property name="password" value="${jdbc.password}" />
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="com/ys/mapper/userMapper.xml"/>
    </mappers>
</configuration>
           

(3)配置映射器(接口和xml映射檔案)

public interface UserMapper {
  User selectUser();
}
           
<mapper namespace="org.apache.test.UserMapper">
    <select id="selectUser" resultType="org.apache.test.User">
    select name from user where id = #{id}
  </select>
</mapper>
           

(4)使用MyBatis查詢資料庫,并将結果集映射到實體中

public static void main(String[] args) throws Exception {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory =
      new SqlSessionFactoryBuilder().build(inputStream);

    //通過sqlSessionFactory擷取SqlSession 
    SqlSession session = sqlSessionFactory.openSession() {
     //方式一:
      UserMapper mapper = session.getMapper(UserMapper.class);
      User user1 = mapper.selectBlog("1");
 
      //方式二:
      User user2 = session.selectOne(
        "test.UserMapper.selectBlog", 1);
    
  }
           

PS:後面就從源碼分析上面的代碼是如何對JDBC進行封裝的