DBUtilsパッケージのBeanProcessorの最適化

3582 ワード

会社が行っているプロジェクトでBeanProcessorを使用してResultSetをBeanに変換しています.データベースに300以上のフィールドがあるテーブルがあります(ここではデータベース設計の問題はともかく)、結果セットをインスタンス化するプロセスに時間がかかることがわかりました.BeanProcessorのソースコードを分析してみると、mapColumnsToPropertiesという方法で問題が発生しています.

	public Object toBean(ResultSet rs, Class type) throws SQLException {
		...
		int[] columnToProperty = mapColumnsToProperties(rsmd, props);
		...
	}

	public List toBeanList(ResultSet rs, Class type) throws SQLException {
		...
		int[] columnToProperty = mapColumnsToProperties(rsmd, props);
		...
	}

	private Object createBean(ResultSet rs, Class type,
	    PropertyDescriptor[] props, int[] columnToProperty) throws SQLException {
		...
		for (int i = 1; i < columnToProperty.length; ++i) {
			...
		}

		...
	}

	protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,
	    PropertyDescriptor[] props) throws SQLException {
		int cols = rsmd.getColumnCount();
		int[] columnToProperty = new int[cols + 1];
		Arrays.fill(columnToProperty, -1);

		for (int col = 1; col <= cols; ++col) {
			String columnName = getPropertyName(rsmd.getColumnName(col));
			for (int i = 0; i < props.length; ++i) {
				if (columnName.equalsIgnoreCase(props[i].getName())) {
					columnToProperty[col] = i;
					break;
				}
			}
		}

		return columnToProperty;
	}

このように、全フィールドクエリでは、外層ループが300回以上、メモリループが300回以上あり、時間がかかる部分はこのネストされたループにあります(もちろん、一般的にフィールド数が少ない場合、ここでの差は無視できます).
そこで手を変えました.

	...
	private Map properties = new HashMap();

	public Object toBean(ResultSet rs, Class type) throws SQLException {
		PropertyDescriptor[] props = propertyDescriptors(type);

		for (int i = 0; i < props.length; i++) {// Map 
			properties.put(props[i].getName(), props[i]);
		}

		return createBean(rs, type);
	}

	public List toBeanList(ResultSet rs, Class type) throws SQLException {
		List results = new ArrayList();

		if (!(rs.next())) {
			return results;
		}

		PropertyDescriptor[] props = propertyDescriptors(type);
		for (int i = 0; i < props.length; i++) {// Map 
			properties.put(props[i].getName(), props[i]);
		}
		do {
			results.add(createBean(rs, type));
		} while (rs.next());

		return results;
	}

	private Object createBean(ResultSet rs, Class type) throws SQLException {
		Object bean = newInstance(type);
		ResultSetMetaData rsmd = rs.getMetaData();
		int cols = rsmd.getColumnCount();
		for (int col = 1; col <= cols; col++) {
			String columnName = getPropertyName(rsmd.getColumnName(col));
			if (properties.containsKey(columnName)) {// 
				PropertyDescriptor prop = (PropertyDescriptor) properties.get(columnName);
				Class propType = prop.getPropertyType();
				Object value = processColumn(rs, col, propType);
				if ((propType != null) && (value == null) && (propType.isPrimitive())) {
					value = primitiveDefaults.get(propType);
				}

				callSetter(bean, prop, value);
			}
		}

		return bean;
	}
	...
	...

フィールド数を300とし、toBeanメソッドでエンティティを変換します.
このように変更した効果は,従来の約300*300+300回のサイクル(内層サイクルのbreakを無視)を300+300回のサイクルに変更することである.
BTW:問題が発見されたら、指摘を歓迎します.