2023 글로벌미디어학부 졸업 작품 프로젝트 Dandi 에서 통합테스트에서 테스트마다 DB를 초기화해줘야 했습니다.
과거에는 아래처럼, EntityManager의 JPA Entity로 사용하는 도메인 객체의 이름과 테이블 이름이 동일했기 때문에
Entity 이름을 Snake Case로 변경하여 테이블을 초기화했습니다.
@Component
@ActiveProfiles("test")
public class DatabaseCleaner implements InitializingBean {
@PersistenceContext
private EntityManager entityManager;
private List<String> tableNames;
@Override
public void afterPropertiesSet() throws Exception {
this.tableNames = entityManager.getMetamodel()
.getEntities().stream()
.filter(e -> e.getJavaType().getAnnotation(Entity.class) != null)
.map(e -> CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, e.getName()))
.collect(Collectors.toList());
}
@Transactional
public void clear() {
entityManager.flush();
entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY FALSE").executeUpdate();
for (String tableName : tableNames) {
entityManager.createNativeQuery("TRUNCATE TABLE " + tableName).executeUpdate();
entityManager
.createNativeQuery("ALTER TABLE " + tableName + " ALTER COLUMN " + tableName + "_id RESTART WITH 1")
.executeUpdate();
}
entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY TRUE").executeUpdate();
}
}
하지만, 헥사고날 아키텍처를 적용하면서 도메인 객체는 더이상 JPA Entity가 아니고 아래와 같이 Out Adapter에서 JPA Entity를 관리했습니다. 네이밍도 도메인 객체 Member와 동일하게 할 수 없기 때문에, MemberJpaEntity로 지정하고 테이블 이름은 member로 지정해주었습니다.
@Entity
@Table(name = "member")
public class MemberJpaEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "member_id")
private Long id;
}
따라서, 더이상 EntityManager를 통해 TableName을 추출할 수 없습니다. 따라서, 다른 방법을 찾아야했습니다. 며칠동안 테이블 이름들을 List에 상수로 하드코딩을 해두었는데, 개발을 함에 따라 휴먼에러가 조금 발생했고 앞으로도 발생할 수 있을 것이라고 생각했습니다.
따라서, DataSource를 통해서 Table 이름을 추출할 수 있겠다는 생각이 들었고, 조금 더 찾아보니 DataSource의 정보를 통해 Table 이름을 추출
하는 방식을 찾아냈습니다.
@Component
public class DatabaseCleaner implements InitializingBean {
private static final String SYS_CONFIG_TABLE_NAME = "sys_config";
@PersistenceContext
private EntityManager entityManager;
@Autowired
private DataSource dataSource;
private final List<String> tableNames = new ArrayList<>();
@Override
public void afterPropertiesSet() {
try (Connection connection = dataSource.getConnection()) {
DatabaseMetaData databaseMetaData = connection.getMetaData();
ResultSet rs = databaseMetaData.getTables(null, null, "%", new String[]{"TABLE"});
while (rs.next()) {
addExceptSysConfigTable(rs);
}
} catch (SQLException e) {
}
}
private void addExceptSysConfigTable(ResultSet rs) throws SQLException {
String tableName = rs.getString("TABLE_NAME");
if (!tableName.equals(SYS_CONFIG_TABLE_NAME)) {
tableNames.add(tableName);
}
}
@Transactional
public void clear() {
entityManager.createNativeQuery("SET FOREIGN_KEY_CHECKS = 0").executeUpdate();
entityManager.flush();
for (String tableName : tableNames) {
entityManager.createNativeQuery("TRUNCATE TABLE " + tableName).executeUpdate();
entityManager.createNativeQuery(
"ALTER TABLE " + tableName + " AUTO_INCREMENT = 1")
.executeUpdate();
}
entityManager.createNativeQuery("SET FOREIGN_KEY_CHECKS = 1").executeUpdate();
}
}
위 afterPropertiesSet 메서드에서 DatabaseMetaData의 getTables 메서드를 통해 Table 이름을 추출하는 로직이 있습니다.
sys_config 테이블은 제가 선언한 테이블이 아니라, mysql에서 시스템 구성 변수를 저장하는 테이블이라서 제외시켰습니다.
Chat GPT