MyBatis 源代码阅读笔记 2 基于"注解"方式的代码编写
源代码工程
https://github.com/Jason-Chen-2017/source-code-reading
代码详解:
package com.light.sword;
import org.apache.ibatis.datasource.DataSourceFactory;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
import org.apache.ibatis.mapping.Environment;
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.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import javax.sql.DataSource;
import java.util.List;
import java.util.Properties;
public class MyBatisDemoAnno {
public static void main(String[] args) {
// 获取数据源
DataSourceFactory dataSourceFactory = new PooledDataSourceFactory();
Properties props = new Properties();
props.setProperty("driver", "com.mysql.cj.jdbc.Driver");
props.setProperty("url", "jdbc:mysql://127.0.0.1:3306/test");
props.setProperty("username", "root");
props.setProperty("password", "root");
dataSourceFactory.setProperties(props);
DataSource dataSource = dataSourceFactory.getDataSource();
// 事务管理
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("dev", transactionFactory, dataSource);
// 配置对象
Configuration configuration = new Configuration(environment);
// add Mapper 接口类
configuration.addMapper(ProductMapper.class);
// open SqlSession
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
SqlSession sqlSession = sqlSessionFactory.openSession();
// 从SQL会话中获取 ProductMapper
ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);
List<Product> productList = productMapper.findAll();
System.out.println(productList);
}
}
其中, ProductMapper 如下:
package com.light.sword;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface ProductMapper {
@Select("select * from product")
List<Product> findAll();
}
运行输出:
[Product{id=1, name='Product1'}, Product{id=2, name='Product2'}]
对应到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>
<typeAliases>
<typeAlias alias="Product" type="com.light.sword.Product"></typeAlias>
</typeAliases>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/ProductMapper.xml"></mapper>
</mappers>
</configuration>
mapper/ProductMapper.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">
<!-- namespace命名空间,做SQL隔离 -->
<mapper namespace="productMapper">
<select id="listAll" resultType="Product">
select *
from product
</select>
</mapper>
源码运行解析
DataSource
===CONFINGURATION==============================================
jdbcDriver com.mysql.cj.jdbc.Driver
jdbcUrl jdbc:mysql://127.0.0.1:3306/test
jdbcUsername root
jdbcPassword ************
poolMaxActiveConnections 10
poolMaxIdleConnections 5
poolMaxCheckoutTime 20000
poolTimeToWait 20000
poolPingEnabled false
poolPingQuery NO PING QUERY SET
poolPingConnectionsNotUsedFor 0
---STATUS-----------------------------------------------------
activeConnections 0
idleConnections 0
requestCount 0
averageRequestTime 0
averageCheckoutTime 0
claimedOverdue 0
averageOverdueCheckoutTime 0
hadToWait 0
averageWaitTime 0
badConnectionCount 0
===============================================================
我们使用的是
DataSourceFactory dataSourceFactory = new PooledDataSourceFactory();
public PooledDataSource() {
dataSource = new UnpooledDataSource();
}
// 获取数据源
DataSourceFactory dataSourceFactory = new PooledDataSourceFactory();
Properties props = new Properties();
props.setProperty("driver", "com.mysql.cj.jdbc.Driver");
props.setProperty("url", "jdbc:mysql://127.0.0.1:3306/test");
props.setProperty("username", "root");
props.setProperty("password", "root");
dataSourceFactory.setProperties(props);
DataSource dataSource = dataSourceFactory.getDataSource();
然后 事务管理器:
public class JdbcTransaction implements Transaction {
private static final Log log = LogFactory.getLog(JdbcTransaction.class);
protected Connection connection;
protected DataSource dataSource;
protected TransactionIsolationLevel level;
protected boolean autoCommit;
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
dataSource = ds;
level = desiredLevel;
autoCommit = desiredAutoCommit;
}
public JdbcTransaction(Connection connection) {
this.connection = connection;
}
@Override
public Connection getConnection() throws SQLException {
if (connection == null) {
openConnection();
}
return connection;
}
@Override
public void commit() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + connection + "]");
}
connection.commit();
}
}
@Override
public void rollback() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection [" + connection + "]");
}
connection.rollback();
}
}
@Override
public void close() throws SQLException {
if (connection != null) {
resetAutoCommit();
if (log.isDebugEnabled()) {
log.debug("Closing JDBC Connection [" + connection + "]");
}
connection.close();
}
}
protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
try {
if (connection.getAutoCommit() != desiredAutoCommit) {
if (log.isDebugEnabled()) {
log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]");
}
connection.setAutoCommit(desiredAutoCommit);
}
} catch (SQLException e) {
// Only a very poorly implemented driver would fail here,
// and there's not much we can do about that.
throw new TransactionException("Error configuring AutoCommit. "
+ "Your driver may not support getAutoCommit() or setAutoCommit(). "
+ "Requested setting: " + desiredAutoCommit + ". Cause: " + e, e);
}
}
protected void resetAutoCommit() {
try {
if (!connection.getAutoCommit()) {
// MyBatis does not call commit/rollback on a connection if just selects were performed.
// Some databases start transactions with select statements
// and they mandate a commit/rollback before closing the connection.
// A workaround is setting the autocommit to true before closing the connection.
// Sybase throws an exception here.
if (log.isDebugEnabled()) {
log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
}
connection.setAutoCommit(true);
}
} catch (SQLException e) {
if (log.isDebugEnabled()) {
log.debug("Error resetting autocommit to true "
+ "before closing the connection. Cause: " + e);
}
}
}
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
connection = dataSource.getConnection();
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
setDesiredAutoCommit(autoCommit);
}
@Override
public Integer getTimeout() throws SQLException {
return null;
}
}
openSession
根据 configuration 构建 SqlSessionFactory, 然后使用 SqlSessionFactory 打开session :
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
SqlSession sqlSession = sqlSessionFactory.openSession();
// 从SQL会话中获取 ProductMapper
ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);
List<Product> productList = productMapper.findAll();
通过 接口类型的 ProductMapper 创建出实例对象的代码:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
MapperProxyFactory
package org.apache.ibatis.binding;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.ibatis.session.SqlSession;
/**
* @author Lasse Voss
*/
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
网友评论