TERASOLUNAのJdbcCodeListの読み込みが遅いので一度に複数登録出来るようにしてみた

TERASOLUNAの`JdbcCodeList`はデータベースからSQLで検索した結果をコードリストとして登録する機能で、よく使われていると思います。
１つや２つのコードリストなら大したことはないのですが、コードリストの数が数百のクラスになってくると起動にかかる時間が耐えがたいレベルにまでなってきます。

そこで下記のように`UNION ALL`で複数のコードリストを1度に登録できないものかと。

```sql
SELECT code_bean_name, code_id, code_name FROM t_sample_codes WHERE code_bean_name = 'CL_ORDERSTATUS'
UNION ALL
SELECT code_bean_name, code_id, code_name FROM t_sample_codes WHERE code_bean_name = 'CL_HOGE'
ORDER BY code_bean_name, code_id
```

コードリストは1コードリスト１Bean定義となっているので、複数のコードリストを登録するには、動的にBean定義を追加してやる必要があります。
`org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor` インタフェースの実装クラスを作ってあげれば、どうやら動的にBeanを追加できそうです。
この`BeanDefinitionRegistryPostProcessor`の実装クラスからDBアクセスしてコードリストのBeanを登録してやれば、思った物ができそうです。

ところが実際動かしてみると、DBアクセスがうまいこといきません。
どうやら`BeanDefinitionRegistryPostProcessor`が動くタイミングでは、Bean定義の `${hogehoge}` みたいなプレースホルダが解決される前のようで、DBの接続文字列などがプロパティから設定される前の文字列（`${hogehoge}`自体）が取れてきてしまっていて、うまく接続できないようです。
DataSourceの定義にプレースホルダを使用せず、Bean定義に直に接続文字列などを記述すれば、DB接続はできるのですがXMLに直書きはどうにもイマイチです。

そこで`BeanDefinitionRegistryPostProcessor`の実装クラスのみで完結させるのは諦め、2段階に分けました。
`BeanDefinitionRegistryPostProcessor`の実装クラスでは、必要なコードリストのBean定義のみを定義します。（ただしコードリストの中身は空っぽ）
別のクラスの`@PostConstruct`でDBアクセスし、空っぽのコードリストに値のマップを設定します。
実装イメージは下記の通り。


```java:CustomBeanDefinitionRegistryPostProcessor.java
/**
 *
 */
package xxxxxx.yyyyyy.zzzzzz.app.welcome;

import java.util.List;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.terasoluna.gfw.common.codelist.SimpleMapCodeList;

/**
 * @author toshio
 *
 */
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
	private List<String> codeListNames;

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		if (codeListNames != null) {
			for (String codeBeanName : codeListNames) {
				BeanDefinition beanDefinition = new RootBeanDefinition(SimpleMapCodeList.class);
				registry.registerBeanDefinition(codeBeanName, beanDefinition);
			}
		}
	}

	/**
	 * @param codeListNames セットする codeListNames
	 */
	public void setCodeListNames(List<String> codeListNames) {
		this.codeListNames = codeListNames;
	}

}
```

