Unique index or primary key violation 오류를 만났습니다.
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Unique index or primary key violation:
위와 같은 오류가 발생했습니다.
DB에 데이터를 INSERT
할 때, PRIMARY KEY
나 UNIQUE KEY
가 중복되어 발생한 오류입니다.
data.sql
을 사용해서 미리 데이터를 DB에 넣어둠.save()
메서드를 사용)save()
가 실행되면 위와 같은 문제가 발생했습니다.
기본키 생성 방법은 아래와 같이 설정했었습니다.
@Entity
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
스프링 부트에서는, 테스트 코드를 실행할 때 별도로 설정해둔 것이 없다면, 테스트용 H2 DB를 생성해서 사용합니다. H2 DB에는 AUTO_INCREMENT
기능이 없으므로, GenerationType.IDENTITY
를 추가하더라도 MySQL처럼 AUTO_INCREMENT
가 적용되지 않습니다.
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.8)
...
2023-02-13 22:23:20.197 INFO 33132 --- [ main] o.s.j.d.e.EmbeddedDatabaseFactory : Starting embedded database: url='jdbc:h2:mem:bf5b6e37-55ee-4cb0-892a-486078b8804c;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
...
2023-02-13 22:23:20.254 INFO 28176 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
...
위는 테스트 코드를 동작시켰을 때, 기록되는 로그의 일부입니다.
database: url='jdbc:h2:mem:...
이라고 출력되는 것을 확인할 수 있습니다.
또한 dialect: org.hibernate.dialect.H2Dialect
을 보면, H2 DB가 사용됨을 확인할 수 있습니다.
해결 방법에는 세가지가 있습니다.
data.sql
을 사용하고 계시다면, 해당 SQL에 id 필드를 제거해서 문제를 해결할 수 있습니다.
아래는 data.sql
의 일부 내용입니다.
변경 전
insert into Article (id, title, content, hashtag, created_at, created_by, modified_at, modified_by) values (1, 'Cold Prey II (Fritt Vilt II)', 'Nam ultrices', 'ipsum primis in faucibus orci luctus et ultrices', '2019-08-28 07:39:08', 'mesel0', '2022-03-22 14:11:38', 'clampl0');
변경 후
insert into Article (title, content, hashtag, created_at, created_by, modified_at, modified_by) values ('Cold Prey II (Fritt Vilt II)', 'Nam ultrices', 'ipsum primis in faucibus orci luctus et ultrices', '2019-08-28 07:39:08', 'mesel0', '2022-03-22 14:11:38', 'clampl0');
위와 같이, INSERT
문에 ID
필드를 제거했습니다.
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 추가!!
@DataJpaTest
class JpaRepositoryTest {
...
}
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
을 설정하면 메모리 DB를 자동 생성하지 않고, application.yaml
혹은 application.properties
에 있는 설정이 적용됩니다.
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.8)
...
2023-02-13 22:29:47.373 INFO 4456 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect1
실행해보면, 위와 같이 dialect: org.hibernate.dialect.MySQL8Dialect1
. 즉, MySQL이 연결에 적용되는 것을 획안할 수 있습니다.
# application.yaml
spring:
config.activate.on-profile: testdb # 프로파일 이름 등록
datasource:
url: jdbc:h2:mem:board;mode=mysql # 핵심!
driver-class-name: org.h2.Driver
@ActiveProfiles("testdb") # testdb라는 이름을 가진 프로파일을 적용
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@DataJpaTest
class JpaRepositoryTest {
...
}
datasource.url:jdbc:h2:mem:board;mode=mysql
을 설정해줍니다.
board
라고 적은 부분에는 본인이 원하는 글자를 적으면 됩니다.
H2 DB의 MySQL모드는 AUTO_INCREMENT
를 지원합니다.
(참고: http://www.h2database.com/html/features.html#compatibility)
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.8)
...
2023-02-13 22:47:26.540 INFO 33508 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
실행시켜보면, 위와 같이 H2 DB가 적용되지만 오류가 발생하지 않습니다.
@ActiveProfiles("testdb")
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
위 두개를 매번 적어주기가 귀찮고 번잡합니다.
spring:
config.activate.on-profile: testdb
datasource:
url: jdbc:h2:mem:board;mode=mysql
driver-class-name: org.h2.Driver
test.database.replace: none # 추가!!
application.yaml에 위 추가 필드를 적습니다.
그러면, @AutoConfigureTestDatabase
어노테이션은 생략할 수 있습니다.
단, @AutoConfigureTestDatabase
혹은 test.database.replace:
둘중 하나는 설정해야 합니다. 그렇지 않으면 자동 생성 메모리 DB를 만들어버려서 앞서 해결한 오류가 계속 발생합니다.
댓글에 답 좀 달아주세요🥺