`
zddava
  • 浏览: 243032 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Derby源代码分析 -- JDBC实现(三)

阅读更多
在开始下面的分析前,先补一下课,插播下Derby的代码结构,

Derby主要是分四个部分来组织代码的:JDBC, SQL, Store and Services。

JDBC层处于最顶端,是与应用程序进行交互的部分。JDBC的下面是SQL层,SQL层主要是负责编译和执行这两种工作,生成SQL执行计划并返回执行结果。再向下的一层是Store层,主要负责访问数据和存储。最后是Service,顾名思义,就是一些服务的module了。这个的原文在http://db.apache.org/derby/papers/derby_arch.html,可以自己去了解下。

所以,我们这部分的解析主要是了解JDBC层的东西,里边也会涉及一些SQL层的代码。SQL层的类很多会和JDBC层有同一个名字,比如Statement,不过是Derby的实现罢了。


大致了解了Derby的代码结构之后,我们就来继续前面的分析,首先来看一下Database的创建,就是EmbedConnection的createDatabase()方法。


	private Database createDatabase(String dbname, Properties info) throws SQLException {

		info = filterProperties(info);

		try {
			// 创建持久化服务
			if (Monitor.createPersistentService(Property.DATABASE_MODULE, dbname, info) == null) {
				addWarning(SQLWarningFactory.newSQLWarning(SQLState.DATABASE_EXISTS, dbname));
			}
		} catch (StandardException mse) {
			throw Util.seeNextException(SQLState.CREATE_DATABASE_FAILED, new Object[] { dbname }, handleException(mse));
		}

		info.clear();

		return (Database) Monitor.findService(Property.DATABASE_MODULE, dbname);
	}



接着是Monitor的#createPersistentService()


	public static Object createPersistentService(String factoryInterface, String serviceName, Properties properties)
			throws StandardException {

		if (SanityManager.DEBUG) {
			SanityManager.ASSERT(factoryInterface != null, "serviceName is null");
			SanityManager.ASSERT(serviceName != null, "serviceName is null");
		}
		// 这里的monitor对应的是BaseMonitor的实例
		return monitor.createPersistentService(factoryInterface, serviceName, properties);
	}



最后是BaseMonitor的#createPersistentService()


	public Object createPersistentService(String factoryInterface, String name, Properties properties)
			throws StandardException {

		PersistentService provider = findProviderForCreate(name);
		if (provider == null) {
			throw StandardException.newException(SQLState.PROTOCOL_UNKNOWN, name);
		}
		// 这里就要启动名字是"name"的database模块了,这里的"name"是在数据库连接URL中写明的
		return bootService(provider, factoryInterface, name, properties, true);
	}



如果你还记得我前面服务器启动部分的分析,这里的bootService()方法会启动一个Module,名字是"name"对应的字符串,在这里就是客户端请求的数据库名。
而factoryInterface是"org.apache.derby.database.Database"对应的实现,可以在modules.properties中找到它的默认实现类是org.apache.derby.impl.db.BasicDatabase。这里还有一个org.apache.derby.impl.db.SlaveDatabase的实现,它是用于replication slave模式的,现在先不考虑。


启动过程中,要调用BasicDatabase的#boot()方法,boot方法主要是一系列服务的启动,这里就不列出代码了。


要了解的第二个是lcc(lcc属于SQL层)实例的获取,就是tr.startTransaction();这步调用,


	void startTransaction() throws StandardException, SQLException {
		lcc = database.setupConnection(cm, username, drdaID, dbname);
	}



这里和前面说的database是有关系的,来看一下BasicDatabase的实现


	public LanguageConnectionContext setupConnection(ContextManager cm, String user, String drdaID, String dbname)
			throws StandardException {

		TransactionController tc = getConnectionTransaction(cm);

		cm.setLocaleFinder(this);
		
		// push DatabaseContext到cm
		pushDbContext(cm);

		// 返回一个GenericLanguageConnectionContext实例,并且push到cm
		LanguageConnectionContext lctx = lcf.newLanguageConnectionContext(cm, tc, lf, this, user, drdaID, dbname);

		// push ClassFactory到cm
		pushClassFactoryContext(cm, lcf.getClassFactory());

		ExecutionFactory ef = lcf.getExecutionFactory();

		// push ExecutionContext到cm
		ef.newExecutionContext(cm);

		lctx.initialize();

		lctx.internalCommitNoSync(TransactionController.RELEASE_LOCKS | TransactionController.READONLY_TRANSACTION_INITIALIZATION);

		return lctx;

	}



这样lcc就初始化完成了。


这里简单的总结一下,EmbedConnection的构造函数是做了很多东西的,首先是Database模块的启动(如果已经启动返回模块引用),然后是创建JDBC层与SQL层的接口lcc,实现了层之间的互联。


下面还是回到EmbedStatement的execute()方法来继续分析下ResultSet的获取,


		/* 获取Activation对象 */
		Activation activation;
		try {
			PreparedStatement preparedStatement = lcc.prepareInternalStatement(lcc.getDefaultSchema(), sql,
					resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY, false);
			activation = preparedStatement.getActivation(lcc,
					resultSetType == java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE);
			checkRequiresCallableStatement(activation);
		} catch (Throwable t) {
			throw handleException(t);
		}

		activation.setSingleExecution();

		if (autoGeneratedKeys == Statement.RETURN_GENERATED_KEYS)
			activation.setAutoGeneratedKeysResultsetInfo(columnIndexes, columnNames);

		/* 执行Statement */
		return executeStatement(activation, executeQuery, executeUpdate);



这里lcc已经知道了是GenericLanguageConnectionContext的实例了,这个类是属于SQL层的。根据GenericLanguageConnectionContext的JavaDoc,LanguageConnectionContext维护了一个PreparedStatement、activation和cursor的实例池,这里的PreparedStatement是Derby SQL层的实现,并不是JDBC的。


暂时这里的Activation和PreparedStatement的获得就不再进一步讨论了,因为我查看源代码时发现里边涉及到了太多的SQL层细节,这里就先不去深究了。还是把目光转回JDBC层,看一下executeStatement(activation, executeQuery, executeUpdate);这句


	boolean executeStatement(Activation a, boolean executeQuery, boolean executeUpdate) throws SQLException {
		synchronized (getConnectionSynchronization()) {

			if (SanityManager.DEBUG) {
				SanityManager.ASSERT(results == null);
				SanityManager.ASSERT(dynamicResults == null);
				SanityManager.ASSERT(autoGeneratedKeysResultSet == null);
			}
			
			// 确保创建上下文
			setupContextStack();
			boolean retval;

			pvs = a.getParameterValueSet();

			try {
				// 清掉警告
				clearWarnings();

				if (!forMetaData) {
					// 如果需要的话,提交上次的Statement
					// 这里要autoCommit和needCommit都为true
					commitIfNeeded();
					// 把needCommit置为true
					needCommit();
				} else {

					if (lcc.getActivationCount() > 1) {
					} else {
						commitIfNeeded(); // we can legitimately commit
						needCommit();
					}
				}

				// 返回SQL层的PreparedStatement
				PreparedStatement ps = a.getPreparedStatement();
				ps.rePrepare(lcc);
				// 加入编译警告
				addWarning(ps.getCompileTimeWarnings());

				// 设定Cursor,看JavaDoc说的是JDBC要求把select statement转变成Cursor,具体我也不是很明白
				if (cursorName != null) {
					a.setCursorName(cursorName);
				}
				
				// 设定结果集持久性,就是当事务提交的之后是否ResultSet还要继续打开
				boolean executeHoldable = getExecuteHoldable();

				a.setResultSetHoldability(executeHoldable);

				a.reset();
				a.setMaxRows(maxRows);
				// 这里的ResultSet也是SQL层的实现,不是JDBC的,这里的#execute()方法很重要,不过JDBC层就不讨论了
				ResultSet resultsToWrap = ps.execute(a, timeoutMillis);
				addWarning(a.getWarnings());

				if (resultsToWrap.returnsRows()) {// 是否返回结果
					if (executeUpdate) {
						throw StandardException.newException(SQLState.LANG_INVALID_CALL_TO_EXECUTE_UPDATE);
					}
					
					// 生成JDBC的ResultSet
					EmbedResultSet lresults = factory.newEmbedResultSet(getEmbedConnection(), resultsToWrap,
							forMetaData, this, ps.isAtomic());
					results = lresults;

					if (a.isSingleExecution())
						lresults.singleUseActivation = a;

					updateCount = -1;
					retval = true;
				} else {// 不用返回结果
					if (a.getAutoGeneratedKeysResultsetMode()
							&& (resultsToWrap.getAutoGeneratedKeysResultset() != null)) {
						resultsToWrap.getAutoGeneratedKeysResultset().open();
						autoGeneratedKeysResultSet = factory.newEmbedResultSet(getEmbedConnection(), resultsToWrap
								.getAutoGeneratedKeysResultset(), false, this, ps.isAtomic());
					}
					
					// 更新的行数
					updateCount = resultsToWrap.modifiedRowCount();
					results = null; // note that we have none.

					int dynamicResultCount = 0;
					if (a.getDynamicResults() != null) {
						dynamicResultCount = processDynamicResults(a.getDynamicResults(), a.getMaxDynamicResults());
					}

					resultsToWrap.close(); // Don't need the result set any more

					if (executeQuery && dynamicResultCount != 1) {
						throw StandardException.newException(SQLState.LANG_INVALID_CALL_TO_EXECUTE_QUERY);
					}

					if (executeUpdate && dynamicResultCount > 0) {
						throw StandardException.newException(SQLState.LANG_INVALID_CALL_TO_EXECUTE_UPDATE);
					}

					if (dynamicResultCount == 0) {
						if (a.isSingleExecution()) {
							a.close();
						}

						if (!forMetaData)
							commitIfNeeded();
						else {
							if (lcc.getActivationCount() > 1) {
							} else {
								commitIfNeeded(); // we can legitimately commit
							}
						}
					}

					retval = (dynamicResultCount > 0);
				}
			} catch (Throwable t) {
				if (a.isSingleExecution()) {
					try {
						a.close();
					} catch (Throwable tt) {
						;
					}
				}
				throw handleException(t);
			} finally {
				restoreContextStack();
			}
			return retval;
		}
	}



这样,大致的SQL运行过程在JDBC层的运行流程就大致讲完了。后边才是更深入的方面,就是SQL运行在SQL层是如何实现的。
分享到:
评论

相关推荐

    各种数据库的jdbc(mysql,orcal ,derby,hive,postgresql,monetdb-jdbc)

    JDBC提供了一种统一的接口,使得开发者可以轻松地切换不同的数据库系统,提高了代码的可移植性。同时,它还支持连接池、数据源等高级特性,以提升性能和管理效率。在实际开发中,理解并熟练使用这些数据库的JDBC特性...

    db-derby-10.11.1.1-src.zip

    "db-derby-10.11.1.1-src.zip" 是Apache Derby的10.11.1.1版本的源代码包,对于开发者来说,这是一个宝贵的资源,可以深入了解Derby的内部工作原理和实现细节。 Apache Derby的核心特性包括: 1. **完全用Java编写...

    db-derby-10.11.1.1-bin.zip

    Apache Derby,也被称为Java DB,是一款轻量级、开源的关系型数据库管理系统,完全用Java编写,遵循Apache软件基金会的开放源代码协议。这个名为"db-derby-10.11.1.1-bin.zip"的压缩包包含了Apache Derby 10.11.1.1...

    jdbc接连数据库:oracle/derby/mysql

    博客链接提到的"源码"可能是指查看或分析JDBC驱动的源代码,理解其内部工作原理,而"工具"可能是指使用一些辅助工具,如数据库管理工具(如SQL Developer或MySQL Workbench),或者开发工具(如IDEA的数据库插件)来...

    spring-jdbc-4.2.xsd.zip

    总之,`spring-jdbc-4.2.xsd`是Spring JDBC模块配置的核心,它为Spring JDBC的配置提供了一套规范,使得开发者可以清晰、有序地设置数据库连接、数据源、事务管理等相关属性,提高了代码的可读性和可维护性。...

    Eclipse下Apache Derby开发

    在开发Derby应用时,JDT用于编写和管理Java源代码,创建JDBC客户端应用程序,这些应用程序将与Derby数据库进行交互。 DB2 plug-ins for Eclipse是IBM提供的扩展,它增强了Eclipse对多种数据库(包括Apache Derby)...

    derby,jtds,mysql,oracle,sql2000,sql2005的jdbc数据库驱动

    2. **jTDS**:jTDS是一个开放源代码的JDBC驱动,主要针对Microsoft SQL Server和Sybase Adaptive Server Anywhere。它实现了JDBC Type 4驱动,能直接与数据库服务器通过TCP/IP通信,提供高速、稳定的数据访问。 3. ...

    源代码检查工具SONAR使用经验

    ### 源代码检查工具SONAR使用经验 #### Sonar概述 Sonar作为一个代码质量管理的开放平台,通过灵活的插件机制集成了多种测试工具、代码分析工具以及持续集成工具。与传统持续集成工具(如Hudson/Jenkins)相比,...

    Derby 命令(转载)

    在标签"源码"提及的情况下,对于开发者来说,Apache Derby的源代码是开放的,这意味着你可以查看、学习甚至贡献代码到项目中。这为理解数据库内部工作原理,调试问题,或者定制特定需求提供了可能性。 至于"工具...

    Apache Derby 10.10版手册集

    1. 开源:Apache Derby遵循Apache License 2.0协议,允许用户自由地使用和分发,并可以根据自己的需求对源代码进行修改。 2. 嵌入式模式:Derby可以作为应用程序的一部分嵌入到应用中去运行,无需单独的服务器进程。...

    derby辅助工具SQuirreL SQL Client的使用

    - **方法一:使用内置Derby插件**:Eclipse有内建的Derby支持,可以通过Window > Preferences > Database Development > Drivers添加Derby驱动,然后创建数据源。 - **方法二:通过Eclipse Marketplace安装插件**...

    JdbcDerby:通过jdbc与derby连接

    - `src`目录:Java源代码文件,包含上述示例中的类。 - `lib`目录(可选):可能包含Apache Derby的JDBC驱动库文件(如`derby.jar`)。 - `build.xml`或`pom.xml`:构建脚本,可能是Ant或Maven,用于编译和打包项目...

    apache-hive-1.2.2-src:蜂巢源代码学习-apache source code

    在深入学习`apache-hive-1.2.2-src`源代码之前,我们首先需要了解Hive的基本概念和工作流程。 Hive的核心组件包括: 1. **元数据存储**:Hive存储关于表、列、分区等元数据,通常在MySQL或Derby数据库中。 2. **...

    MyEclipse Derby是什么.txt

    6. **开放源代码:**作为ASF的一个项目,Derby的源代码完全开放,这有助于开发者深入理解其内部机制,并根据自身需求进行定制和扩展。 #### 五、Derby的应用场景 由于其轻量级和易于集成的特点,Derby广泛应用于...

    Swing+derby仿QQ聊天软件及源码

    开发者可以查看这些源代码,学习如何利用Swing组件构建用户界面,以及如何通过Derby进行数据存储和检索。源码通常包括了类文件、资源文件以及可能的配置文件。通过阅读源码,初学者可以了解实际项目中Swing和Derby的...

    销售信息管理系统v0.3带源代码.zip

    源代码的提供意味着用户可以查看、学习甚至修改系统的内部实现,以适应特定需求或进行二次开发。 Swing是Java的一个图形用户界面(GUI)工具包,它是Java Foundation Classes (JFC)的一部分,用于构建桌面应用。...

    JavaSwing+derby通讯录源码

    5. **src目录**: 源码文件通常存放在src目录下,包括所有Java源代码文件(以.java为扩展名)。这里的源码应该是实现通讯录功能的类,比如用于显示和管理联系人的控制器类,以及可能的模型类和视图类。开发者可能会...

    oracle jdbc mysql 等数据连接所有的配置

    在Java应用程序中通过JDBC(Java Database Connectivity)连接到Oracle数据库时,主要使用以下代码: ```java Class.forName("oracle.jdbc.driver.OracleDriver"); Connection con = DriverManager.getConnection(...

    常用的JDBC驱动名字

    1. **基本JDBC-ODBC桥接驱动**:这是一种基于ODBC的驱动,它不是纯Java实现的。 2. **部分Java驱动**:这种驱动需要本地代码与Java交互,提供了一些Java功能,但仍依赖于ODBC。 3. **网络协议纯Java驱动**:这类驱动...

Global site tag (gtag.js) - Google Analytics