๋ฌธ์ ์ํฉ์ ๊ฐ๋จํ๊ฒ ๋ํ๋ด ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค..
TestEntity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class TestEntity {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "test_id")
private Long id;
private String name;
}
TestRepository
public interface TestRepository extends JpaRepository<TestEntity , Long> {
}
TestService
@Service
@RequiredArgsConstructor
@Transactional
public class TestService {
private final TestRepository testRepository;
public void test(){
testRepository.findAll().forEach( i -> i.setName("๋ฉ์ฒญ์ด"));
throw new IllegalStateException("์ธ์ฒต ์์ธ ๋ฐ์");
}
public void add() {
TestEntity test = new TestEntity();
test.setName("๋ฐ๋ณด");
testRepository.save(test);
}
}
๋ฌด์์ด ๋ฌธ์ ์ธ๊ฐ?
@SpringBootTest
@Transactional
class TestServiceTest {
@Autowired TestService testService;
@Autowired TestRepository testRepository;
@Autowired EntityManager em;
@Test
public void rollBackTest() throws Exception {
TestEntity test = new TestEntity();
test.setName("๋ฐ๋ณด");
TestEntity save = testRepository.save(test);
//'๋ฐ๋ณด' ๋ผ๋ TestEntity๊ฐ ์ ์ฅ
assertThrows(IllegalStateException.class, ()->testService.test());
//'๋ฐ๋ณด' ๋ฅผ '๋ฉ์ฒญ์ด'๋ก ๋ฐ๊พธ๋ ค๋๋ฐ RunTimeException์ด ๋ฐ์
em.flush();
em.clear();
final TestEntity test1 = testRepository.findById(save.getId()).get();
System.out.println(test1.getName());
//'๋ฉ์ฒญ์ด' ๊ฐ ์ถ๋ ฅ...ใ
}
}
์ด์ ์์:
ํ
์คํธ ํด๋์ค์ @Transactional ์ด๋
ธํ
์ด์
์ด ๋ถ์ด์์ผ๋ฏ๋ก rollBackTest()๊ฐ ํ๋์ ํธ๋์ญ์
์ผ๋ก ๊ด๋ฆฌ
์ฐ์ ์์์ฑ ์ปจํ
์คํธ์ '๋ฐ๋ณด' -> '๋ฉ์ฒญ์ด'๋ก ์ด๋ฆ์ด ๋ณ๊ฒฝ๋ ์ํ๋ก ์ ์ฅ
๊ทธ ์ํ์์ RuntimeException์ด ๋ฐ์ํ์ฌ Rollback ๋งํฌ๊ฐ ํ์๋ ํ, assertThrows์์ catch๋ก ์์ธ๋ฅผ ์ก์
์ด ์ํ์์๋ ์์ง ์์์ฑ ์ปจํ
์คํธ์๋ ๋ฉ์ฒญ์ด๋ก ์ด๋ฆ์ด ์ ์ฅ๋ ์ํ.
๊ทธ ์ํ์์ testRepository.findById(save.getId())๋ก ์กฐํํ์ฌ ์์์ฑ ์ปจํ
์คํธ์ ์กด์ฌํ๋ '๋ฉ์ฒญ์ด'๊ฐ ์กฐํ๋จ.
์ด๋ฅผ ์ถ๋ ฅํ ํ ๋ฉ์๋๊ฐ ๋๋ฌ์ผ๋ Rollback ๋งํฌ ํ์๊ฐ ๋์์ผ๋ฏ๋ก ๋กค๋ฐฑ์ฒ๋ฆฌ๋ฅผ ํจ.
๋ฐ๋ผ์ ์ค์ ๋ก๋ ๋กค๋ฐฑ์ด ๋์ง๋ง ํ ์คํธ์ฝ๋ ์์์๋ ์ด๋ฅผ ํ์ธํ ์๊ฐ ์์..
๋ฌธ์ : ์ด๋ฅผ ํ
์คํธ ์ฝ๋์์ ๋กค๋ฐฑ์ฒ๋ฆฌ๊ฐ ๋ ๊ฒ์ ํ์ธํ๊ธฐ ์ํด์๋ ์ด๋ป๊ฒ ํด์ผํ๋?
1.
assertThrows(IllegalStateException.class, ()->testService.test()); ์ดํ
em.flush();
em.clear();๋ก ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๋น์์ค ํ ์กฐํํ๊ธฐ
-> ์คํจ
์ด์ ๋ฅผ ์๊ฐํด๋ณด์๋ฉด ๋กค๋ฐฑ์ ์ ์ฒด ํธ๋์ญ์ ์ด ๋๋๋ ์๊ฐ ๋กค๋ฐฑ ๋งํฌ๋ฅผ ํ์ธํ๊ณ ๋กค๋ฐฑ์ํด.
๊ทธ๋ฌ๋ ์์ง ์ ์ฒด ํธ๋์ญ์ ์ด ๋๋์ง ์์์ผ๋ฏ๋ก em.flush๋ก ๋ฐ์ด๋ฒ๋ฆฌ๋ ์๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฟผ๋ฆฌ๊ฐ ๋ ๋ผ๊ฐ
๊ทธ๋์ ์ดํ clear()๋ฅผ ํ๋๋ผ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์๋ ๋ฉ์ฒญ์ด๋ก ์ ์ฅ๋์ด ์์(๋ฌผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ปค๋ฐ์ด๋ ๋กค๋ฐฑ์ ์์ง ํธ๋์ญ์ ์ด ์ข ๋ฃ๋์ง ์์์ผ๋ฏ๋ก ๋ฐ์ํ์ง ์์)
2.
assertThrows(IllegalStateException.class, ()->testService.test())๋ฅผ ์๋ก์ด ๋ฉ์๋๋ก ์ถ์ถํ ํ
@Transactional(propagation = Propagation.REQUIRES_NEW) ์ ์ ์ฉํ์ฌ ์๋ก์ด ํธ๋์ญ์
์์ ์คํ๋๋๋ก ํจ
-> ์คํจ (์ด์ : ๊ฐ์ ํด๋์ค(๋น)์ ๋ค๋ฅธ ๋ฉ์๋๋ฅผ ์คํํ๋ฉด ์ ์ฉ๋์ง ์์ - ์คํ๋ง AOP ํ๋ก์์ self invocation ๋ฌธ์ )
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ธ์ฒดํฌ ์์ธ๊ฐ ๋ฐ์ํ๋ ๋ฉ์๋๋ฅผ ์๋ก์ด ํด๋์ค์ ๋ฉ์๋๋ก ๋ง๋ ํ, @Transactional(propagation = Propagation.REQUIRES_NEW) ์ต์ ์ ์ฃผ๋ฉด ํด๊ฒฐ๋๊ธด ํจ.
์์๋ ๋ค์๊ณผ ๊ฐ์.
@Service
@RequiredArgsConstructor
@Transactional
public class TestExceptionService {
private final TestRepository testRepository;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void test(){
testRepository.findAll().forEach( i -> i.setName("๋ฉ์ฒญ์ด"));
throw new IllegalStateException("์ธ์ฒต ์์ธ ๋ฐ์");
}
}
๊ทธ๋ฌ๋ ํ ์คํธ์ฝ๋๋ฅผ ์ํด ๊ธฐ์กด ์ฝ๋๋ฅผ ๋ฐ๊พธ๋ ๊ฒ์ด ๋ง๋ ๊ฒ์ผ๊น..? ํ๋ ์๋ฌธ..
3.
TestService ์์ฒด์ @Transactional(propagation = Propagation.REQUIRES_NEW)๋ฅผ ์ ์ฉ
-> ์ฑ๊ณต
2๋ฒ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ํ
์คํธ์ฝ๋๋ฅผ ์ํด ๊ธฐ์กด ์ฝ๋๋ฅผ ๋ฐ๊พธ๋ ๊ฒ์ด ๋ง๋ ๊ฒ์ผ๊น..? ํ๋ ์๋ฌธ
4. ํ ์คํธ ํด๋์ค์ ์กด์ฌํ๋ @Transactional์ ์ ๊ฑฐ
-> EntityManager๋ฅผ ์ฌ์ฉํ ์ ์์ด์ ์คํจ
์ํ๋ ํด๊ฒฐ๋ฐฉ๋ฒ:
ํ
์คํธ์ฝ๋ ๋ด๋ถ์์ ๊ฐ๊ฐ์ ํธ๋์ญ์
์ ๊ฐ๋๋ก ๋ง๋ค ์ ์๋ ๋ฐฉ๋ฒ์ด ์์๊น...?
์ฐ์ ํด๋์ค ๋จ์ @Transactional ์ ์ฌ์ฉํ์๊ธฐ์ ์๋ ๊ฑฐ๊ฐ๋ค..
๋ง์ฝ ํด๋์ค ๋จ์ @Transactional ์ ์ ๊ฑฐํ๋ค๋ฉด? -> EntityManager ๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋จ.
๊ทธ๋ฐ๋ฐ ํ ์คํธ ์ฝ๋๊ฐ ์๋ ์ค์ ๋ก ์๋์์ผ๋ณธ๋ค๋ฉด?
@RestController
@RequiredArgsConstructor
public class TestController {
private final TestService testService;
private final TestRepository testRepository;
@GetMapping("/test")
public void test(){
testService.test();
//๋ฐ๋ณด๋ผ๋ ์ฌ๋์ ์ด๋ฆ์ @@@@@๋ก ๋ฐ๊พธ๋ ค ํ์ผ๋ RuntimeException์ด ๋ฐ์ํ์ฌ ๋กค๋ฐฑ์ฒ๋ฆฌ.
//๊ฒฐ๊ณผ๋ ๋ฐ๋์ง ์๊ณ ๋ฐ๋ณด๋ก ๋จ์์์
}
@GetMapping("/test/add")
public void add(){
testService.add();
//๋ฐ๋ณด ๋ผ๋ ์ด๋ฆ์ TestEntity๋ฅผ DB์ ์ ์ฅ
}
}
๋จผ์ /test/add ๋ก ์์ฒญ์ ๋ณด๋ด๋ฉด '๋ฐ๋ณด'๊ฐ ์ ์ฅ
์ดํ /test ๋ก ์์ฒญ์ ๋ณด๋ด๋ฉด '๋ฉ์ฒญ์ด'๋ก ๋ฐ๊ฟจ์ผ๋ ๋ฐํ์ ์์ธ๊ฐ ๋ฐ์ํ์ฌ ๋กค๋ฐฑ -> ๋๋น์๋ '๋ฐ๋ณด'๊ฐ ๋จ์์์
๊ฒฐ๋ก
๋ฏฟ์์ผ๋ก ๊ฐ์!!!!!!!!!!!!!!!!!
+ ๊ฐ์ ํด๋์ค ๋ด์์ ์๋ก ๋ค๋ฅธ @Transactional ์์ฑ์ ๊ฐ์ง ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค๋ฉด ๋ถ๋ชจ @Transactional์ ์์ฑ์ ๋ฐ๋ผ๊ฐ๋ค. ์ฆ ๋ค๋ฅธ ์์ฑ์ ํธ์ถํ๋๋ผ๋ ์๋ฌด ์์ฉ์ด ์๋ค๋ ๋ป!
โจ 2023 08 15 ํด๊ฒฐ๋ฐฉ๋ฒ ์ถ๊ฐ
@FunctionalInterface
public interface TransactionalAssert {
void execute();
}
@Component
public class TransactionHelper {
@Transactional(readOnly = true)
public void doAssert(final TransactionalAssert transactionalAssert) {
transactionalAssert.execute();
}
}
@SpringBootTest
// @Transactional ์ ๊ฑฐ
class TestServiceTest {
@Autowired TestService testService;
@Autowired TestRepository testRepository;
@Autowired TransactionHelper helper;
@Test
public void rollBackTest() throws Exception {
TestEntity test = new TestEntity();
test.setName("๋ฐ๋ณด");
TestEntity save = testRepository.save(test);
//'๋ฐ๋ณด' ๋ผ๋ TestEntity๊ฐ ์ ์ฅ
assertThrows(IllegalStateException.class, ()->testService.test());
helper.doAssert(() -> {
final TestEntity test1 = testRepository.findById(save.getId()).get();
System.out.println(test1.getName());
});
}
}