mybatis3.4.2 与 JDBC中操作对象映射的过程

mybatis是对 jdbc 的封装。

mybatis3.4.2 与 JDBC中操作对象映射的过程_第1张图片

如果想要知道mybatis到底做了哪些事情,必须了解jdbc如何对数据库进行操作的,以及这些操作mybatis是如何实现的。

一、JDBC 操作数据库的过程

下面代码直接使用 JDBC 进行数据库操作

1、创建数据库连接

public Connection getConnection() throws SQLException {

    Connection conn = null;
    Properties connectionProps = new Properties();
    connectionProps.put("user", this.userName);
    connectionProps.put("password", this.password);

    if (this.dbms.equals("mysql")) {
        conn = DriverManager.getConnection(
                   "jdbc:" + this.dbms + "://" +
                   this.serverName +
                   ":" + this.portNumber + "/",
                   connectionProps);
    } 
    System.out.println("Connected to database");
    return conn;
}

2、执行数据库操作

public static void viewTable(Connection con, String dbName)
    throws SQLException {

    Statement stmt = null;
    String query = "select COF_NAME, SUP_ID, PRICE, " +
                   "SALES, TOTAL " +
                   "from " + dbName + ".COFFEES";
    try {
        stmt = con.createStatement();
        ResultSet rs = stmt.executeQuery(query);
        while (rs.next()) {
            String coffeeName = rs.getString("COF_NAME");
            int supplierID = rs.getInt("SUP_ID");
            float price = rs.getFloat("PRICE");
            int sales = rs.getInt("SALES");
            int total = rs.getInt("TOTAL");
            System.out.println(coffeeName + "\t" + supplierID +
                               "\t" + price + "\t" + sales +
                               "\t" + total);
        }
    } catch (SQLException e ) {
        e.printStackTrace();
    } finally {
        if (stmt != null) { 
            stmt.close(); 
         }
    }
}

这部分包括:

1、获取 Statement 实例 stmt = con.createStatement()
2、执行查询 ResultSet rs = stmt.executeQuery(query);
3、处理查询结果 while (rs.next()) { … }
4、关闭 Statement 实例 stmt.close()

二、mybatis 3.4.2 操作数据库的过程以及与JDBC的映射

mybatis是对 JDBC API 进行二次封装的framework。
通常情况下一些 framework 和 archticture 为了性能需要,需要提供必要资源的准备和释放过程。

这个过程通常分为三个阶段:
1、启动过程。预先加载处理使用或者调用过程需要的资源,也可以叫做加载过程。
2、使用过程,也可以叫做调用过程。
3、释放过程,当程序结束释放一些必要的资源。

JDBC 中的数据库连接就属于这种调用过程使用的资源。因此,mybatis 在framework内部进行了处理,封装了JDBC的数据库操作对象,外部用户在使用mybatis时,直接使用 sqlSession进行数据库的操作。

mybatis3.4.2 与 JDBC中操作对象映射的过程_第2张图片

1、mybatis 中创建数据库连接的代码

  • 创建连接前,参数准备
  private Connection doGetConnection(String username, String password) throws SQLException {
    Properties props = new Properties();
    ...
    if (username != null) {
      props.setProperty("user", username);
    }
    if (password != null) {
      props.setProperty("password", password);
    }
    return doGetConnection(props);
  }
  • 创建连接
 private Connection doGetConnection(Properties properties) throws SQLException {
    ...
    Connection connection = DriverManager.getConnection(url, properties);
    ...
    return connection;
  }

这里面提到的url,username,password都是在 mybatis-config.xml里面设置的。

  
  
<configuration>  
    <environments default="development">  
        <environment id="development">  
            <transactionManager type="JDBC"/>  
            <dataSource type="POOLED">  
                <property name="driver" value="${driver}"/>  
                <property name="url" value="${url}"/>  
                <property name="username" value="${username}"/>  
                <property name="password" value="${password}"/>  
            dataSource>  
        environment>  
    environments>  
    <mappers>  
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>  
    mappers>  
