[Project] NonUniqueResultException ๋ฐ์
Categories: Project
๐ ๊ฐ์ธ์ ์ธ ๊ณต๊ฐ์ผ๋ก ๊ณต๋ถ๋ฅผ ๊ธฐ๋กํ๊ณ ๋ณต์ตํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ๋ธ๋ก๊ทธ์
๋๋ค.
์ ํํ์ง ์์ ์ ๋ณด๊ฐ ์์ ์ ์์ผ๋ ์ฐธ๊ณ ๋ฐ๋๋๋ค :๐ธ
[ํ๋ฆฐ ๋ด์ฉ์ ๋๊ธ๋ก ๋จ๊ฒจ์ฃผ์๋ฉด ๋ณต๋ฐ์ผ์ค๊ฑฐ์์]
์ค๋ OrderService ๋ถ๋ถ ๋ง๋ฌด๋ฆฌํ๋ ค๊ณ ํ
์คํธํด๋ณด๋ค๊ฐ ๋จธ๋ฆฌ๊ฐ ๋ตํด์ก๋ค. ์๋ฌ ๋ฉ์์ง ๋ณด๋๋ฐ javax.persistence.NonUniqueResultException: query did not return a unique result: 2
๋ผ๋ ์๋ฌ๊ฐ ๋ ์ ๋๋์ฒด ์ด๊ฒ ๋ญ๊ฐ ์ถ์๋ค.
์๋ Order ๊ด๋ จ ๋ถ๋ถ์ ์ ๋ถ ๋ด๊ฐ ๊ฑด๋๋ฆฌ๋ค๊ฐ, ํ๋ก ํธ์์ item์ชฝ์ ๊ธํ๊ฒ ์๋ด๋ฌ๋ผ๊ณ ํด์
ํ๋ ๋์ค์ Order์ ๊ธฐ๋ณธ ํค๊ฐ ์๋ ์ฃผ๋ฌธ๋ฒํธ๊ฐ ๋ณ๋๋ก ํ์ํ ๊ฒ ๊ฐ๋ค๊ณ ํ๋ค
item์ด ์๊ฐ๋ณด๋ค ์ค๋๊ฑธ๋ ค์ ๋ค๋ฅธ ํ์์๊ฒ ์ํด๋ฅผ ๊ตฌํ๊ณ
์ฃผ๋ฌธ์ฝ๋๋ฅผ ์์ฑ ํ์ Post ์์ฒญ์ด ๋ค์ด์ค๋ฉด ์๋น์ค์ธต์์ ์ฝ๋ ์์ฑ ํ set ํด์ฃผ๊ธฐ๋ก ํ๋๋ฐ
์ฝ๋๋ ์ ๋ง๋ค์ด์ก๋ค๊ณ ํ๋๋ฐ post ์์ฒญ์ ์ฌ๋ฌ ๋ฒ ํ๋ฉด 500 ์์ธ๊ฐ ๋ฐ์ํ๋ค๊ณ ํด์
์ ์๋ ๊ทธ๋ฐ์ ์ด ์์๋๋ฐ ํ๊ณ ์ด์ ๋ค์ Order ์ชฝ refactoring ํ test ํด๋ณด๋ NonUniqueResultException์ด ๋ฐ์ํ๋ค
Postman์ผ๋ก ์ฐ์ํด์ ๋ ๋ฒ์ POST ์์ฒญ์ ๋ณด๋๋๋ฐ, ์๊พธ ์๋ฌ๊ฐ ํฐ์ง๊ธธ๋
ํฌ๊ฒ ๋ฌ๋ผ์ง ์ ์ด ์ฃผ๋ฌธ๋ฒํธ๋ฅผ ์์ฑํ๋ ๋ถ๋ถ์ด๋ผ์ ์ค๋ณต์ด ์ ๋ฐ์ํ์ง ํ๊ณ ์ฃผ๋ฌธ๋ฒํธ ์์ฑํ๋ ๋ถ๋ถ์ ์ฃผ์ ์ฒ๋ฆฌํด๋ณด๋๊น ์์ธ๊ฐ ์ ํฐ์ก๋ค.
์ด๊ฑธ ๋ณด๊ณ ๋ฌธ์ ๋ ํ์คํ ์ฃผ๋ฌธ๋ฒํธ ์์ฑ ๋ถ๋ถ์ ์๋ค๊ณ ์๊ฐํ๊ฒ ๋๋ค.
?
๋ฌธ์ ์ ์์ธ
์ญ ์ฝ์ด๋ณด๋ findTopByOrderCdStartingWithOrderByOrderCdDesc(date)
์ด ์ฟผ๋ฆฌ๊ฐ ์๋ ํ๋์ ๊ฒฐ๊ณผ๋ง ๋ฐํํด์ผ ํ๋๋ฐ, ๋ ๊ฐ ์ด์์ ๊ฒฐ๊ณผ๊ฐ ๋ฐํ๋๋ค๋ ๊ฒ ๊ฐ์๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// ์ฃผ๋ฌธ ์ฝ๋ ์์ฑ ๋ฉ์๋
private String createOrderCd() {
// ํ์ฌ ๋ ์ง๋ฅผ "yyMMMdd" ํ์์ผ๋ก ๋ณํ (MMM์ ์์ ์์ด ์ฝ์)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyMMMdd", Locale.ENGLISH);
String date = LocalDate.now().format(formatter).toUpperCase();
// ํ์ฌ ๋ ์ง์ ํด๋นํ๋ ๋ง์ง๋ง ์ฃผ๋ฌธ ์ฝ๋๋ฅผ DB์์ ์กฐํ
Optional<String> lastOrderCdOptional = orderHeadersRepository.findTopByOrderCdStartingWithOrderByOrderCdDesc(date);
// ๋ง์ง๋ง ์ฃผ๋ฌธ ์ฝ๋๊ฐ ์์ผ๋ฉด 00001๋ถํฐ ์์
int newOrderNumber = 1;
if (lastOrderCdOptional.isPresent()) {
String lastOrderCd = lastOrderCdOptional.get();
// ๋ง์ง๋ง ์ฃผ๋ฌธ ์ฝ๋์์ ๋ฒํธ ๋ถ๋ถ ์ถ์ถ (์: "24DEC1100001" -> 00001 ์ถ์ถ)
String lastOrderNumberStr = lastOrderCd.substring(7); // ๋ ์ง(6์๋ฆฌ) ์ดํ ์ซ์(5์๋ฆฌ) ๋ถ๋ถ ์ถ์ถ
int lastOrderNumber = Integer.parseInt(lastOrderNumberStr);
// ์๋ก์ด ์ฃผ๋ฌธ ๋ฒํธ๋ ๋ง์ง๋ง ๋ฒํธ + 1
newOrderNumber = lastOrderNumber + 1;
}
// ์๋ก์ด ์ฃผ๋ฌธ ์ฝ๋ ์์ฑ (์: 24DEC1100002)
String newOrderCd = String.format("%s%05d", date, newOrderNumber);
return newOrderCd;
}
์ฃผ๋ฌธ๋ฒํธ๋ฅผ ์์ฑํ ๋, ๊ฐ์ ๋ ์ ์ฌ๋ฌ ์ฃผ๋ฌธ์ด ๋ค์ด์ค๋ฉด ๊ทธ๋ ๋ ์ง๋ก ์์ํ๋ ์ฃผ๋ฌธ๋ฒํธ๋ค์ด DB์ ์ฌ๋ฌ ๊ฐ ์์ด๊ฒ ๋๋ค. ๊ทผ๋ฐ ์ฌ๊ธฐ์ findTopByOrderCdStartingWithOrderByOrderCdDesc(date)
๊ฐ ๊ทธ ๋ ์ง๋ก ์์ํ๋ ๊ฐ์ฅ ์ต๊ทผ์ ์ฃผ๋ฌธ๋ฒํธ๋ฅผ ์ฐพ๋ ์ฟผ๋ฆฌ๋ค ๋ณด๋, ๊ฐ์ ๋ ์ง์ ์ค๋ณต๋ ๊ฒฐ๊ณผ๊ฐ ์ฌ๋ฌ ๊ฐ ๋์ฌ ์ ์๋ ์ํฉ์ด์๋ค.
๊ฒฐ๊ตญ ์ฟผ๋ฆฌ๊ฐ ์ฌ๋ฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๊ณ , ๊ทธ๊ฑธ ํ๋๋ก ๋ฌถ์ง ๋ชปํด์ ์๋ฌ๊ฐ ๋ฐ์ํ ๊ฒ ๊ฐ๋ค.
ํด๊ฒฐ ๋ฐฉ๋ฒ
- ์ฃผ๋ฌธ๋ฒํธ ์์ฑํ ๋ ์ค๋ณต์ ํผํ๊ธฐ ์ํด ์ฟผ๋ฆฌ๋ถํฐ ์์ ํ์
findTopByOrderCdStartingWithOrderByOrderCdDesc()
์ด ๋ถ๋ถ์์ ๊ฐ์ฅ ์ต๊ทผ ํ๋์ ์ฃผ๋ฌธ๋ฒํธ๋ง ์ ํํ๊ฒ ๋ฐํํ๋๋ก ์ฟผ๋ฆฌ ์กฐ๊ฑด์ ์์
- ๋์์ฑ ๋ฌธ์ ๋ ๊ณ ๋ ค
- ๋ด๊ฐ ์ฐ์์ผ๋ก ๋ ๋ฒ POST ์์ฒญ์ ๋ณด๋ด๋ ์ํฉ์ด๋ผ๋ฉด ๋์์ ์ฌ๋ฌ ์ฃผ๋ฌธ์ด ๋ค์ด์ฌ ์ ์๊ธฐ ๋๋ฌธ์, ์ฃผ๋ฌธ๋ฒํธ ์์ฑ ๋ก์ง์ ๋๊ธฐํ๋ฅผ ์ถ๊ฐํ๊ฑฐ๋,
- DB์์ ์์ฐจ์ ์ผ๋ก ์ฃผ๋ฌธ๋ฒํธ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ๋ ํ์
- ์๋ฅผ ๋ค์ด,
synchronized
ํค์๋๋ฅผ ์ฌ์ฉํด์ ์ฃผ๋ฌธ๋ฒํธ ์์ฑํ ๋ ํ ๋ฒ์ ํ๋์ ์ฐ๋ ๋๋ง ์ ๊ทผํ ์ ์๋๋ก ํ๊ฑฐ๋, - DB์์ ๊ณ ์ ํ ์ฃผ๋ฌธ๋ฒํธ๋ฅผ ๊ด๋ฆฌํ๋ ํ
์ด๋ธ์ ๋ฐ๋ก ๋ง๋ค์ด์ ๊ทธ๊ฑธ ์ฐธ์กฐํ๋ ๋ฐฉ๋ฒ๋ ๊ณ ๋ คํ ์ ์์ ๊ฒ ๊ฐ๋ค.
- ์๋ฅผ ๋ค์ด,
1 2 3
private synchronized String createOrderCd() { }
-
์ด๋ ๊ฒ synchronized๋ฅผ ๋ถ์ด๋ฉด ํด๋น ๋ฉ์๋์ ํ๋์ ์ค๋ ๋๋ง ์ ๊ทผํ ์ ์๊ธฐ ๋๋ฌธ์ ๋์์ฑ์ ๋ง์ ์ ์๋ค๊ณ ํ๋ค.
-
Synchronized
- ์๋ฐ์์ ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์ ๋์ ์ ๊ทผ์ ์ ์ดํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ํค์๋
- ํด๋น ๋ฉ์๋๋ ๋ธ๋ก์ ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ ์ ๊ทผํ์ง ๋ชปํ๋๋ก ํด์ ๋์์ฑ ๋ฌธ์ ๋ฅผ ๋ฐฉ์ง
- ๋ด๊ฐ ๊ฐ์ ํ ๋ฐฉ๋ฒ
- ์์ ๋ฐฉ๋ฒ์ ๊ณ ์งํ๋ ค๊ณ ํ๋๊น ์์งํ ์ฃผ๋ฌธ๋ฒํธ ์์ฑํ๋๋ฐ DB๋ ์กฐํํ๋ ๋ฑ๋ฑ์ ๋น์ฉ์ด ๋๋ฌด ๋ง์ด ๋ ๋ค๋ ์๊ฐ์ด๋ค์ด ๊ทธ๋ฅ ๊ฐ๋จํ๊ฒ ๊ตฌํํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ค.
- ๋๋ค ์์ฑํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ฐพ์๋๋ randonUUID๊ฐ ๊ฑฐ์ ์ค๋ณต์ด ์๋ค๊ณ ํด์ ์ด ๋ฐฉ๋ฒ์ผ๋ก ๊ฒฐ์ ํ๊ณ
- ๊ธฐ์กด์ ์ฃผ๋ฌธ์ฝ๋ ์์ฑ ๋ฐฉ๋ฒ์ฒ๋ผ ํ์ฌ์ ์์ฑ๊ท์น์
1 2 3 4 5 6
// ์ฃผ๋ฌธ ์ฝ๋ ์์ฑ ๋ฉ์๋ private String createOrderCd() { String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 12).toUpperCase(); return "SHO" + uuid; }
๋จ์ ์ฃผ๋ฌธ์ฝ๋ ์์ฑ์ธ๋ฐ ์ฒ์ ๋ณด๋ ์์ธ ๋ฐ์ ๋๋ฌธ์ ์๊ฐ์ด ์ฌ๋ฅด๋ฅด๋ฅต ๋ น์๋ฒ๋ ธ๋ค. ์ฃผ๋ฌธ์ด ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ๊ฐ ์ด๋ ต๋ค๊ณ ํ๋๋ฐ ๋ฒ์จ ์ฝ๋ ํ๋ ์์ฑํ๋ ๊ฒ์์ ๋ง๋์ ๋นํฉ์ค๋ฝ๋ค..
Leave a comment