✅ DI와 기능의 확장
애플리케이션을 새로 시작하지 않고 특정 SQL의 내용만을 변경하고 싶다면 어떻게 해야 할까요? 그에 앞서서 먼저 DI와 인터페이스의 상속에 대해서 알아보겠습니다.
- DI를 적용할때 가능한한 인터페이스를 사용해야 한다.
- 인터페이스의 사용으로 다형성을 얻기 위함이다.
- 인터페이스 분리 원칙을 통해 클라이언트와 의존 오브젝트 사이의 관계를 명확하게 해줄 수 있다.
- 각기 다른 관심과 목적에 따라 명확하게 구분이 가능하며, 하나의 오브젝트가 여러 개의 인터페이스를 구현할 수 있으므로, 하나의 오브젝트를 바라보는 창이 여러 가지일 수 있게 된다.
- 인터페이스 분리 원칙이 주는 장점은 모든 클라이언트가 자신의 관심에 따른 접근 방식을 불필요한 간섭 없이 유지할 수 있다는 점
- 기존 클라이언트에 영향을 주지 않은 채, 오브젝트의 기능을 확장하거나 수정이 가능
- 새로운 인터페이스를 추가로 구현하거나, 인터페이스 상속을 통해 확장할 때도 적용됨
- SqlRegistry
package vol1.jhcode.ch7.user.slqservice; import vol1.jhcode.ch7.user.slqservice.exception.SqlNotFoundException; public interface SqlRegistry { void registerSql(String key, String sql); String findSql(String key) throws SqlNotFoundException; }
BaseSqlService 입장에서 SQL을 업데이트하는 기능을 이용하는 클라이언트가 될 이유가 없기 때문에 SqlRegistry interface의 코드를 수정하는 것은 바람직하지 않습니다. 새로운 기능을 사용하는 클라이언트를 위해 새로 인터페이스를 만들거나, 기존의 인터페이스를 확장하는 게 바람직한 인터페이스 분리 원칙을 지키는 길입니다.
- UpdatableSqlRegistry
package vol1.jhcode.ch7.user.slqservice; import java.util.Map; import vol1.jhcode.ch7.user.slqservice.exception.SqlUpdateFailureException; public interface UpdatableSqlRegistry extends SqlRegistry { public void updateSql(String key, String gsql) throws SqlUpdateFailureException; public void updateSql(Map<String, String> sqlmap) throws SqlUpdateFailureException; }
SqlRegistry 인터페이스를 확장하여 SQL을 찾고 등록하는 기본적인 기능은 상속 받고, SQL을 update하는 기능의 메소드를 추가하였습니다.
✅ DI를 이용해 다양한 구현 방법 적용
앞에서 만든 UpdatableSqlRegistry를 구현해보도록 하겠습니다. 이때 동기화된 해시 데이터 조작에 최적화되도록 만들어진 ConcurrentHashMap을 많이 사용합니다. TDD 방식으로 테스트 코드를 먼저 작성해 보겠습니다.
- ConcurrentHashMapSqlRegistryTest
package vol1.jhcode.ch7.user.slqservice.updatable; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import vol1.jhcode.ch7.user.slqservice.UpdatableSqlRegistry; import vol1.jhcode.ch7.user.slqservice.exception.SqlNotFoundException; import vol1.jhcode.ch7.user.slqservice.exception.SqlUpdateFailureException; public class ConcurrentHashMapSqlRegistryTest { UpdatableSqlRegistry sqlRegistry; @BeforeEach public void setUp() { sqlRegistry = new ConcurrentHashMapSqlRegistry(); 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) { assertEquals(sqlRegistry.findSql("KEY1"), expected1); assertEquals(sqlRegistry.findSql("KEY2"), expected2); assertEquals(sqlRegistry.findSql("KEY3"), expected3); } @Test public void unknownKey() { //sqlRegistry.findSql("SQL9999!@#$"); Assertions.assertThrows(SqlNotFoundException.class, () -> {sqlRegistry.findSql("SQL9999!@#$");}); } @Test public void updateSingle() { sqlRegistry.updateSql("KEY2", "Modified2"); checkFindResult("SQL1", "Modified2", "SQL3"); } @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"); } @Test public void updateWithNotExistingKey() { //sqlRegistry.updateSql("SQL9999!@#$", "Modified2"); Assertions.assertThrows(SqlUpdateFailureException.class, () -> {sqlRegistry.updateSql("SQL9999!@#$", "Modified2");}); } }
동시성에 대한 부분은 간단하지 않기 때문에 우선 넘어가고, 일단 수정 기능을 검증하는 것으로 해보겠습니다. 테스트 코드의 기능을 충족시키는 UpdatableSqlRegistry를 구현한 클래스를 만들어 보겠습니다.
- ConcurrentHashMapSqlRegistry
package vol1.jhcode.ch7.user.slqservice.updatable; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import vol1.jhcode.ch7.user.slqservice.UpdatableSqlRegistry; import vol1.jhcode.ch7.user.slqservice.exception.SqlNotFoundException; import vol1.jhcode.ch7.user.slqservice.exception.SqlUpdateFailureException; public class ConcurrentHashMapSqlRegistry implements UpdatableSqlRegistry { private Map<String, String> sqlMap = new ConcurrentHashMap<String, String>(); @Override public void registerSql(String key, String sql) { sqlMap.put(key, sql); } @Override public String findSql(String key) throws SqlNotFoundException { String sql = sqlMap.get(key); if (sql == null) throw new SqlNotFoundException(key + "를 이용해서 SQL을 찾을 수 없습니다"); else return sql; } @Override public void updateSql(String key, String sql) throws SqlUpdateFailureException { if (sqlMap.get(key) == null) { throw new SqlUpdateFailureException(key + "에 해당하는 SQL을 찾을 수 없습니다"); } sqlMap.put(key, sql); } @Override public void updateSql(Map<String, String> sqlmap) throws SqlUpdateFailureException { for(Map.Entry<String, String> entry : sqlmap.entrySet()) { updateSql(entry.getKey(), entry.getValue()); } } }
단위 테스트는 성공적으로 수행되었습니다. 이제 JavaConfig DI 설정을 변경하여 OxmSqlService와 통합 테스트를 진행해 잘 수행되는지 확인해 보겠습니다.
- TestDaoFactory
@Bean public OxmSqlService sqlService() { OxmSqlService oxmSqlService = new OxmSqlService(); oxmSqlService.setSqlmap(new ClassPathResource ("/vol1/jhcode/ch7/user/dao/sqlmap.xml", UserDao.class)); oxmSqlService.setUnmarshaller(unmarshaller()); oxmSqlService.setSqlRegistry(sqlRegistry()); return oxmSqlService; } @Bean public ConcurrentHashMapSqlRegistry sqlRegistry() { return new ConcurrentHashMapSqlRegistry(); }
SqlService를 통해 실질적으로 SQL을 사용하는 UserDao도 성공적으로 테스트를 완료하였습니다.
✅ 내장형 데이터베이스를 이용한 SQL 레지스트리
- pom.xml
<!-- 내장형 DB, h2 --> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>2.1.214</version> <scope>test</scope> </dependency>
🔹내장형 DB 빌더 학습 테스트
먼저 내장형 DB 생성에 필요한 Table SQL과, data SQL을 준비합니다.
- schema.sql
CREATE TABLE SQLMAP ( KEY_ VARCHAR(100) PRIMARY KEY, SQL_ VARCHAR(100) NOT NUll );
- data.sql
INSERT INTO SQLMAP(KEY_, SQL_) values('KEY1', 'SQL1'); INSERT INTO SQLMAP(KEY_, SQL_) values('KEY2', 'SQL2');
- EmbeddeDBTest
package vol1.jhcode.ch7.learningtest.spring.embeddeddb; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType.H2; import java.util.List; import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.dao.support.DataAccessUtils; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; public class EmbeddedDBTest { EmbeddedDatabase db; JdbcTemplate template; @BeforeEach public void setUp() { //== 내장형 DB를 생성하기 위한 코드 ==// db = new EmbeddedDatabaseBuilder() .setType(H2) .addScript("classpath:/vol1/jhcode/ch7/learningtest/spring/embeddeddb/schema.sql") .addScript("classpath:/vol1/jhcode/ch7/learningtest/spring/embeddeddb/data.sql") .build(); template = new JdbcTemplate(db); } @AfterEach public void tearDown() { db.shutdown(); } @Test public void initData() { List<Integer> result = template.query("select count(*) from sqlmap", (rs, rowNum) -> rs.getInt(1)); int ret = (int) DataAccessUtils.singleResult(result); assertEquals(ret, 2); List<Map<String, Object>> list = template.queryForList("select * from sqlmap order by key_"); assertEquals((String)list.get(0).get("key_"), "KEY1"); assertEquals((String)list.get(0).get("sql_"), "SQL1"); assertEquals((String)list.get(1).get("key_"), "KEY2"); assertEquals((String)list.get(1).get("sql_"), "SQL2"); } @Test public void insert() { template.update("insert into sqlmap(key_, sql_) values(?,?)", "KEY3", "SQL3"); List<Integer> result = template.query("select count(*) from sqlmap", (rs, rowNum) -> rs.getInt(1)); int ret = (int) DataAccessUtils.singleResult(result); assertEquals(ret, 3); } }
❗책에서는 SimpleJdbcTemplate을 사용했지만 저는 JdbcTemplate을 그대로 사용했습니다.
EmbeddedDatabase는 DataSource의 서브 인터페이스이므로 DataSource를 필요하는 JdbcTemplate을 만들 때 사용할 수 있습니다.
✅ 내장형 DB를 이용한 SqlRegistry 만들기
- EmbeddedDbSqlRegistry
package vol1.jhcode.ch7.user.slqservice.updatable; import java.util.Map; import javax.sql.DataSource; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import vol1.jhcode.ch7.user.slqservice.UpdatableSqlRegistry; import vol1.jhcode.ch7.user.slqservice.exception.SqlNotFoundException; import vol1.jhcode.ch7.user.slqservice.exception.SqlUpdateFailureException; public class EmbeddedDbSqlRegistry implements UpdatableSqlRegistry { JdbcTemplate jdbc; TransactionTemplate transactionTemplate; public void setDataSource(DataSource dataSource) { jdbc = new JdbcTemplate(dataSource); transactionTemplate = new TransactionTemplate( new DataSourceTransactionManager(dataSource)); transactionTemplate.setIsolationLevel(TransactionTemplate.ISOLATION_READ_COMMITTED); } @Override public void registerSql(String key, String sql) { jdbc.update("insert into sqlmap(key_, sql_) values(?,?)", key, sql); } @Override public String findSql(String key) throws SqlNotFoundException { try { return jdbc.queryForObject("select sql_ from sqlmap where key_ = ?", String.class, key); } catch(EmptyResultDataAccessException e) { throw new SqlNotFoundException(key + "에 해당하는 SQL을 찾을 수 없습니다", e); } } @Override public void updateSql(String key, String sql) throws SqlUpdateFailureException { int affected = jdbc.update("update sqlmap set sql_ = ? where key_ = ?" , sql, key); if (affected == 0) { throw new SqlUpdateFailureException(key + "에 해당하는 SQL을 찾을 수 없습니다"); } } @Override public void updateSql(Map<String, String> sqlmap) throws SqlUpdateFailureException { transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { for(Map.Entry<String, String> entry : sqlmap.entrySet()) { updateSql(entry.getKey(), entry.getValue()); } } }); } }
여기서 왜 EmbeddedDatabase를 받지 않고 DataSource만 받았을까요? 이는 인터페이스 분리 원칙을 지키기 위해서 입니다. DataSource와 EmbeddedDatabase는 상속 관계에 있지만 JdbcTemplate 입장에서는 다른 부가 기능 없이 DB에 접근만 할 수 있는 DataSource 인터페이스가 가장 적합하기 때문입니다.
🔹테스트 코드의 확장
ConcurrentHashMapSqlRegistry와 EmbeddedDbSqlRegistry는 동일한 SqlRegistry를 상속 받았기 때문에 테스트 코드 또한 비슷하며, 중복되는 부분이 많습니다. 기존의 테스트 코드에서 SqlRegistry를 생성하는 코드만 분리해 낸다면 유연하게 테스트 코드를 확장 시킬 수 있습니다.
- AbstractUpdatableSqlRegistry
package vol1.jhcode.ch7.user.slqservice.updatable; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import vol1.jhcode.ch7.user.slqservice.UpdatableSqlRegistry; import vol1.jhcode.ch7.user.slqservice.exception.SqlNotFoundException; import vol1.jhcode.ch7.user.slqservice.exception.SqlUpdateFailureException; public abstract class AbstractUpdatableSqlRegistryTest { UpdatableSqlRegistry sqlRegistry; @BeforeEach public void setUp() { sqlRegistry = createUpdatableSqlRegistry(); sqlRegistry.registerSql("KEY1", "SQL1"); sqlRegistry.registerSql("KEY2", "SQL2"); sqlRegistry.registerSql("KEY3", "SQL3"); } //== SqlRegistry 구현체를 생성하는 부분은 서브 클래스에서 구현한다 ==// abstract protected UpdatableSqlRegistry createUpdatableSqlRegistry(); @Test public void find() { checkFind("SQL1", "SQL2", "SQL3"); } @Test public void unknownKey() { assertThrows(SqlNotFoundException.class, () -> sqlRegistry.findSql("SQL9999!@#$")); } protected void checkFind(String expected1, String expected2, String expected3) { assertEquals(sqlRegistry.findSql("KEY1"), expected1); assertEquals(sqlRegistry.findSql("KEY2"), expected2); assertEquals(sqlRegistry.findSql("KEY3"), expected3); } @Test public void updateSingle() { sqlRegistry.updateSql("KEY2", "Modified2"); checkFind("SQL1", "Modified2", "SQL3"); } @Test public void updateMulti() { Map<String, String> sqlmap = new HashMap<String, String>(); sqlmap.put("KEY1", "Modified1"); sqlmap.put("KEY3", "Modified3"); sqlRegistry.updateSql(sqlmap); checkFind("Modified1", "SQL2", "Modified3"); } @Test public void updateWithNotExistingKey() { assertThrows(SqlUpdateFailureException.class, () -> sqlRegistry.updateSql("SQL9999!@#$", "Modified2")); } }
createUpdatableSqlRegistry()
메소드를 통해서 SqlRegistry 구현체를 생성하는 부분을 분리해내었습니다. 이렇게 추상 클래스로 만들었기 때문에 서브 클래스는 5개의 테스트 코드를 사용할 수 있게 됩니다.
- ConcurrentHashMapSqlRegistry
package vol1.jhcode.ch7.user.slqservice.updatable; import vol1.jhcode.ch7.user.slqservice.UpdatableSqlRegistry; public class ConcurrentHashMapSqlRegistryTest extends AbstractUpdatableSqlRegistryTest { @Override protected UpdatableSqlRegistry createUpdatableSqlRegistry() { return new ConcurrentHashMapSqlRegistry(); } }
SqlRegistry를 생성하는 코드만 구현한다면, @Test 테스트 메소드를 모두 상속 받아서 자신의 테스트로 사용할 수 있습니다.
이 방법으로 EmbeddedDbSqlRegistry에 대한 테스트를 만들어보겠습니다.
- EmbeddedDbSqlRegistry
package vol1.jhcode.ch7.user.sqlservice.updatable; import java.util.List; import java.util.Map; import javax.sql.DataSource; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import vol1.jhcode.ch7.user.sqlservice.UpdatableSqlRegistry; import vol1.jhcode.ch7.user.sqlservice.exception.SqlNotFoundException; import vol1.jhcode.ch7.user.sqlservice.exception.SqlUpdateFailureException; public class EmbeddedDbSqlRegistry implements UpdatableSqlRegistry { JdbcTemplate jdbc; TransactionTemplate transactionTemplate; public void setDataSource(DataSource dataSource) { jdbc = new JdbcTemplate(dataSource); transactionTemplate = new TransactionTemplate( new DataSourceTransactionManager(dataSource)); transactionTemplate.setIsolationLevel(TransactionTemplate.ISOLATION_READ_COMMITTED); } public void registerSql(String key, String sql) { jdbc.update("insert into sqlmap(key_ , sql_) values(?,?)", key, sql); } public String findSql(String key) throws SqlNotFoundException { try { String sql = "select sql_ from sqlmap where key_ = ?"; List<String> sqlList = jdbc.query(sql, new Object[]{key}, (resultSet, rowNum) -> resultSet.getString("sql_")); String sqlValue = null; if (!sqlList.isEmpty()) { return sqlValue = sqlList.get(0); } else { throw new SqlNotFoundException(key); } } catch(EmptyResultDataAccessException e) { throw new SqlNotFoundException(key + "에 해당하는 SQL을 찾을 수 없습니다", e); } } public void updateSql(String key, String sql) throws SqlUpdateFailureException { int affected = jdbc.update("update sqlmap set sql_ = ? where key_ = ?" , sql, key); if (affected == 0) { throw new SqlUpdateFailureException(key + "에 해당하는 SQL을 찾을 수 없습니다"); } } public void updateSql(Map<String, String> sqlmap) throws SqlUpdateFailureException { transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { for(Map.Entry<String, String> entry : sqlmap.entrySet()) { updateSql(entry.getKey(), entry.getValue()); } } }); } }
- EmbeddedDbSqlRegistryTest
package vol1.jhcode.ch7.user.sqlservice.updatable; import java.util.List; import java.util.Map; import javax.sql.DataSource; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import vol1.jhcode.ch7.user.sqlservice.UpdatableSqlRegistry; import vol1.jhcode.ch7.user.sqlservice.exception.SqlNotFoundException; import vol1.jhcode.ch7.user.sqlservice.exception.SqlUpdateFailureException; public class EmbeddedDbSqlRegistry implements UpdatableSqlRegistry { JdbcTemplate jdbc; TransactionTemplate transactionTemplate; public void setDataSource(DataSource dataSource) { jdbc = new JdbcTemplate(dataSource); transactionTemplate = new TransactionTemplate( new DataSourceTransactionManager(dataSource)); transactionTemplate.setIsolationLevel(TransactionTemplate.ISOLATION_READ_COMMITTED); } public void registerSql(String key, String sql) { jdbc.update("insert into sqlmap(key_ , sql_) values(?,?)", key, sql); } public String findSql(String key) throws SqlNotFoundException { try { String sql = "select sql_ from sqlmap where key_ = ?"; List<String> sqlList = jdbc.query(sql, new Object[]{key}, (resultSet, rowNum) -> resultSet.getString("sql_")); String sqlValue = null; if (!sqlList.isEmpty()) { return sqlValue = sqlList.get(0); } else { throw new SqlNotFoundException(key); } } catch(EmptyResultDataAccessException e) { throw new SqlNotFoundException(key + "에 해당하는 SQL을 찾을 수 없습니다", e); } } public void updateSql(String key, String sql) throws SqlUpdateFailureException { int affected = jdbc.update("update sqlmap set sql_ = ? where key_ = ?" , sql, key); if (affected == 0) { throw new SqlUpdateFailureException(key + "에 해당하는 SQL을 찾을 수 없습니다"); } } public void updateSql(Map<String, String> sqlmap) throws SqlUpdateFailureException { transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { for(Map.Entry<String, String> entry : sqlmap.entrySet()) { updateSql(entry.getKey(), entry.getValue()); } } }); } }
❗java.lang.NoSuchMethodError
- 기존 코드
public String findSql(String key) throws SqlNotFoundException { try { return jdbc.queryForObject("select sql_ from sqlmap where key_ = ?", String.class, key); } catch(EmptyResultDataAccessException e) { throw new SqlNotFoundException(key + "에 해당하는 SQL을 찾을 수 없습니다", e); } }
- 변경된 코드
public String findSql(String key) throws SqlNotFoundException { try { String sql = "select sql_ from sqlmap where key_ = ?"; List<String> sqlList = jdbc.query(sql, new Object[]{key}, (resultSet, rowNum) -> resultSet.getString("sql_")); String sqlValue = null; if (!sqlList.isEmpty()) { return sqlValue = sqlList.get(0); } else { throw new SqlNotFoundException(key); } } catch(EmptyResultDataAccessException e) { throw new SqlNotFoundException(key + "에 해당하는 SQL을 찾을 수 없습니다", e); } }
queryForObject 부분에서 해당 메소드가 없다는 java.lang.NoSuchMethodError 예외가 계속 발생되었습니다. 다른 프로젝트에서는 제대로 동작했기 때문에 컴파일, 라이브러리 충돌 등의 문제로 예상했고, 라이브러리 버전 변경, 재컴파일 등 무수한 시도를 했지만 결국 해결하지 못하여 코드를 수정했습니다.
@Override public <T> T queryForObject(String sql, Class<T> requiredType, @Nullable Object... args) throws DataAccessException { return queryForObject(sql, args, getSingleColumnRowMapper(requiredType)); }
기존의 토비 코드에서 그대로 Maven으로 변경한 kitec 코드에서도 동일한 queryForObject() 메소드를 사용하는 것으로 확인이 되었습니다. 어떤 프로젝트는 해당 메소드가 동작하고, 어떤 프로젝트는 해당 메소드가 동작하지 않는다? jdk 버전, spring tools sutie 버전, 의존성 버전 등 모든 충돌 부분을 확인했지만 원인을 찾을 수 없어서 조금 아쉽습니다.
- 기존 코드
✅ 내장형 DB 생성과 적용
- TestDaoFactory
@Bean public OxmSqlService sqlService() { OxmSqlService oxmSqlService = new OxmSqlService(); oxmSqlService.setSqlmap(new ClassPathResource ("/vol1/jhcode/ch7/user/dao/sqlmap.xml", UserDao.class)); oxmSqlService.setUnmarshaller(unmarshaller()); oxmSqlService.setSqlRegistry(sqlRegistry()); return oxmSqlService; } @Bean public Jaxb2Marshaller unmarshaller() { Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller(); jaxb2Marshaller.setContextPath("vol1.jhcode.ch7.user.sqlservice.jaxb"); return jaxb2Marshaller; } @Bean public EmbeddedDbSqlRegistry sqlRegistry() { EmbeddedDbSqlRegistry embeddedDbSqlRegistry = new EmbeddedDbSqlRegistry(); embeddedDbSqlRegistry.setDataSource(embeddedDatabase()); return embeddedDbSqlRegistry; } @Bean public DataSource embeddedDatabase() { ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(); databasePopulator.addScript(new ClassPathResource("vol1/jhcode/ch7/user/sqlservice/updatable/sqlRegistrySchema.sql")); return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .setName("embeddedDatabase") .setScriptEncoding("UTF-8") .addScript("classpath:vol1/jhcode/ch7/user/sqlservice/updatable/sqlRegistrySchema.sql") .build(); }
FactoryBean을 따로 구성하지 않고 @Bean을 생성할 때 EmbeddeDatabase 객체를 생성하도록 구성하였습니다. 테스트도 잘 수행하는 것을 확인할 수 있습니다.
📖 토비 스프링 3.1 -p616~645
🚩jhcode33의 toby-spring-study.git으로 이동하기
Uploaded by N2T