Mybatis是一个持久层框架,通过对底层的封装,使得我们通过一些配置以及SQL语句就可以很容易就可以对数据库进行操作。
下面就是Mybatis的一个HelloWorld的使用(基于Mybatis 3.4.6版本):
下面是一个java bean对象,一个实体类Student
public class Student {
private Integer id;
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "student [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
IStudentDao接口
public interface IStudentDao {
//按id查询
Student selById(int id);
}
Mapper.xml配置
<mapper namespace="cn.xiongyu.dao.IStudentDao">
<select id="selById" resultType="Student">
select * from stu where id=#{id}
</select>
</mapper>
主配置文件Mybatis.xml配置信息
<configuration>
<properties resource="jdbc_mysql.properties"></properties>
<typeAliases>
<package name="cn.xiongyu.bean"/>
</typeAliases>
<environments default="testEM">
<environment id="testEM">
<transactionManager type="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="cn/xiongyu/dao/mapper.xml"/>
</mappers>
</configuration>
测试类
public class MyBatisTest{
@Test
public void test01(){
//通过主配置文件的路径获取主配置文件的字节流,实际上是一个BufferedInputStream
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
//获取SqlSessionFactory,用于得到SqlSession对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//通过SqlSession对象获取代理mapper,用于实现IStudentDao接口中的方法
IStudentDao studentDao = sqlSession.getMapper(IStudentDao.class);
//具体的查询语句
Student stu = studentDao.selById(10);
sqlSession.close();
}
}
首选通过SqlSessionFactoryBuilder来构建SqlSessionFactory对象,传入的是一个mybatis.xml配置文件流,下面看看SqlSessionFactoryBuilder源码:
public class SqlSessionFactoryBuilder {
//通过调用该build方法,实际调用的是下面的重载方法
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
//实际构造SqlSessionFactory 的代码
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//对Mybatis.xml进行解析得到XMLConfigBuilder 对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//真正对主配置文件进行解析是parser.parse()操作,该源码部分如下。
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
XMLConfigBuilder 的parse()方法
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//实际调用下面方法进行解析XML
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
//可以知道主配置文件中只能配置下面节点,下面只介绍了一下主要用到的
private void parseConfiguration(XNode root) {
try {
//解析properties属性文件节点
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
//解析typeAliases节点,设置别名,实际上底层是一个HashMap用于映射别名
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
//环境节点,可以配置多个环境
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//类型处理节点,就是用于映射jdbc_type与java类型
typeHandlerElement(root.evalNode("typeHandlers"));
//用于解析mapper节点,后面用于解析mapper文件
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
以上解析的结果都放于Configuration对象内,Configuration就像一个大仓库,放有所有的配置信息,解析出Configuration之后,就调用SqlSessionFactory build(Configuration config)方法,返回一个DefaultSqlSessionFactory对象。
得到了SqlSessionFactory 之后调用openSession()方法得到SqlSession对象。
下面是DefaultSqlSessionFactory部分源代码
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
//直接调用openSessionFromDataSource方法,事务设置手动提交
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
//调用该方法返回SqlSession对象
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//获取环境对象
final Environment environment = configuration.getEnvironment();
//获取TransactionFactory ,用于生产Transaction对象,默认是JdbcTransaction
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//得到事务对象,默认实现是JdbcTransaction,隔离级别采用默认的
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//获取Executor 对象,用于实际执行SQL语句
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();
}
}
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
return environment.getTransactionFactory();
}
}
得到SqlSession对象之后,然后就可以获取Mapper动态代理对象,用于调用接口中相应的方法。
下面看看SqlSession.getMapper()方法具体实现:
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
getMapper()把具体实现抛给了Configuration,再来看Configuration类中的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
Configuration把问题抛给了MapperRegistry,再来看MapperRegistry类中的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//得到Mapper代理工厂,用于生产Mapper代理对象
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//返回Mapper对象,具体实现看下面
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
下面再看MapperProxyFactory类
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
//使用JDK动态代理生成代理对象
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}
//调用该方法,实际执行的是上面方法
public T newInstance(SqlSession sqlSession) {
//生成MapperProxy对象
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
下面看看MapperProxy对象部分实现:
//实现了InvocationHandler对象
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@Override
//通过代理调用Dao接口方法后,会执行该方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//该方法会创建MapperMethod 对象
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
下面是MapperMethod 类中的execute方法的实现
//可以看到该方法的执行是首先判断是执行CURD哪种操作,绕后就调用SqlSession中具体的方法执行
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
接下来看SqlSession中的某个查询方法
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
//最终交给executor执行
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
经过一层层的调用,会来到SimpleExecutor的doQuery方法
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
//StatementHandler封装了Statement, 让 StatementHandler 去处理
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
看到这里,mybatis的最基本的流程应该大致了解了。