TobiのSpring Chapter 7.3.3~7.5.2定理
リソースの抽象化
同じクラスパス以外のXMLまたは相対クラスパスではなく、サーバまたは開発システム内の特定のフォルダ内のファイルを読み込めませんか?Web上のリソースファイルをさらにインポートできますか?
しげん
<property>
のvalue-atreviewに入れるには外部で指定するしかありません.しかしvalueを入れることができるのは簡単な文字列だけです.リソースローダ
public interface ResourceLoader{
// location에 담긴 스트링 정보를 바탕으로 그에 적절한 Resource로 반환해준다.
Resource getResource(Stirng location);
}
Resourceを使用したXMLファイルのインポート
private class OxmSqlReader implements SqlReader {
private Resource sqlmap = new ClassPathResource("sqlmap.xml", UserDao.class);
public void setSqlmap(Resource sqlmap) {
this.sqlmap = sqlmap;
}
public void read(SqlRegistry sqlRegistry) {
try {
// 리소스 종류에 상관없이 스트림으로 가져올 수 있다.
Source source = new StreamSource(sqlmap.getInputStream());
} catch ...
}
}
<bean id="sqlService" class="springbook.user.sqlservice.OxmSqlService">
<property name="sqlMap" value="classpath:springbook/user/dao/sqlmap.xml" />
...
</bean>
特定の場所のファイル、Httpプロトコルにアクセス可能なWebリソースは、接頭辞を変更してインポートすることもできます.(file:, http:)インタフェースによる拡張セキュリティ機能の継承
原則として推奨されませんが、サーバの実行中にSQLを変更する必要がある場合があります.アプリケーションを再起動するのではなく、アプリケーションが使用しているSQLを緊急に変更する必要がある場合があります.
DIと機能拡張
DIインタフェースとのプログラミング
インタフェースの継承
複数のインタフェースを作成するのではなく、継承された方法で既存のインタフェースを拡張する場合があります.
public interface SqlRegistry {
void registerSql(String key, String sql);
Stirng findSql(String key) throws SqlNotFoundException;
}
ここに登録されているSQLを追加することで拡張したいと考えています.public interface UpdatableSqlRegistry extends SqlRegistry {
void updateSql(String key, String sql) throws SqlUpdateFailureException;
void updateSql(Map<String, String> sqlmap) throws SqlUpdateFailureException;
}
DI応用を用いた多様な実施方法
実行中のシステムで使用される情報をリアルタイムで変更する場合は、まず同期性の問題を考慮します.
ConcurrentHashMapを使用した変更可能なSQLレジストリ
したがって、一定のセキュリティとパフォーマンスを保証する同期HashMapとして使用するのに適しています.
変更可能なSQLレジストリテスト
public class ConcurrentHashMapSqlRegistryTest {
UpdatableSqlRegistry sqlRegistry;
@Before
public void setUp() {
sqlRegistry = new ConcurrentHashMapSqlRegistry();
// 테스트 메소드에서 사용할 초기화 SQL 정보를 미리 등록한다.
sqlRegistry.registerSql("KEY1", "SQL1");
sqlRegistry.registerSql("KEY2", "SQL2");
sqlRegistry.registerSql("KEY3", "SQL3");
}
@Test
public void find() {
checkFindResult("SQL1", "SQL2", "SQL3");
}
private void checkFindResult(String expected1, String expected2, String expected3) {
assertThat(sqlRegistry.findSql("KEY1"), is(expected1));
assertThat(sqlRegistry.findSql("KEY2"), is(expected2));
assertThat(sqlRegistry.findSql("KEY3"), is(expected3));
}
// 주어진 key에 해당하는 sql을 찾을 수 없을때 예외가 발생하는 지 확인하다. 예외상황에 대한 테스트는 빼먹기 쉽기에 항상 의식적으로 넣으려고 노력해야 한다.
@Test(expected=SqlNotFoundException.class)
public void unknownKey() {
sqlRegistry.findSql("SQL9999!@#$");
}
// 하나의 sql 업데이트 테스트, 검증할 때는 나머지 sql은 그대로인지도 확인해주는 것이 좋다.
@Test
public void updateSingle() {
sqlRegistry.updateSql("KEY2", "Modified2");
checkFindResult("SQL1", "Modified2", "SQL3");
}
// 여러개의 sql 수정 테스트
@Test
public void updateMulti() {
Map<String, String> sqlmap = new HashMap<String, String>();
sqlmap.put("KEY1", "Modified1");
sqlmap.put("KEY3", "Modified3");
sqlRegistry.updateSql(sqlmap);
checkFindResult("Modified1", "SQL2", "Modified3");
}
// 존재하지 않는 key의 sql을 변경하려고 시도할 때 예외가 발생하는 지 검증.
@Test(expected=SqlUpdateFailureException.class)
public void updateWithNotExistingKey() {
sqlRegistry.updateSql("SQL9999!@#$", "Modified2");
}
}
変更可能なSQLレジストリの実装
public class ConcurrentHashMapSqlRegistry implements UpdatableSqlRegistry {
private Map<String, String> sqlMap = new ConcurrentHashMap<String, String>();
@Override
public String findSql(String key) {
String sql = sqlMap.get(key);
if (sql == null) {
throw new SqlNotFoundException(key + "에 해당하는 SQL을 찾을 수 없습니다");
} else {
return sql;
}
}
@Override
public void registerSql(String key, String sql) {
sqlMap.put(key, sql);
}
@Override
public void updateSql(String key, String sql) {
if(sqlMap.containsKey(key)) {
sqlMap.put(key, sql);
} else {
throw new SqlUpdateFailureException(key + "에 해당하는 SQL을 찾을 수 없습니다");
}
}
@Override
public void updateSql(Map<String, String> sqlmap) {
sqlMap.putAll(sqlmap);
}
}
埋め込みDBによる実装
環境に格納されるデータ量が多くなり、クエリーや変更が頻繁に発生する場合はdbを使用します.
Springの組み込みデータベースのサポート
new EmbeddedDatabaseBuilder() // 빌더 오브젝트 생성
.setType(내장형DB종류) // H2, HSQL, DERBY 중에서 하나를 선택한다.
.addScript(초기화 db script리소스) // 초기화를 위한 SQL문장을 담은 스크립트 위치를 지정한다. 하나 이상을 지정할 수 있다.
...
.build(); // 주어진 조건에 맞는 내장형 DB를 준비하고 초기화 스크립트를 모두 실행한 뒤에 이에 접근할 수 있는 EmbeddedDatabase를 돌려준다.
埋め込みデータベースを使用したSqlRegistryの作成
<jdbc:embedded-database id="embeddedDatabase" type="HSQL">
<jdbc:script location="classpath:schema.sql"/>
</jdbc:embedded-database>
public class EmbeddedDbSqlRegistry implements UpdatableSqlRegistry{
SimpleJdbcTemplate jdbc;
public void setDataSource(DataSource dataSource) {
this.jdbc = new SimpleJdbcTemplate(dataSource); // 내장형 DB의 Datasource를 DI 받는다.
}
...
@Override
public void updateSql(String key, String sql) throws SqlUpdateFailureException {
// update()는 SQL 실행 결과로 영향을 받은 레코드의 개수를 리턴한다. 이를 이용하면 주어진 키(key)를 가진 SQL이 존재했는지를 간단히 확인할 수 있다.
int affected = jdbc.update("update sqlmap set sql_ = ? where key_ = ?", sql, key);
if(affected == 0) {
throw new SqlUpdateFailureException(key + "에 해당하는 SQL을 찾을 수 없습니다.");
}
}
}
Reference
この問題について(TobiのSpring Chapter 7.3.3~7.5.2定理), 我々は、より多くの情報をここで見つけました https://velog.io/@jamieshin/토비의-스프링-Chapter-7.3.3-7.5.2-정리テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol