代码分析(四)

2021SC@SDUSC

AbstractSQLExecutor—SQL执行器

接着上次的分析,在上次的分析中,我们在execute方法里面是调用了executeAppJoin方法,但在上次并没有对其进行详细的分析,这次便来分析一下它的作用

JOIN查询附表
/**@ APP JOIN 查询副表并缓存到 childMap
	 * @param config
	 * @param resultList
	 * @param childMap
	 * @throws Exception 
	 */
	protected void executeAppJoin(SQLConfig config, List<JSONObject> resultList, Map<String, JSONObject> childMap) throws Exception {
		List<Join> joinList = config.getJoinList();
		if (joinList != null) {

			SQLConfig jc;
			SQLConfig cc;

			for (Join j : joinList) {
				if (j.isAppJoin() == false) {
					continue;
				}

				cc = j.getCacheConfig(); //这里用config改了getSQL后再还原很麻烦,所以提前给一个config2更好
				if (cc == null) {
					if (Log.DEBUG) {
						throw new NullPointerException("服务器内部错误, executeAppJoin cc == null ! 导致不能缓存 @ APP JOIN 的副表数据!");
					}
					continue;
				}

				jc = j.getJoinConfig();

				//取出 "id@": "@/User/userId" 中所有 userId 的值
				List<Object> targetValueList = new ArrayList<>();
				JSONObject mainTable;
				Object targetValue;

				for (int i = 0; i < resultList.size(); i++) {
					mainTable = resultList.get(i);
					targetValue = mainTable == null ? null : mainTable.get(j.getTargetKey());

					if (targetValue != null && targetValueList.contains(targetValue) == false) {
						targetValueList.add(targetValue);
					}
				}


				//替换为 "id{}": [userId1, userId2, userId3...]
				jc.putWhere(j.getOriginKey(), null, false);  // remove orginKey
				jc.putWhere(j.getKey() + "{}", targetValueList, true);  // add orginKey{}

				jc.setMain(true).setPreparedValueList(new ArrayList<>());

				boolean prepared = jc.isPrepared();
				final String sql = jc.getSQL(false);
				jc.setPrepared(prepared);

				if (StringUtil.isEmpty(sql, true)) {
					throw new NullPointerException(TAG + ".executeAppJoin  StringUtil.isEmpty(sql, true) >> return null;");
				}
				//执行副表的批量查询 并 缓存到 childMap
				ResultSet rs = null;
				try {
					rs = executeQuery(jc);

					int index = -1;

					ResultSetMetaData rsmd = rs.getMetaData();
					final int length = rsmd.getColumnCount();

					JSONObject result;
					String cacheSql;
					while (rs.next()) { //FIXME 同时有 @ APP JOIN 和 < 等 SQL JOIN 时,next = false 总是无法进入循环,导致缓存失效,可能是连接池或线程问题
						index ++;
						Log.d(TAG, "\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n executeAppJoin while (rs.next()){  index = " + index + "\n\n");

						result = new JSONObject(true);

						for (int i = 1; i <= length; i++) {

							result = onPutColumn(jc, rs, rsmd, index, result, i, null);
						}

						//每个 result 都要用新的 SQL 来存 childResultMap = onPutTable(config, rs, rsmd, childResultMap, index, result);

						//缓存到 childMap
						cc.putWhere(j.getKey(), result.get(j.getKey()), true);
						cacheSql = cc.getSQL(false);
						childMap.put(cacheSql, result);

						Log.d(TAG, ">>> executeAppJoin childMap.put('" + cacheSql + "', result);  childMap.size() = " + childMap.size());
					}
				}
				finally {
					if (rs != null) {
						try {
							rs.close();
						} 
						catch (Exception e) {
							e.printStackTrace();
						}
					}
				}	
			}
		}

	}

首先就是获取到相对应的joinlist,这个我们原来在进行查询是都有关联表的时候进行过分析,这个列表存放的是查询所需要的关联的表,如果有副表,那么去除这个列表的每一项,然后就搞了两个sqlconfig对象,分别读出getCacheConfig()和getJoinConfig()对其进行赋值。

然后就是对结果集列表的操作,取出 “id@”: “@/User/userId” 中所有 userId 的值,替换为 “id{}”: [userId1, userId2, userId3…],这一步其实是很好理解,就是一个结果集的替换,然后获取到sql,开始进行从数据库获取数据executeQuery,而这个ResultSetMetaData对象我们在下面会讲。

