스프링부트에서 스프링 데이터 JPA를 사용할 때, 데이터베이스의 스키마를 초기화하고 초기 데이터를 추가하는 방법에 대해 알아보자.

스프링 데이터 JPA 에서 사용한 Accoumt, AccountRepository, AccountRepositoryTest를 베이스로 진행한다.

@Entity
public class Account {

    @Id
    @GeneratedValue
    private Long id;

    private String username;

    private String password;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Account account = (Account) o;
        return Objects.equals(id, account.id) &&
                Objects.equals(username, account.username) &&
                Objects.equals(password, account.password);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, username, password);
    }
}

public interface AccountRepository extends JpaRepository<Account, Long> {

    Optional<Account> findByUsername(String username);
}

JPA를 사용한 데이터베이스 초기화

@RunWith(SpringRunner.class)
@DataJpaTest
public class AccountRepositoryTest {

    @Autowired
    DataSource dataSource;

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Autowired
    AccountRepository repository;

    @Test
    public void dt() throws SQLException {
        Account account = new Account();
        account.setUsername("jch");
        account.setPassword("pass");

        Account newAccount = repository.save(account);

        assertThat(newAccount).isNotNull();

        Optional<Account> existingAccount = repository.findByUsername(newAccount.getUsername());
        assertThat(existingAccount).isNotEmpty();

        Optional<Account> nonExistingAccount = repository.findByUsername("asdposda");
        assertThat(nonExistingAccount).isEmpty();
    }
}

테스트를 실행하면서 로그를 살펴보면 자동으로 스키마가 생성되는 것을 알 수 있다.

애플리케이션을 실행하면 어떨까? 아직 애플리케이션에 대해서는 아무런 설정도 하지 않은 상태이므로 스키마를 생성하는 쿼리를 수행하지 않는다. 이런 문제는 spring.jpa.generate-dll를 true로 설정한 상태에서 spring.jpa.hibernate.ddl-auto를 설정하면 해결할 수 있다. spring.jpa.hibernate.ddl-auto에 update, create, create-drop 중 하나를 설정해주면 자동으로 스키마가 생성된다.

개발환경

  • create-drop
    • 애플리키에션을 실행할 때 스키마를 생성하고 종료할 때 스키마를 삭제
    • 기존 데이터를 유지할 수 없음
  • create
    • 애플리키에션을 실행할 때 모든 스키마를 지우고 새로 생성
    • 기존 데이터를 유지할 수 없음
  • update
    • 기존의 스키마는 유지한 채 추가해야 하는 스키마만 추가
    • 기존 데이터를 유지할 수 있음
    • 개발할 때 주로 사용하는 설정

spring.jpa.show-sql를 true로 설정하면 쿼리로그를 볼 수 있다

운영환경

운영환경에서는 안정성을 위해 spring.jpa.generate-dll를 false로 설정한 상태에서 spring.jpa.hibernate.ddl-auto에 validate를 설정한다. validate는 현재 앤티티 매핑이 DB 릴레이션에 매핑이 되는지 검증만 하고 DLL에 변경을 가하지는 않는다.

앤티티 매핑을 변경한다면 어떻게 될까?

@Entity
public class Account {

    @Id
    @GeneratedValue
    private Long id;

    // username -> nickname
    private String nickname;

    private String password;

    // 추가
    private String email;

    // .... getter setter equals 등
}

Account 클래스에서 username을 nickname으로 변경하고 email 속성을 추가했다. 이 상태에서 애플리케이션을 실행하면 Schema-validation: missing column [email] in table [account] 와 같은 로그를 출력하면서 애플리케이션이 종료된다.

spring.jpa.hibernate.ddl-auto를 update로 설정한 뒤 다시 실행해보자. 하이버네이트는 username에서 nickname으로 바뀌었다는 사실을 알 수가 없기 때문에 nickname과 email을 추가한다.

SQL 스크립트를 사용한 데이터베이스 초기화

JPA를 사용하지 않아도 SQL 스크립트를 이용하여 데이터베이스를 초기화할 수 있다.

drop table account if exists
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start with 1 increment by 1
create table account ( id bigint not null, password varchar(255), username varchar(255) not null, email varchar(255), primary key (id) )

src/main/resources/schema.sql 에 데이터베이스 초기화를 위해 스키마를 삭제하고 생성하는 SQL 문을 저장한다.

spring.jpa.generate-dll=false
spring.jpa.hibernate.ddl-auto=validate

JPA에서 스키마를 생성하지 않도록 하고 테스트를 실행하더라도, 해당 파일에 지정된 쿼리가 수행되면서 스키마를 생성하기 때문에 테스트를 통과한다.

플랫폼 별 설정

  • 1순위: schema.sql 또는 schema-${platform}.sql
  • 2순위: data.sql 또는 data-${platform}.sql

${platform} 값은 spring.datasource.platform 프로퍼티즈에 설정할 수 있다.

보통 개발할 때는 hibernate.ddl-auto를 update로 설정한 상태에서 개발을 하나. 배포할 때가 되면 테스트 코드로 스키마를 생성하도록 정리한 뒤, 해당 쿼리를 복사해서 schema.sql에 저장하고 validate로 설정한다.

update로 설정하면 스키마가 굉장히 지저분해진다. 이름을 바꿨던 컬럼이 적용되지 않으므로 사용하지 않는 컬럼이 남아있다. 운영환경에서는 위험하기 때문에 추천하지 않는다.

해당 포스팅은 스프링 부트 개념과 활용 강의 내용을 토대로 작성하였습니다.