configuration> 
  • 参数取值
    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/test
    username=test
    password=test

以上Java代码位于 UnpooledDataSource.java 中。

2、对数据库连接的封装和优化

mybatis对数据库 Connection进行了封装和优化,提供带连接池 PooledDataSource 和 不带连接池的数据源 UnpooledDataSource。

Connection、UnpooledDataSource和PooledDataSource三者的关系如下图:

mybatis3.4.2 与 JDBC中操作对象映射的过程_第3张图片

  • UnpooledDataSource

UnpooledDataSource 负责加载jdbc的驱动,保存jdbc连接需要的driver、url、username、password、以及默认的事务等级和默认的是否自动提交,并创建数据库连接Connection。

通过UnpooledDataSource的 getConnection() 方法可以获得Connection实例。

  • PooledDataSource

PooledDataSource封装了UnpooledDataSource,在UnpooledDataSource基础上增加了连接池管理 PoolState 。

PooledDataSource 的 getConnection() 方法与UnpooledDataSource差别很大:

 public Connection getConnection() throws SQLException {
    return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
  }

简单的说,popConnection()方法在初始阶段创建了一个PooledConnection实例。

PooledConnection conn = new PooledConnection(dataSource.getConnection(), this);

通常 pop 是出栈操作,也就是从PooledDataSource连接池中弹出了一个PooledConnection(对应pop操作的是 push操作-进栈)。

PooledConnection封装了 Connection实例 和 PooledDataSource实例,并提供了一个 Connection 的代理 proxyConnection。

通过 PooledDataSource 的 getConnection() 方法获得的是 PooledConnection 内部提供的 proxyConnection。

PooledConnection 在连接关闭的时候,调用PooledDataSource 实例的 pushConnection() 方法入栈。

根据 mybatis 3.4.2 DataSourceFactory与DataSource 第四节、DataSource的配置分析,通过在mybatis-config.xml中对

    dataSource type="POOLED"

进行设置。

<environments default="development">
    <environment id="development">
       <transactionManager type="JDBC" />
       <dataSource type="POOLED">
            <property name="driver" value="${db.driver}" />
            <property name="url" value="${db.url}" />
            <property name="username" value="${db.username}" />
            <property name="password" value="${db.password}" />
            <property name="poolPingQuery" value="SELECT NOW()" />
            <property name="poolPingEnabled" value="true" />
      dataSource>
   environment>
environments>

根据xml的设置,在构造DefaultSqlSession的时候,

environment.getDataSource()

获得就是 PooledDataSource 的实例。

PooledDataSource实例被事务封装,并根据此事务的实例创建了Executor实例。

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

总结:

1、数据库连接 Connection 被封装的层次:

Created with Raphaël 2.1.0 开始 Connection UnpooledDataSource封装Connection PooledDataSource封装UnpooledDataSource Transaction封装PooledDataSource Executor封装Transaction DefaultSqlSession封装Executor 结束

2、mybatis 从 SqlSession 到 statment

Created with Raphaël 2.1.0 开始 selectList(...)@DefaultSqlSession query(...)@Executor doQuery(...)@SimpleExecutor-->Executor prepareStatement(...)@SimpleExecutor-->BaseExecutor getConnection(...)@BaseExecutor getConnection()@JdbcTransaction-->Transaction openConnection()@JdbcTransaction-->Transaction getConnection()@PooledDataSource-->DataSource popConnection()@PooledDataSource-->DataSource getConnection()@UnpooledDataSource doGetConnection(...)@UnpooledDataSource Connection connection = DriverManager.getConnection(url, properties); 结束

以上就是从JDBC API 的Connection封装到 mybatis SqlSession,又从mybatis SqlSession调用到 JDBC API Connection 的过程。

其他相关文章:
1、 http://blog.csdn.net/luanlouis/article/details/37671851
2、http://www.cnblogs.com/timlearn/p/4161567.html
3、http://www.cnblogs.com/fangjian0423/p/mybatis-cache.html

你可能感兴趣的:(MyBatis)