```java:CustomCodeListLoader.java
/**
 *
 */
package xxxxxx.yyyyyy.zzzzzz.app.welcome;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.terasoluna.gfw.common.codelist.SimpleMapCodeList;

/**
 * @author toshio
 *
 */
public class CustomCodeListLoader {
	/**
	 * Database access information
	 */
	private JdbcTemplate jdbcTemplate;

	@Autowired
	private ApplicationContext applicationContext;

	/**
	 * SQL Query to access the database
	 */
	private String querySql;

	private List<String> codeListNames;

	/**
	 *
	 */
	private String codeNameColumn;

	/**
	 * property that holds the name of the column of the database holding the value part of the codelist
	 */
	private String valueColumn;

	/**
	 * property that holds the name of the column of the database holding the label part of the codelist
	 */
	private String labelColumn;

	@PostConstruct
	public void afterPropertiesSet() {

		Map<String, Map<String, String>> allMap = new HashMap<String, Map<String, String>>();
		for (String codeName : codeListNames) {
			allMap.put(codeName, new LinkedHashMap<String, String>());
		}

		List<Map<String, Object>> rows = jdbcTemplate.queryForList(querySql);
		for (Map<String, Object> row : rows) {
			Object codeName = row.get(codeNameColumn);
			Object key = row.get(valueColumn);
			Object value = row.get(labelColumn);
			if (codeName != null && key != null && value != null) {
				Map<String, String> map = allMap.get(codeName.toString());
				if (map == null) {
					map = new LinkedHashMap<String, String>();
				}
				map.put(key.toString(), value.toString());
				allMap.put(codeName.toString(), map);
			}
		}

		for (Entry<String, Map<String, String>> codeEnt : allMap.entrySet()) {
			Object codeBean = applicationContext.getBean(codeEnt.getKey());
			if (codeBean instanceof SimpleMapCodeList) {
				SimpleMapCodeList smc = (SimpleMapCodeList) codeBean;
				smc.setMap(codeEnt.getValue());
			}
		}
	}

	/**
	 * Sets JdbcTemplate
	 * @param jdbcTemplate JdbcTemplate instance for fetching code list records
	 */
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	/**
	 * Setter method for labelColumn
	 * @param labelColumn column name for fetch a code label
	 */
	public void setLabelColumn(String labelColumn) {
		this.labelColumn = labelColumn;
	}

	/**
	 * Setter method for valueColumn
	 * @param valueColumn column name for fetch code value
	 */
	public void setValueColumn(String valueColumn) {
		this.valueColumn = valueColumn;
	}

	/**
	 * Setter method for querySql
	 * @param querySql sql for fetching code list records from database
	 */
	public void setQuerySql(String querySql) {
		this.querySql = querySql;
	}

	public void setCodeNameColumn(String codeNameColumn) {
		this.codeNameColumn = codeNameColumn;
	}

	/**
	 * @param codeListNames セットする codeListNames
	 */
	public void setCodeListNames(List<String> codeListNames) {
		this.codeListNames = codeListNames;
	}

}
```


```xml:projectName-codelist.xml
    <bean class="xxxxxx.yyyyyy.zzzzzz.app.welcome.CustomBeanDefinitionRegistryPostProcessor">
        <property name="codeListNames">
            <list>
                <value>CL_ORDERSTATUS</value>
                <value>CL_HOGE</value>
            </list>
        </property>
    </bean>

    <bean class="xxxxxx.yyyyyy.zzzzzz.app.welcome.CustomCodeListLoader">
        <property name="querySql">
            <value>
                SELECT code_bean_name, code_id, code_name FROM t_sample_codes WHERE code_bean_name = 'CL_ORDERSTATUS'
                UNION ALL
                SELECT code_bean_name, code_id, code_name FROM t_sample_codes WHERE code_bean_name = 'CL_HOGE'
                ORDER BY code_bean_name, code_id
            </value>
        </property>
        <property name="codeNameColumn" value="code_bean_name"/>
        <property name="valueColumn" value="code_id" />
        <property name="labelColumn" value="code_name" />
        <property name="jdbcTemplate" ref="jdbcTemplateForCodeList" />
        <property name="codeListNames">
            <list>
                <value>CL_ORDERSTATUS</value>
                <value>CL_HOGE</value>
            </list>
        </property>
    </bean>

```

```sql
CREATE TABLE t_sample_codes (
                                code_bean_name varchar(32) not null,
                                code_id        varchar(32) not null,
                                code_name      varchar(256) not null,
                                primary key (code_bean_name, code_id)
                            );

INSERT INTO t_sample_codes(code_bean_name, code_id, code_name) VALUES ('CL_ORDERSTATUS', '1', 'Received');
INSERT INTO t_sample_codes(code_bean_name, code_id, code_name) VALUES ('CL_ORDERSTATUS', '2', 'Sent');
INSERT INTO t_sample_codes(code_bean_name, code_id, code_name) VALUES ('CL_ORDERSTATUS', '3', 'Cancelled');

INSERT INTO t_sample_codes(code_bean_name, code_id, code_name) VALUES ('CL_HOGE', '1', 'hoge');
INSERT INTO t_sample_codes(code_bean_name, code_id, code_name) VALUES ('CL_HOGE', '2', 'hogehoge');
INSERT INTO t_sample_codes(code_bean_name, code_id, code_name) VALUES ('CL_HOGE', '3', 'hogehogehoge');

commit;
```