总结来说,这个executeAppJoin就是先根据副表的内容,去生成不同的sql语言,然后根据不同的sql语言与数据库进行交互从而获得数据,然后讲这些数据在进行合并。

判断是否为JSON类型方法
@Override
public boolean isJSONType(@NotNull SQLConfig config, ResultSetMetaData rsmd, int position, String lable) {
	try {
		String column = rsmd.getColumnTypeName(position);
		//return rsmd.getColumnType(position) == 1;

		if (column.toLowerCase().contains("json")) {
			return true;
		}
	} catch (SQLException e) {
		e.printStackTrace();
	}
	//		List<String> json = config.getJson();
	//		return json != null && json.contains(lable);
	return false;
}

在这个方法中传入的是ResultSetMetaData类的对象,而ResultSetMetaData类存储了 ResultSet的MetaData。所谓的MetaData在英文中的解释为“Data about Data”,直译成中文则为“有关数据的数据”或者“描述数据的数据”,实际上就是描述及解释含义的数据。以Result的MetaData为例,ResultSet是以表格的形式存在,所以getMetaData就包括了数据的字段名称、类型以及数目等表格所必须具备的信息。

我们这里调用的是ResultSetMetaData类的方法:

              `String getColumnTypeName(int column)`

这个方法的作用就是检索指定列的数据库特定的类型名称,而对于CHAR和JSON类型的字段,getColumnType返回值都是1 ,如果不用CHAR,改用VARCHAR,则可以用String column = rsmd.getColumnTypeName(position)这个代码来提高性能。

获取statement:
public PreparedStatement getStatement(@NotNull SQLConfig config) throws Exception {
		PreparedStatement statement; //创建Statement对象
		if (config.getMethod() == RequestMethod.POST && config.getId() == null) { //自增id
			statement = getConnection(config).prepareStatement(config.getSQL(config.isPrepared()), Statement.RETURN_GENERATED_KEYS);
		}
		else {
			statement = getConnection(config).prepareStatement(config.getSQL(config.isPrepared()));
		}
		List<Object> valueList = config.isPrepared() ? config.getPreparedValueList() : null;

		if (valueList != null && valueList.isEmpty() == false) {
			for (int i = 0; i < valueList.size(); i++) {
				statement = setArgument(config, statement, i, valueList.get(i));
			}
		}
		// statement.close();

		return statement;
	}

在这里面大多基本是和我们的JDBC相同,但是其中主要是与config进行了信息的传递,通过config获得相应的statement

在这里面将SQL的配置对象传入,然后就是根据getId的返回是不是null也就是说是不是自增的id,根据这两种情况返回不同的statement。

获取connection:
public Connection getConnection(@NotNull SQLConfig config) throws Exception {
		String connectionKey = config.getDatasource() + "-" + config.getDatabase();
		connection = connectionMap.get(connectionKey);
		if (connection == null || connection.isClosed()) {
			Log.i(TAG, "select  connection " + (connection == null ? " = null" : ("isClosed = " + connection.isClosed()))) ;
			// PostgreSQL 不允许 cross-database
			connection = DriverManager.getConnection(config.getDBUri(), config.getDBAccount(), config.getDBPassword());
			connectionMap.put(connectionKey, connection);
		}

		int ti = getTransactionIsolation();
		if (ti != Connection.TRANSACTION_NONE) { //java.sql.SQLException: Transaction isolation level NONE not supported by MySQL
			begin(ti);
		}

		return connection;
	}

这个方法的核心作用就是说针对不同的数据库比如MySQL或者PostgreSQL这样的数据库,根据它们的数据库类型从而获得相应的connection对象。

关闭连接:
public void close() {
		cacheMap.clear();
		cacheMap = null;

		generatedSQLCount = 0;
		cachedSQLCount = 0;
		executedSQLCount = 0;

		if (connectionMap == null) {
			return;
		}

		Collection<Connection> connections = connectionMap.values();

		if (connections != null) {
			for (Connection connection : connections) {
				try {
					if (connection != null && connection.isClosed() == false) {
						connection.close();
					}
				}
				catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}

		connectionMap.clear();
		connectionMap = null;
	}

这个方法很好理解,就是讲应用的各种资源去归零也就是释放资源,然后就是对于当前连接的所有connection去进行一个循环关闭。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值