mybatis是对 jdbc 的封装。
如果想要知道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进行数据库的操作。
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>
以上Java代码位于 UnpooledDataSource.java 中。
2、对数据库连接的封装和优化
mybatis对数据库 Connection进行了封装和优化,提供带连接池 PooledDataSource 和 不带连接池的数据源 UnpooledDataSource。
Connection、UnpooledDataSource和PooledDataSource三者的关系如下图:
UnpooledDataSource 负责加载jdbc的驱动,保存jdbc连接需要的driver、url、username、password、以及默认的事务等级和默认的是否自动提交,并创建数据库连接Connection。
通过UnpooledDataSource的 getConnection() 方法可以获得Connection实例。
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 被封装的层次:
2、mybatis 从 SqlSession 到 statment
以上就是从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