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

Derby源代码分析 -- SQL层实现(一) -- LanguageConnectionContext

阅读更多
前段时间事情太多,一直没有继续看Derby的源代码。国庆过去了,闲暇的时间多了些了,终于可以接着把这个系列写完了。不过也过了很长时间了,自己前面看的东西都忘的差不多了...... 慢慢找回来吧!

前面的文章提到了Derby的JDBC层,不敢说仔细的读,也就是大概的过了一遍,下面的文章主要是讲的SQL层,前面的JDBC实现(二)这篇文章中提到了JDBC层的sql语句执行的代码,其中在执行SQL的过程中,涉及到了Activation的获取,见org.apache.derby.impl.jdbc.EmbedStatement#execute(String, boolean, boolean, int, int[], String[]):


	private boolean execute(String sql, boolean executeQuery, boolean executeUpdate, int autoGeneratedKeys,
			int[] columnIndexes, String[] columnNames) throws SQLException {
		synchronized (getConnectionSynchronization()) {

			checkExecStatus();
			if (sql == null) {
				throw newSQLException(SQLState.NULL_SQL_TEXT);
			}
			checkIfInMiddleOfBatch();

			/* 关闭与这个Statement关联的ResultSet */
			clearResultSets();

			setupContextStack();

			SQLText = sql;

			try {
				/* 获取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);
			} finally {
				restoreContextStack();
			}
		}
	}



注意在try/catch中的语句,这里就是JDBC层与SQL层进行的一次“亲密接触”,这里首当其冲应该了解的就是这个“lcc”,它是LanguageConnectionContext的对象,在前面的文章JDBC(三)中提到了LanguageConnectionContext的初始化,是在BasicDatabase#setupConnection()中的:


	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;

	}


这里,LanguageConnectionFactory的newLanguageConnectionContext()方法返回了一个LanguageConnectionContext对象。根据默认配置,LanguageConnectionFactory的实现类是GenericLanguageConnectionFactory,它的newLanguageConnectionContext()方法返回了一个GenericLanguageConnectionContext对象作为LanguageConnectionContext的默认实现。

获得了实例之后,紧接着调用了LanguageConnectionContext的initialize()和internalCommitNoSync()两个方法,主要了解下initialize()这个方法的细节


	public void initialize() throws StandardException {
		// 创建此用户的授权类,IdUtil.getUserAuthorizationId()应该是对某些用户名进行特殊处理,例如加上双引号的
		authorizer = new GenericAuthorizer(IdUtil.getUserAuthorizationId(userName), this);

		if (SanityManager.DEBUG) {
			if (getAuthorizationId() == null) {
				SanityManager.THROWASSERT("User name is null," + " check the connection manager to make sure it is set"
						+ " reasonably");
			}
		}

		// 设定默认的Schema
		setDefaultSchema(initDefaultSchemaDescriptor());
	}



由于Schema作为数据字典中的顶层元素,所以这里还是要了解Schema的具体初始化过程的。


	protected SchemaDescriptor initDefaultSchemaDescriptor() throws StandardException {
		// 如果数据库支持Schema,并且与用户名同名的Schema存在,那么就设定这个Schema为用户的默认Schema,否则使用默认的"APP" Schema
		if (cachedInitialDefaultSchemaDescr == null) {
			// 数据字典,通过查看modules.properties,它的默认实现是org.apache.derby.impl.sql.catalog.DataDictionaryImpl
			DataDictionary dd = getDataDictionary();
			// authorizationId其实就是用户名
			String authorizationId = getAuthorizationId();
			// 先尝试获取与用户名同名的Schema描述
			SchemaDescriptor sd = dd.getSchemaDescriptor(authorizationId, getTransactionCompile(), false);

			if (sd == null) {
				// 返回一个默认的UUID是null的Schema
				sd = new SchemaDescriptor(dd, authorizationId, authorizationId, (UUID) null, false);
			}

			cachedInitialDefaultSchemaDescr = sd;
		}
		return cachedInitialDefaultSchemaDescr;
	}



这里DataDictionary的实现类其实是DataDictionaryImpl(同样在modules.properties中可以找到),下面就来看看它的getSchemaDescriptor()方法:


	public SchemaDescriptor getSchemaDescriptor(String schemaName, TransactionController tc, boolean raiseError)
			throws StandardException {
		if (tc == null) {
			tc = getTransactionCompile();
		}

		// 先从系统Schema中寻找,然后是IBM Schema
		if (getSystemSchemaDescriptor().getSchemaName().equals(schemaName)) {
			return getSystemSchemaDescriptor();
		} else if (getSysIBMSchemaDescriptor().getSchemaName().equals(schemaName)) {
			if (dictionaryVersion.checkVersion(DataDictionary.DD_VERSION_CS_5_2, null)) {
				return getSysIBMSchemaDescriptor();
			}
		}

		// 在sys.SYSSCHEMAS中查找名称为schemaname的记录
		SchemaDescriptor sd = locateSchemaRow(schemaName, tc);

		// 如果Schema叫SESSION,那么是与临时表相关的操作,创建一个在内存中保存的Schema描述
		if (sd == null && getDeclaredGlobalTemporaryTablesSchemaDescriptor().getSchemaName().equals(schemaName)) {
			return getDeclaredGlobalTemporaryTablesSchemaDescriptor();
		}

		if (sd == null && raiseError) {
			throw StandardException.newException(SQLState.LANG_SCHEMA_DOES_NOT_EXIST, schemaName);
		} else {
			return sd;
		}
	}



在这个方法中,先不去深入考虑细节的问题,因为那时Store层的工作。

LCC在默认Schema设定完毕后就完成了自身的初始化,还是回到前面的execute()方法,看一下LCC是怎么返回PreparedStatement的:


	public PreparedStatement prepareInternalStatement(SchemaDescriptor compilationSchema, String sqlText,
			boolean isForReadOnly, boolean forMetaData) throws StandardException {
		if (forMetaData) {// 要保证对MetaData的获取始终在SYS Schema中进行
			compilationSchema = getDataDictionary().getSystemSchemaDescriptor();
		}
		return connFactory.getStatement(compilationSchema, sqlText, isForReadOnly).prepare(this, forMetaData);
	}



这个方法最后通过LanguageConnectionFactory返回一个Statement,然后再通过Statement的prepare()方法来返回PreparedStatement。

这里的getStatement()会每次new一个GenericStatement实例,下面还是主要看一下它的prepare()方法吧:


	public PreparedStatement prepare(LanguageConnectionContext lcc, boolean forMetaData) throws StandardException {
		return prepMinion(lcc, true, (Object[]) null, (SchemaDescriptor) null, forMetaData);
	}



内部只有一条调用,这个prepMinion()方法是最终的实现,方法比较长:


	private PreparedStatement prepMinion(LanguageConnectionContext lcc, boolean cacheMe, Object[] paramDefaults,
			SchemaDescriptor spsSchema, boolean internalSQL) throws StandardException {
		long beginTime = 0;
		long parseTime = 0;
		long bindTime = 0;
		long optimizeTime = 0;
		long generateTime = 0;
		Timestamp beginTimestamp = null;
		Timestamp endTimestamp = null;
		StatementContext statementContext = null;

		// 是不是已经有了一个新的preparedStmt
		// 疑问:这个对于每个查询都使用一个新的GeneriStatement的情况是否有必要?
		if (preparedStmt != null) {
			if (preparedStmt.upToDate())// preparedStmt是不是最新的
				return preparedStmt;
		}

		if (lcc.getOptimizerTrace())
			lcc.setOptimizerTraceOutput(getSource() + "\n");

		beginTime = getCurrentTimeMillis(lcc);

		if (beginTime != 0) {
			beginTimestamp = new Timestamp(beginTime);
		}

		// 隔离级别
		prepareIsolationLevel = lcc.getPrepareIsolationLevel();

		// preparedStmt的初始化,
		boolean foundInCache = false;
		if (preparedStmt == null) {
			if (cacheMe)// 如果设置缓存了PreparedStament
				// 在缓存中获取PreparedStament
				preparedStmt = (GenericPreparedStatement) ((GenericLanguageConnectionContext) lcc)
						.lookupStatement(this);

			if (preparedStmt == null) {
				preparedStmt = new GenericPreparedStatement(this);
			} else {
				foundInCache = true;
			}
		}

		// 如果其他用户也在使用这个preparedStmt,那么不允许对方编译,直到自己编译完毕
		synchronized (preparedStmt) {
			for (;;) {

				if (foundInCache) {
					if (preparedStmt.referencesSessionSchema()) {// 如果引用了SESSION Schema
						foundInCache = false;
						preparedStmt = new GenericPreparedStatement(this);
						break;
					}
				}

				if (preparedStmt.upToDate()) {
					return preparedStmt;
				}

				if (!preparedStmt.compilingStatement) {// 如果没有被编译,退出循环
					break;
				}

				try {
					preparedStmt.wait();// 等待编译完毕,此方法最后的finally中有notify()
				} catch (InterruptedException ie) {
					throw StandardException.interrupt(ie);
				}
			}

			preparedStmt.compilingStatement = true;
			preparedStmt.setActivationClass(null);
		}

		try {

			HeaderPrintWriter istream = lcc.getLogStatementText() ? Monitor.getStream() : null;

			// 如果preparedStmt是GenericStorablePreparedStatement的实例,对nested connection的处理
			// 据我猜测,此处应该为对Java存储过程中要使用的nested connection的支持
			if (!preparedStmt.isStorable() || lcc.getStatementDepth() == 0) {
				statementContext = lcc.pushStatementContext(true, isForReadOnly, getSource(), null, false, 0L);
			}

			CompilerContext cc = lcc.pushCompilerContext(compilationSchema);

			if (prepareIsolationLevel != ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL) {// 隔离级别
				cc.setScanIsolationLevel(prepareIsolationLevel);
			}

			if (internalSQL || (spsSchema != null) && (spsSchema.isSystemSchema())
					&& (spsSchema.equals(compilationSchema))) {
				// 如果是查询metadate,那么设定任何SQL都是合法的
				cc.setReliability(CompilerContext.INTERNAL_SQL_LEGAL);
			}

			try {
				if (istream != null) {
					String xactId = lcc.getTransactionExecute().getActiveStateTxIdString();
					istream.printlnWithHeader(LanguageConnectionContext.xidStr + xactId + "), "
							+ LanguageConnectionContext.lccStr + lcc.getInstanceNumber() + "), "
							+ LanguageConnectionContext.dbnameStr + lcc.getDbname() + "), "
							+ LanguageConnectionContext.drdaStr + lcc.getDrdaID()
							+ "), Begin compiling prepared statement: " + getSource() + " :End prepared statement");
				}

				// 解析器,内部有SQL解析器
				Parser p = cc.getParser();

				cc.setCurrentDependent(preparedStmt);

				// 解析SQL文本,返回查询的树状结构(不是计算机科班,对数据库原理不太了解,这个以后还要自己研究)
				StatementNode qt = p.parseStatement(statementText, paramDefaults);

				parseTime = getCurrentTimeMillis(lcc);

				if (SanityManager.DEBUG) {
					if (SanityManager.DEBUG_ON("DumpParseTree")) {
						qt.treePrint();
					}

					if (SanityManager.DEBUG_ON("StopAfterParsing")) {
						lcc.setLastQueryTree(qt);

						throw StandardException.newException(SQLState.LANG_STOP_AFTER_PARSING);
					}
				}

				DataDictionary dataDictionary = lcc.getDataDictionary();

				int ddMode = dataDictionary == null ? 0 : dataDictionary.startReading(lcc);

				try {
					lcc.beginNestedTransaction(true);

					// 对Statement进行绑定
					qt.bindStatement();
					bindTime = getCurrentTimeMillis(lcc);

					if (SanityManager.DEBUG) {
						if (SanityManager.DEBUG_ON("DumpBindTree")) {
							qt.treePrint();
						}

						if (SanityManager.DEBUG_ON("StopAfterBinding")) {
							throw StandardException.newException(SQLState.LANG_STOP_AFTER_BINDING);
						}
					}

					if (preparedStmt.referencesSessionSchema(qt)) {
						if (foundInCache)
							((GenericLanguageConnectionContext) lcc).removeStatement(this);
					}

					// statement优化
					qt.optimizeStatement();

					optimizeTime = getCurrentTimeMillis(lcc);

					if (istream != null) {
						String xactId = lcc.getTransactionExecute().getActiveStateTxIdString();
						istream.printlnWithHeader(LanguageConnectionContext.xidStr + xactId + "), "
								+ LanguageConnectionContext.lccStr + lcc.getInstanceNumber() + "), "
								+ LanguageConnectionContext.dbnameStr + lcc.getDbname() + "), "
								+ LanguageConnectionContext.drdaStr + lcc.getDrdaID()
								+ "), End compiling prepared statement: " + getSource() + " :End prepared statement");
					}
				}

				catch (StandardException se) {
					lcc.commitNestedTransaction();

					if (istream != null) {
						String xactId = lcc.getTransactionExecute().getActiveStateTxIdString();
						istream.printlnWithHeader(LanguageConnectionContext.xidStr + xactId + "), "
								+ LanguageConnectionContext.lccStr + lcc.getInstanceNumber() + "), "
								+ LanguageConnectionContext.dbnameStr + lcc.getDbname() + "), "
								+ LanguageConnectionContext.drdaStr + lcc.getDrdaID()
								+ "), Error compiling prepared statement: " + getSource() + " :End prepared statement");
					}
					throw se;
				}

				finally {
					if (dataDictionary != null)
						dataDictionary.doneReading(ddMode, lcc);
				}

				try {
					if (SanityManager.DEBUG) {
						if (SanityManager.DEBUG_ON("DumpOptimizedTree")) {
							qt.treePrint();
						}

						if (SanityManager.DEBUG_ON("StopAfterOptimizing")) {
							throw StandardException.newException(SQLState.LANG_STOP_AFTER_OPTIMIZING);
						}
					}

					// 生成Activation Class
					GeneratedClass ac = qt.generate(preparedStmt.getByteCodeSaver());

					generateTime = getCurrentTimeMillis(lcc);

					if (generateTime != 0) {
						endTimestamp = new Timestamp(generateTime);
					}

					if (SanityManager.DEBUG) {
						if (SanityManager.DEBUG_ON("StopAfterGenerating")) {
							throw StandardException.newException(SQLState.LANG_STOP_AFTER_GENERATING);
						}
					}

					// 将编译后生成的对象设定到preparedStmt
					preparedStmt.setConstantAction(qt.makeConstantAction());
					preparedStmt.setSavedObjects(cc.getSavedObjects());
					preparedStmt.setRequiredPermissionsList(cc.getRequiredPermissionsList());
					preparedStmt.setActivationClass(ac);
					preparedStmt.setNeedsSavepoint(qt.needsSavepoint());
					preparedStmt.setCursorInfo((CursorInfo) cc.getCursorInfo());
					preparedStmt.setIsAtomic(qt.isAtomic());
					preparedStmt.setExecuteStatementNameAndSchema(qt.executeStatementName(), qt.executeSchemaName());
					preparedStmt.setSPSName(qt.getSPSName());
					preparedStmt.completeCompile(qt);
					preparedStmt.setCompileTimeWarnings(cc.getWarnings());
				} catch (StandardException e) {
					lcc.commitNestedTransaction();
					throw e;
				}

				if (lcc.getRunTimeStatisticsMode()) {
					preparedStmt.setCompileTimeMillis(parseTime - beginTime, // parse time
							bindTime - parseTime, // bind time
							optimizeTime - bindTime, // optimize time
							generateTime - optimizeTime, // generate time
							getElapsedTimeMillis(beginTime), beginTimestamp, endTimestamp);
				}

			} finally { 
				lcc.popCompilerContext(cc);
			}
		} catch (StandardException se) {
			if (foundInCache)
				((GenericLanguageConnectionContext) lcc).removeStatement(this);
			throw se;
		} finally {
			synchronized (preparedStmt) {
				preparedStmt.compilingStatement = false;
				preparedStmt.notifyAll();
			}
		}

		lcc.commitNestedTransaction();

		if (statementContext != null)
			lcc.popStatementContext(statementContext, null);

		return preparedStmt;
	}


在这个方法中,有很多细节没有去仔细的研究,比如解析SQL文本、对Statement进行绑定、statement优化、生成Activation Class等,这些细节就要留待下面去研究了
分享到:
评论
1 楼 Canicanliu 2012-07-19  
写得不错啊。正好有一个项目需要阅读derby的源码!学习学习!

相关推荐

    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.13.1.1-bin.tar.gz

    db-derby-10.13.1.1-bin.tar.gz 是一个包含Apache Derby 10.13.1.1版本的二进制发行版的压缩包,适用于Linux操作系统。 Apache Derby的核心特性包括: 1. **轻量级**:由于完全用Java实现,Derby具有小巧的体积,...

    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...

    db-derby-10.14.2.0-lib.zip

    Derby数据库是一个纯用Java实现的珍袖型的数据库,属于Apache的一个开源项目。由于是用Java实现的,所以可以在任何平台上运行;另外一个特点是体积小,免安装,java1.6开始集成了derby数据库,位于jdk下面的db目录下...

    derby辅助工具SQuirreL SQL Client的使用

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

    Derby SQL使用规范(refderby.pdf)

    Apache Derby是一个完全开放源代码的Java数据库,完全用Java编写,它符合标准的SQL和JDBC接口。它在Java平台上被设计为嵌入式数据库。由于其轻量级特性,它特别适合小型应用程序。Derby的SQL使用规范非常详细,涵盖...

    db-derby-10.11

    "db-derby-10.11"指的是Derby数据库的一个特定版本,即10.11.1.1。这个版本可能包含了若干重要的改进、修复和新特性。在Java开发环境中,使用这样的版本号可以帮助开发者精确地追踪和管理他们所依赖的库。 在提供的...

    derby数据库转sql、db2等其他数据库的简易工具

    本文将详细介绍如何使用这个由朋友手工制作的“derby转sql工具”来实现这一目标。 首先,让我们了解为什么会有这样的需求。Derby数据库虽然高效且易于使用,但其功能和性能可能无法满足大型企业或复杂应用的需求。...

    Apache Derby 10.2版手册集

    Apache Derby是一款由Apache软件基金会开发并维护的开源数据库管理系统。它以其轻量级、高性能和纯Java实现而著称,使得用户只需在目标操作系统上安装Java虚拟机(JVM),即可运行Derby。这种特性使得Derby成为一种跨...

    我的derby学习笔记之一:derby开始准备

    derby的eclipse插件 博文链接:https://hugebait.iteye.com/blog/47188

    版本 derby-10.17.1.0

    版本 derby-10.17.1.0

    derby驱动jar包

    derby-jdbc-driver-10.4.jar

    QuickKnowledge网站源代码

    4. **源代码分析**: - 通过分析QuickKnowledge嵌入式数据库源代码,开发者可以了解如何在实际项目中结合SSH框架和Derby数据库进行开发。这包括但不限于:数据库连接池的配置、实体类的设计、DAO接口及其实现、...

    Derby数据库(V10.4)用户手册(PDF版)

    01. Getting Started with Derby - 10.4.pdf 02. Derby Reference Manual - 10.4.pdf 03. Derby Developer's Guide - 10.4.pdf 04. Tuning Derby - 10.4.pdf 05. Derby Server and Administration Guide - 10.4.pdf ...

    Eclipse下Apache Derby开发

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

    Derby To Butt Plus-crx插件

    语言:English (United States) 用“我的对接”替换文本'roller derby',以及在某些... 源代码在https://github.com/joppeschwartz/derby-to-butt 从云到对接加上叉,这里可用:https://github.com/hank/butt-to-butt

    derby 数据库开发文档

    1. **纯 Java 实现**:作为一款完全用 Java 编写的数据库,Derby 可以在任何支持 Java 的平台上运行。 2. **嵌入式数据库引擎**:Derby 可以直接嵌入到 Java 应用程序中运行,无需单独安装服务器软件。 3. **高度可...

Global site tag (gtag.js) - Google Analytics