从一次查询看MyBatis对JDBC的封装
来源:互联网 发布:js \ join 区别 编辑:程序博客网 时间:2024/06/02 11:07
MyBatis是半ORM的持久层框架,把SQL的书写留给了开发人员,然后利用JDBC的API对数据库进行操作。在这里面,MyBatis对JDBC进行了封装,本文从一次查询的执行来跟踪MyBatis的执行过程,了解对JDBC封装的大体结构。
首先看传统的JDBC编码,注意其中主要的元素。
Class.forName("com.mysql.jdbc.Driver");Connection connection = DriverManager.getConnection("url","username", "password");PreparedStatement preparedStatement = connection.prepareStatement("sql");preparedStatement.setObject(1, "param");ResultSet resultSet = preparedStatement.executeQuery();List<Object> result = dealResultSet(resultSet);System.out.println(result);
JDBC中首先需要取得数据库的链接connection,然后创建预编译语句preparedStatement对象并向其注入参数,然后返回ResultSet结果集,从结果集中获取数据构造对象。
再看一下MyBatis执行一次查询的代码,然后跟踪代码分析。
InputStream is = Resources.getResourceAsStream("config/mybatis_config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);SqlSession session = sqlSessionFactory.openSession();List<Object> result = session.selectList("namespace.statement", "parameter");System.out.println(result);session.close();
MyBatis首先创建sqlSessionFactory,然后打开sqlSession,然后调用sqlSession的API对数据库进行操作并返回结果,并且是已经处理过的List,最后关闭SqlSession结束操作。
1.创建sqlSessionFactory。读取配置文件的XML信息,创建Configuration对象,并将这个对象设置给sqlSessionFactory使用,这个是MyBatis的核心,所有的行为都由Configuration对象控制。创建的过程,就是将XML对象装换成JAVA对象的过程。
2.打开sqlSession,打开DefaultSqlSessionFactory的openSession方法,默认从数据源取链接。
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, autoCommit); return new DefaultSqlSession(configuration, executor); } 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(); } }在这个方法中,从configuration取得配置文件中的environment,并再从中取得配置的事务管理器transactionFactory(本文不深入探讨),最中意的就是返回一个新的executor,这个executor用来执行相关的操作。然后将configuration, executor设置给sqlSession,打开一个sqlSession,实质上就是新建一个sqlSession对象。接下来看executor的创建过程。
public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor, autoCommit); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }创建executor的方法需要三个参数,tx, execType, autoCommit。tx事务由事务管理器产生。execType默认是SIMPLE,还有批量BATCH,可继续执行的Reuse。autoCommit是否自动提交默认为false。
SimpleExecutor继承BaseExecutor,BaseExecutor实现接口Executor。
Executor是定义了各种操作的接口,如update,query,commit,rollback,deferLoad(延时加载)等操作。
BaseExecutor对接口方法做了简单实现,并且使用了模板方法的设计模式,把具体的实现留给子类完成。
BaseExecutor的query方法:
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) throw new ExecutorException("Executor was closed."); if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } deferredLoads.clear(); // issue #601 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { clearLocalCache(); // issue #482 } } return list; }可以看到会先从缓存中取,取不到就到数据库中取。
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; localCache.putObject(key, EXECUTION_PLACEHOLDER); try { list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; }从数据库中取完后,会将结果List缓存。具体的操作使用模板方法doQuery,在子类中实现。接下来看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(this, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }在这个方法中,又出现了一个新对象StatementHandler,调用他的query方法来执行数据库的查询,参数是Statement和resultHandler。Statement就是JDBC包下的接口了,resultHandler结果处理器默认为空。具体看一下prepareStatement方法。
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection); handler.parameterize(stmt); return stmt; }Connection出现了!这个是真正与数据库的链接,getConnetion方法中取得链接是这样的:Connection connection = transaction.getConnection();从事务中获取,因为一开始的时候创建了事务管理器,新建事务的时候就已经创建了链接,而且将链接与事务管理,所以这边就可以通过当前事务取得当前的链接。
JDBC中链接取完知乎第二个对象是Statement,这边的handler.prepare()方法就是传入connection,获取Statement对象的过程。看一下PrepareStatement对象的获取过程:
protected Statement instantiateStatement(Connection connection) throws SQLException { String sql = boundSql.getSql(); if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) { String[] keyColumnNames = mappedStatement.getKeyColumns(); if (keyColumnNames == null) { return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); } else { return connection.prepareStatement(sql, keyColumnNames); } } else if (mappedStatement.getResultSetType() != null) { return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } else { return connection.prepareStatement(sql); } }connection.prepareStatement(sql);和jdbc一样,只不过已经是通过层层包装。最后调用handler.<E>query(stmt, resultHandler);来返回查询的结果。
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.<E> handleResultSets(ps); }然后通过resultSetHandler来处理结果,将返回的ResultSet处理成List对象返回。
0 0
- 从一次查询看MyBatis对JDBC的封装
- JDBC 对数据库连接的封装
- 【Mybatis】从JDBC到Mybatis的改进
- 对HGE游戏引擎的一次封装
- Mybatis的一次简单查询过程
- Spring对JDBC异常的封装
- spring 对jdbc的封装 aop
- hibernate对JDBC封装的简单分析
- Spring(三) spring对jdbc的封装
- 理解dbutils对jdbc的封装
- 从封装的角度看OO编程
- Java 封装jdbc查询
- 对jdbc进行封装
- 从jdbc到mybatis
- ibatis又名(mybatis)使用方法,针对jdbc的封装
- mybatis性能优化二之多对多查询:用一次请求解决n次请求查询
- 对Rxjava和Retrofit的一次封装使用
- 从一次需求调研会议看项目经理的能力
- hive大数据倾斜总结
- 微信公众号支付相关设置介绍
- ARM代码搬移
- hdu 2838 cow sorting(树状数组)
- POJ 1750 Dictionary
- 从一次查询看MyBatis对JDBC的封装
- c++内存对齐
- opensuse下的redmine安装
- tornado 启动处流程分析
- oracle 数据类型---日期和时间
- ECMall多用户商城系统再次冲击电商市场
- 10115
- jquery easy ui datebox 只返回年和月
- SELECT INTO 和 INSERT INTO SELECT 两种表复制语句