TERASOLUNAのJdbcCodeList
はデータベースからSQLで検索した結果をコードリストとして登録する機能で、よく使われていると思います。
1つや2つのコードリストなら大したことはないのですが、コードリストの数が数百のクラスになってくると起動にかかる時間が耐えがたいレベルにまでなってきます。
そこで下記のようにUNION ALL
で複数のコードリストを1度に登録できないものかと。
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コードリスト1Bean定義となっているので、複数のコードリストを登録するには、動的に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アクセスし、空っぽのコードリストに値のマップを設定します。
実装イメージは下記の通り。
/**
*
*/
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;
}
}
/**
*
*/
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;
}
}
<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>
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;