월 19,800원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
internalCall() 실행 질문입니다.
@Test void internalCall(){ callService.internal(); } static class CallService{ @Transactional public void internal(){ log.info("call internal"); printTxInfo(); } private void printTxInfo(){ boolean txActive = TransactionSynchronizationManager.isActualTransactionActive(); log.info("tx active={}", txActive); } }강의 pdf에서 트랜잭션 적용 후 실제 callService 객체 인스턴스의 internal() 을 호출한다 라고 되있는데 언제 실제 internal()을 호출하는 지 잘 모르겠습니다.트랜잭션 시작 - printTxInfo()(실제 callService 객체 인스턴스의 internal() 을 호출) - 트랜잭션 종료 라고 생각이 드는데 맞게 생각한건지 알려주시면 감사하겠습니다.
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
트랜잭션 동기화 매니저 작동 원리
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]내부 트랜잭션의 경우, 트랜잭션 동기화 매니저에서 con이 있으면, 생성하지 않고 그걸 가져다가 사용한다고 배웠습니다. 그림에는 단순하게 보여주기 위해 트랜잭션 동기화 매니저에 con이 하나만 있는 경우 인데, 실제로는 여러개가 있을텐데 그중에 아무거나 가져다 쓰나요?? 아니면 내부 트랜잭션을 호출한 외부 트랜잭션의 con을 찾아서 쓰려나요??
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
Invalid bound statement 에러
안녕하세요.MyBatis 적용 예제를 테스트하던 중 아래와 같은 에러가 발생해 질문드립니다. test와 application 모두 에러가 발생합니다. 다음과 같은 방법들을 시도해봤습니다.오타 수정 : 오타 문제인가 싶어 강사님의 자료를 그대로 복사해서 사용했습니다.패키지 구조 확인 : Mapper 파일이 있는 경로와 xml 파일의 경로도 일치하니다.버전 수정 : mybatis의 버전이 안맞나 싶어 2.3.1로 수정도 해봤습니다. 현재 스프링부트 버전은 2.6.5, mybatis 버전은 2.2.0입니다.gradle 재빌드캐시 삭제 제가 놓친 부분이 있는지 확인 부탁드립니다. 감사합니다!
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
RestAssured를 사용한 테스트에서는 Transactional이 동작하지 않는데 어떻게 데이터를 롤백할 수 있을까요?
RestAssured를 사용한 테스트에서는 Transactional이 동작하지 않는데 어떻게 데이터를 롤백할 수 있을까요? 현재 사용하고 있는 방법은 테스트 데이터베이스와 프로덕션 데이터베이스를 확실히 분리하고각 테스트 메서드 실행 이전, 혹은 이후에 'DELETE FROM item'과 같이 테이블의 모든 데이터를 삭제하는 쿼리를 날리는 방식을 사용하고 있습니다. 질문드리고 싶은 것은RestAssured를 사용하는 경우 @Transactional이 잘 작동하지 않는 이유에 대해 찾아봤지만 잘 이해가 되지 않습니다.위에서 제시한 방법 외에 더 좋은 방법은 없을까요..? (참고: 위에 설명드린 방법은 아래 다른 분의 포스팅에 자세히 나와 있더라고요..!)https://mangkyu.tistory.com/264
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
스프링부트 임베디드 모드 테스트 시 커밋
안녕하세요 강사님좋은 강의 감사드리고 해당 질문이 다소 답답하게 느껴지실 수도 있을 것 같아 죄송하지만 강의 실습하는 도중 궁금한 점이 생겨 질문 남깁니다.테스트할 때 임베디드 모드 DB를 사용하게 되면@Commit 어노테이션을 달더라도 실제 db 테이블에 데이터가 저장되지는 않더라고요실제 db가 아닌 임베이드 db를 사용해서 그런 것이라면 임베디드 모드 db를 사용할 때는 애플리케이션이 종료되면 임베디드 모드로 동작하는 H2 데이터베이스도 함께 종료되고, 데이터도 모두 사라진다고 강의 자료에도 써있는데 그러면 @Commit 어노테이션이 무의미하게 되는 것인가요?제가 아래의 동일한 테스트를 여러번 돌릴 때마다 DB데이터가 다 사라지는 것 같더라고요(테스트 할 때는 로그로 데이터 개수를 찍어봤습니다)그럼 각 테스트 단위마다 새로운 임베디드 모드의 DB가 종료되고 다시 새롭게 생성되는 건가요?확인 후 답변 주시면 정말 감사드리겠습니다..!!
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
Item find 관련한 쿼리 메소드 작성에 대한 질문 겸 참고사항입니다!
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]>> 로컬 환경윈도우 10hibernate-core:6.2.5.Finalspring boot:3.0.1아이템 조회하는 부분에서 강의와 똑같이 오류가 발생했었는데요. 지금 버전에서는 findByItemName 혹은 findByItemNameContaining 모두 정상 작동합니다.다른 부분에서 오류가 발생해서 찾아보니까 findItems에 작성된 JPQL에서 like 뒤에 변수 기입할 때 %를 넣지 않아서 그런 거더라고요. 넣으니 테스트 통과합니다 :-) @Query("select i from Item i where i.itemName like %:itemName% and i.price <= :price") List<Item> findItems(@Param("itemName") String itemName, @Param("price") Integer price);저만 겪은 오류인가 싶기도 한데 혹시 몰라서 공유해봅니다. 감사합니다.ps. 제가 겪은 에러는 문법 오류라고 알려주지 않는 것 같네요. 어렵습니다ㅠ12:17:43.066 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class [hello.itemservice.domain.ItemRepositoryTest]: ItemRepositoryTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration. 12:17:43.135 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @SpringBootConfiguration hello.itemservice.ItemServiceApplication for test class hello.itemservice.domain.ItemRepositoryTest . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.1.1) 2023-07-04T12:17:43.419+09:00 INFO 26524 --- [ main] h.itemservice.domain.ItemRepositoryTest : Starting ItemRepositoryTest using Java 17.0.7 with PID 26524 (started by taetae in D:\source codes\intellij\itemservice-db) 2023-07-04T12:17:43.420+09:00 INFO 26524 --- [ main] h.itemservice.domain.ItemRepositoryTest : The following 1 profile is active: "test" 2023-07-04T12:17:43.813+09:00 INFO 26524 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 2023-07-04T12:17:43.858+09:00 INFO 26524 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 37 ms. Found 1 JPA repository interfaces. 2023-07-04T12:17:44.197+09:00 DEBUG 26524 --- [ main] o.s.jdbc.datasource.DataSourceUtils : Fetching JDBC Connection from DataSource 2023-07-04T12:17:44.197+09:00 INFO 26524 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2023-07-04T12:17:44.323+09:00 INFO 26524 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:be66698a-0e22-4c39-868d-4400050c76bd user=SA 2023-07-04T12:17:44.324+09:00 INFO 26524 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2023-07-04T12:17:44.336+09:00 DEBUG 26524 --- [ main] o.s.jdbc.datasource.DataSourceUtils : Fetching JDBC Connection from DataSource 2023-07-04T12:17:44.337+09:00 DEBUG 26524 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : Executing SQL script from file [D:\source codes\intellij\itemservice-db\out\test\resources\schema.sql] 2023-07-04T12:17:44.340+09:00 DEBUG 26524 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : 0 returned as update count for SQL: drop table if exists item CASCADE 2023-07-04T12:17:44.346+09:00 DEBUG 26524 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : 0 returned as update count for SQL: create table item ( id bigint generated by default as identity, item_name varchar(10), price integer, quantity integer, primary key (id) ) 2023-07-04T12:17:44.347+09:00 DEBUG 26524 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : Executed SQL script from file [D:\source codes\intellij\itemservice-db\out\test\resources\schema.sql] in 9 ms. 2023-07-04T12:17:44.374+09:00 DEBUG 26524 --- [ main] o.s.jdbc.datasource.DataSourceUtils : Fetching JDBC Connection from DataSource 2023-07-04T12:17:44.396+09:00 INFO 26524 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 2023-07-04T12:17:44.437+09:00 INFO 26524 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.2.5.Final 2023-07-04T12:17:44.440+09:00 INFO 26524 --- [ main] org.hibernate.cfg.Environment : HHH000406: Using bytecode reflection optimizer 2023-07-04T12:17:44.534+09:00 INFO 26524 --- [ main] o.h.b.i.BytecodeProviderInitiator : HHH000021: Bytecode provider name : bytebuddy 2023-07-04T12:17:44.638+09:00 INFO 26524 --- [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer 2023-07-04T12:17:44.813+09:00 INFO 26524 --- [ main] o.h.b.i.BytecodeProviderInitiator : HHH000021: Bytecode provider name : bytebuddy 2023-07-04T12:17:45.187+09:00 INFO 26524 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform] 2023-07-04T12:17:45.197+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : drop table if exists item cascade 2023-07-04T12:17:45.200+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : create table item (price integer, quantity integer, id bigint generated by default as identity, item_name varchar(10), primary key (id)) 2023-07-04T12:17:45.203+09:00 INFO 26524 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2023-07-04T12:17:45.369+09:00 WARN 26524 --- [ main] ocalVariableTableParameterNameDiscoverer : Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: hello.itemservice.repository.jpa.SpringDataJpaItemRepository 2023-07-04T12:17:45.426+09:00 INFO 26524 --- [ main] o.s.d.j.r.query.QueryEnhancerFactory : Hibernate is in classpath; If applicable, HQL parser will be used. 2023-07-04T12:17:45.747+09:00 WARN 26524 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning 2023-07-04T12:17:45.875+09:00 INFO 26524 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page: class path resource [static/index.html] 2023-07-04T12:17:46.137+09:00 INFO 26524 --- [ main] h.itemservice.domain.ItemRepositoryTest : Started ItemRepositoryTest in 2.883 seconds (process running for 3.558) OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended 2023-07-04T12:17:46.518+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : insert into item (item_name,price,quantity,id) values (?,?,?,default) 2023-07-04T12:17:46.543+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : insert into item (item_name,price,quantity,id) values (?,?,?,default) 2023-07-04T12:17:46.544+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : insert into item (item_name,price,quantity,id) values (?,?,?,default) 2023-07-04T12:17:46.594+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 2023-07-04T12:17:46.633+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 2023-07-04T12:17:46.645+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 where i1_0.item_name like ? escape '\' 2023-07-04T12:17:46.648+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 where i1_0.item_name like ? escape '\' 2023-07-04T12:17:46.649+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 where i1_0.item_name like ? escape '\' 2023-07-04T12:17:46.651+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 where i1_0.price<=? 2023-07-04T12:17:46.656+09:00 DEBUG 26524 --- [ main] org.hibernate.SQL : select i1_0.id,i1_0.item_name,i1_0.price,i1_0.quantity from item i1_0 where i1_0.item_name like ? escape '' and i1_0.price<=? org.opentest4j.AssertionFailedError: Expecting actual: [] to contain exactly (and in same order): [Item(id=1, itemName=itemA-1, price=10000, quantity=10)] but could not find the following elements: [Item(id=1, itemName=itemA-1, price=10000, quantity=10)] Expected :[Item(id=1, itemName=itemA-1, price=10000, quantity=10)] Actual :[] <Click to see difference> at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) at hello.itemservice.domain.ItemRepositoryTest.test(ItemRepositoryTest.java:107) at hello.itemservice.domain.ItemRepositoryTest.findItems(ItemRepositoryTest.java:102) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57) at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38) at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55) 2023-07-04T12:17:46.678+09:00 INFO 26524 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 2023-07-04T12:17:46.678+09:00 DEBUG 26524 --- [ionShutdownHook] org.hibernate.SQL : drop table if exists item cascade 2023-07-04T12:17:46.680+09:00 INFO 26524 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 2023-07-04T12:17:46.681+09:00 INFO 26524 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. Process finished with exit code -1
- 해결됨스프링 DB 2편 - 데이터 접근 활용 기술
트랜잭션 사용 전 후 / 수동 오토 커밋
1. 이전 강의에서는 트랜잭션을 사용하지 않아 오토커밋이 되었고, 이번 강의에서는 트랜잭션을 사용해서 수동커밋이 된것으로 이해했는데 맞을까요?2. 수동커밋이 맞다면, 수동 커밋이 되어서 롤백을 할 수 있는 건가요?3. 이전 강의 까지는 트랜잭션을 사용하지 않고 DB 커넥션만 사용했다고 이해하면 되는 걸까요?4. 제가 테스트 해보고 싶었던 것은 '스프링 DB 1편'에 있는 '트랜잭션 - DB 예제3 - 트랜잭션 실습'처럼 수동 커밋의 경우 한 세션에서는 데이터가 들어간것이 보이지만 다른 한 세션에서는 안 보이는 것을 테스트 해보고 싶었습니다.그래서 랜잭션 롤백 부분 코드( transactionManager.rollback(status); )를 주석처리 하였고, H2 DB를 2개 띄웠습니다. 이 상태에서 save(), updateItem(), findItems()를 각각 실행해서 확인해 보면 두 세션 모두 데이터가 보이는 것을 확인할 수 있었습니다.커밋을 안했고 롤백도 안했는데 둘다 데이터가 보이는 걸까요?
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
구현 클래스 없어도 CRUD의 기능을 사용할수 있디는 말의 의미
강의 에서 JpaRepository 인터페이스만 상속받으면 스프링 데이터 JPA가 프록시 기술을 사용해서 구현 클래스를 만들어준다. 그리고 만든 구현 클래스의 인스턴스를 만들어서 스프링 빈으로 등록한다. 따라서 개발자는 구현 클래스 없이 인터페이스만 만들면 기본 CRUD 기능을 사용할 수 있다.이 글의 뜻은 public class JpaItemRepositoryV2 implements ItemRepository { private final SpringDataJpaItemRepository repository; @Override public Optional<Item> findById(Long id) { return repository.findById(id);}}위 코드에서 findById의 구현 클래스가 없어도 인터페이스(SpringDataJpaItemRepository )만 만들면 기본 CRUD 기능을 사용할 수 있다. 라고 이해하면 될까요??
- 해결됨스프링 DB 2편 - 데이터 접근 활용 기술
트랜잭션
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]안녕하세요. 강의 들으면서 혼자 이것저것 만들어보면서 이해가 안가는부분이있어서 관련 강의에 질문남깁니다.스프링 aop를 사용해서 로깅 기능을 만들어보고있는중에 막히는 부분이있는데. joinpoint가 실행되고 정상 흐름일때,예외 상황일때 로그 저장 기능을 만들고있습니다. 코드를 알려드리면@Around(pointcut) public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable { try { result = joinPoint.proceed(); // @Transactional } catch { logService.saveLog(args); // @Transactional throw e; } logService.saveLog(args); return result; }간략히 이런식 구성돼있습니다.joinpoint가 실행되는 매서드에는 @Transactional이 붙어있어 트랜잭션이 실행되고 logService.saveLog에도 @Transactional이 붙어있어 트랜잭션이 실행됩니다.여기서 문제가 제가 이해하기론 트랜잭션안에서 트랜잭션이 실행될때 내부 트랜잭션, 외부 트랜잭션으로 나뉘고 이것들을 통합하는 하나의 물리트랜잭션으로 된다고 이해했는데, 위 코드의 상황에는 joinpoint.proceed에서 생성된 트랜잭션 안에서 또 다른 트랜잭션이 생성된게 아닌 joinpoint.proceed가 완전히 수행된후 logService.saveLog로 새로운 트랜잭션이 시작된거같은데 이때도 joinpoint.proceed에서 예외가 발생하면 logService.saveLog도 커밋이 되지 않더라구요. logService.saveLog의 @Transactional의 속성을 Requires_new로 하면 예외 상황에서도 잘 저장이되구요.내부 트랜잭션 외부 트랜잭션의 구분이 하나의 @Transactional과 같은 트랜잭션 안에서 또다른 트랜잭션이 생성될때만 구분되는게아니라 사용자 요청이 들어오고 응답이 나가기 전까지의 모든 트랜잭션이 연관되는건지 궁금합니다.아니면 애초에 제가 잘못 하고있는게 있는걸까요..?
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
패키지를 분류하는 기준
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.안녕하세요!패키지를 분류하는 기준은 Entity별로 패키지를 분류할 수 있고 or 강의에서 설명해주신 것 처럼 기능별로 패키지를 분류할 수 있을것 같습니다.Entity별로 패키지를 분류한다면 -> member라는 패키지 안에 Member 엔티티 , MemberRepository , MemberService, MemberController 그리고 각종 MemberDto들을 저는 위치시켰습니다. 그리고 기능별로 패키지를 분류한다면 -> controller라는 패키지에 , 각 controller들을 모아놓았습니다. 이 두가지 방식이 있고 , 각 방식에 대한 장단점을 chatGPT에게 물어보았었는데요, 대규모 프로젝트에서는 entity 수가 많아지기 때문에 -> 기능별로 패키지를 분류하는 방법을 더 권장해주었습니다.혹시 이러한 이유로 영한님께서도 기능별로 패키지를 분류하는 방식으로 강의를 진행하시는 건지 여쭤보고 싶습니다.
- 해결됨스프링 DB 2편 - 데이터 접근 활용 기술
Test 코드에서 @Transactional 의 자동 롤백 유무 질문
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]안녕하세요! 강의듣다 궁금한점이 생겨 질문 드립니다.섹션 9 트랜잭션 적용 확인 강의 약 10분쯤에서, Test 코드에서 @Transactional 을 남긴 메서드를 실행했는데, 커밋된 로그는 남지 않고, 컴플리트 된 로그만 남아있다고 설명하시는 부분에서 질문이 있습니다.지난번 강의에서 Test 코드에서 @Transactional 을 남기면 이건 Test이기 때문에 자동으로 롤백 된다는 설명을 하셨던 거로 기억하는데,이게 @Test 어노테이션이 붙은 메서드만 해당해서, 강의중 나온 메서드에선 적용이 안된는걸까요? 강의에선 Test코드에서 BasicService 를 static 클래스로 만들고, 해당 메서드에서 @Transactional 을 사용하는 메서드를 선언했습니다.아님 단순 로그가 커밋이나 롤백을 표시해주지 않기 때문에 그 내용을 설명해주신 걸까요?
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
spring.profiles.active=test 의 필요성
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]test 패키지의 application.properties에서 spring.profiles.active=test를 주석처리하고 실행해보았을때 아래와 같은 로그가 나옵니다. h.i.ItemServiceApplicationTests : No active profile set, falling back to 1 default profile: "default"test 패키지의 application.properties에 profile을 지정해주는 이유는 local 프로필로 인식되지않게하기 위함인데 위와 같은 결과가 나온다면 test 패키지의 application.properties에 profile을 따로 지정해줄 필요가 없지않을까요?
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
spring data jpa동적 업데이터에 관한 질문입니다.
안녕하세요 진짜 잘 보고 있습니다.저 spring data jpa공부하던 중 질문이 있어 글 남깁니다.예를 들어update memberset pass=?, addr=?, memo=?where id=?이런 식으로 sql이 만들어지고 pass,addr,memo는 사용자가 뭘 입력하냐에 따라서 변경하기 위한 입력값이 들어오는 경우도 있고 없을 수도 있습니다.한 사용자는 pass만 변경할 수도 있고 한 사용자는 memo만 변경할 수 있다고 가정할때 mybatis같은 경우 값이 설정된 값만 업데이트가 됐던 거 같은데jpa는 어떻게 해야 할까요?@DynamicUpdate를 추가해도......member.setId(id);member.setAddr(addr);member.setMemo(memo);repository.save(member);로 업데이트를 하면 id,addr,memo에 대한 업데이트 코드가 만들어집니다. 이거 동적 처리하는게 querydsl말고는 방법이 없는지 여쭤봅니다.
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
프록시 호출 관련 질문
안녕하세요 강사님좋은 강의 감사드립니다.참여했던 실무 프로젝트에서 @Transactional 을 잘 이해하지 못한 채 사용했던 적이 있습니다.특정 서비스의 한 메소드에서 @Transactional을 선언하고 해당 메소드가 여러 작업을 하기 때문에 다른 메소드를 호출하는 방식이었습니다.이때 저는 트랜잭션의 특징을 잘 몰라서 두 메소드에 다 @Transactional을 선언했었습니다.예시)@Transactionalpublic void ex1() {...// 로직 수행 후 다른 메소드 호출ex2();}@Transactionalpublic void ex2() {// 로직 수행}테스트를 하는데 로직 수행중 ex1에서 에러가 나든 ex2에서 에러가 나든 트랜잭션 롤백이 안됐었는데 이런 케이스는 무엇이 원인인지 알려주실 수 있나요?강의에서처럼 ex1이 아예 @Transactional이 선언되어 있지 않았다면 롤백이 안되는게 맞다고 생각이 드는데 저는 두 메소드 다 선언했었거든요,,확인해주시고 답변 주시면 정말 감사드리겠습니다ㅠ(혹시나 해서 강의 코드의 externalCall 메소드에 트랜잭션 선언해보고 테스트 돌려봤는데 트랜잭션 적용 되더라고요)+추가 질문으로 이런 경우에는 ex1 메소드에만 @Transactional을 선언하면 될까요?
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
소스코드안에 itemservice-db-start, itemservice-db
itemservice-db-start를 itemservice-db로 바꾸라 하셨는데 이미 itemservice-db가 있었습니다.그래서 itemservice-db로 intellij를 켰는데 안되고 itemservice-db-start로 하니까 되었습니다.두개 다 필요한것인지 궁금합니다.
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
3번째 tx가 1번째 tx에 참여하게 할 수 있나요?
안녕하세요. 현재 스프링 트랜잭션 전파7을 듣는 예제에서 제가 3번째 트랜잭션을 만들어봤습니다. 2번째 트랜잭션은 REQUIRES_NEW이므로 새로운 커넥션이 생성되고 1번째 커넥션은 지연됩니다. 그 결과 3번째 트랜잭션을 만들면 2번째 트랜잭션에 참여하는 것처럼 보입니다. 그러면 2번째 트랜잭션을 사용하다가 잠시 2번째를 지연시키고, 다시 1번째를 사용하면서 3번째가 이에 참여하게 만들었다가 다시 2번째를 사용하도록 만들 수도 있나요? [예시][tx 1] 생성[tx 1] 지연, [tx 2] 생성[tx 2] 지연, [tx 1] 사용[tx 3] 생성, [tx 1]에 참여[tx 3]과 [tx 1] 커밋[tx 2] 커밋 이런 상황을 만날 일은 없어보이고, 이런 문제상황은 다른 해결방법이 있겠지만 트랜잭션 매니저로 위와 같은 상황을 해결할 수 있는지가 궁금합니다.
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
로그에 대해 실무에서 저장방법
[질문 내용]실무에서는 로그를 파일로 저장하나요? 아니면 데이터베이스에 저장하나요? 궁금하네요
- 해결됨스프링 DB 2편 - 데이터 접근 활용 기술
애너테이션 대신 AOP로 트랜잭션 설정하면 어떤가요?
@Transactional 애너테이션을 사용하는 대신 예를 들어 비즈니스 로직이 담겨있는 service 계층의 모든 메서드에 AOP를 통해 일괄적으로 트랜잭션을 적용한다면 어떤 문제가 있을까요?생각으로는 성능 상 부하가 생길 것 같지만 그 영향이 어느 정도인지는 또는 다른 어떤 단점들이 있을지 가늠이 잘 되지 않습니다.
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
jdbc 탬플릿으로 findById로 조회 시 정상적으로 반환은 되지만 AUTO_INCREMENT 한 컬럼이 null인 상황입니다.
안녕하세요. 질문 제목에 관해 말씀드리겠습니다.상황은 이러합니다.member table에 jdbc 탬플릿으로 잘 저장하고 잘 조회하나 테스트하는 중으로 테이블은 아래와 같습니다.CREATE TABLE member ( member_id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(45), login_id VARCHAR(45), password VARCHAR(45), email VARCHAR(45) UNIQUE ); 레포지토리는 아래와 같습니다.package com.boaz.sketch2fashion.repository.member; import com.boaz.sketch2fashion.domain.Member; import com.boaz.sketch2fashion.repository.member.MemberRepository; import com.boaz.sketch2fashion.repository.member.dto.MemberSaveDTO; import com.boaz.sketch2fashion.repository.member.dto.MemberUpdateDTO; import lombok.extern.slf4j.Slf4j; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Repository; import javax.sql.DataSource; import java.security.Signature; import java.util.List; import java.util.Map; @Slf4j @Repository public class JdbcMemberRepository implements MemberRepository { private final NamedParameterJdbcTemplate template; public JdbcMemberRepository(DataSource dataSource) { this.template = new NamedParameterJdbcTemplate(dataSource); } @Override public Member save(Member member) { String sql = "insert into member(name, login_id, password, email) values(:name, :loginId, :password, :email)"; KeyHolder keyHolder = new GeneratedKeyHolder(); SqlParameterSource param = new BeanPropertySqlParameterSource(member); template.update(sql, param, keyHolder); long key = keyHolder.getKey().longValue(); member.setId(key); return member; } @Override public Member findById(Long id) { String sql = "select * from member where member_id = :id"; Map<String, Object> param = Map.of("id", id); Member member = template.queryForObject(sql, param, memberRowMapper()); return member; } @Override public Member findByLoginId(String loginId) { String sql = "select * from member where login_id = :loginId"; Map<String, Object> param = Map.of("loginId", loginId); Member member = template.queryForObject(sql, param, memberRowMapper()); return member; } @Override public void update(Long id, MemberUpdateDTO memberUpdateDTO) { String sql = "update member set name = :name, login_id = :loginId, password = :password, email = :email where id = :id"; SqlParameterSource param = new MapSqlParameterSource() .addValue("name", memberUpdateDTO.getName()) .addValue("loginId", memberUpdateDTO.getLoginId()) .addValue("password", memberUpdateDTO.getPassword()) .addValue("email", memberUpdateDTO.getEmail()); template.update(sql, param); } @Override public void delete(Long id) { String sql = "delete from member where id = :id"; Map<String, Object> param = Map.of("id", id); template.update(sql, param); } private RowMapper<Member> memberRowMapper() { return BeanPropertyRowMapper.newInstance(Member.class); } } 테스트 코드는 아래와 같습니다. @Test void findById() { // given Member memberA = new Member("a", "a", "a", "a"); // when Member saveMember = memberRepository.save(memberA); log.info("{}", saveMember.getId()); Member findMember = memberRepository.findById(saveMember.getId()); log.info("{}", saveMember.getId()); log.info("{}", findMember.getId()); // then assertThat(saveMember).isEqualTo(findMember); } memberA를 저장하고 findById로 찾아서 saveMember와 findMember의 내용물이 같은지 확인하는 코드입니다. memberA를 저장하면 pk는 1이 될 것이라고 생각하여 findById(saveMember.getId());로 H2 DB에서 pk가 1인 행을 찾아 findMember로 반환하여 isEqualTo를 하려고 했습니다. 결과 이미지saveMember의 getId 1로 findMember를 찾았는데 findMember의 getId는 null입니다.따라서 테스트가 실패합니다. 테스트 환경H2 스프링 내장 DB, jdk 17 항상 질문 답변 해주셔서 정말 감사합니다.
- 미해결스프링 DB 2편 - 데이터 접근 활용 기술
데이타소스없이 JpaRepository 작동하는 이유
안녕하세요.문득 스프링 데이타 JPA로 테스트 진행 중 없는 테이블을 만들어주는 기능을 보니 db에 커넥션이 없이 이 모든 게 진행된다는 게 갑자기 낯선 느낌이 들었습니다.쿼리를 만들고 날리는 과정이 커넥션 여부와 관계없이 실행되는 것인가요? 어떻게 데이타소스 없이 작동하는건지 조금 이상하다는 생각이 듭니다.트랜잭션과 관련있는 내용은 아니지만 강의에서 설명을 들으니 문득 궁금해져 질문 드립니다.감사합니다.