30%
61,600원
다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결실전! 스프링 데이터 JPA
postUpdate 어노테이션에 대하여
안녕하세요! JPA 정말 재미있게 잘 공부하고 있습니다. 제가 jpa를 사용해서 프로젝트를 하고 있는데, postUpdate annotation을 이용하여 특정 시점에 제가 원하는 작업을 하고 싶어서 그때 변화가 생기기 전의 entity와 변화가 생긴 후의 entity모두를 기록해야할 필요가 있어서요. 근데 postUpdate는 하나의 인자만(변경 후의 엔티티) 받는 것 같아서 이걸 어떻게 접근할 수 있을지 혹시 질문드릴 수 있을지 여쭤보게 되었습니다! 임시 방편으로는 postLoad가 되는 시점에 엔티티 내부에 transient 필드를 하나 선언하고 거기에 복사를 떠둔 후 postUpdate가 이뤄지는 시점에 이것을 읽어와 사용하는 방법을 생각해보았는데, 그러면 조회성 서비스 모두에서 postLoad가 불릴때 불필요하게 객체를 복사하는 문제가 생겨서요. 혹시 방법이 있을지 여쭤보고 싶습니다!! 감사합니다!
- 미해결실전! 스프링 데이터 JPA
하이버네이트 인터셉터문의
안녕하세요 강사님 다름아니라 하이버네이트 인터셉터 사용시 디비 인서트 업데이트 전후에 암호화가 필요한 필드 그리고 암호화가된 필드에 암호화나 복호화처리를 할 수 있는데 jpa에서 이런 인터셉터 스펙이 있는지 궁금합니다 구글링을 해도 하이버네이트 인터셉터는 있는데 jpa는 도저히 못 찾겠더라구요 그리고 없다면 하이버네이트 인터셉터를 사용해야하는지도 문의드립니다 답변주시면 감사드리겠습니다
- 해결됨실전! 스프링 데이터 JPA
Entity 사용에 대한 질문
안녕하세요. 강사님 먼저 항상 좋은 강의를 해주셔서 감사합니다. 제가 실무에서는 Mybatis만 사용하다, 강사님 강의를 통해서 처음 JPA를 접하고, 개인 프로젝트에서 처음 JPA를 사용하고 있습니다. 강의에서 Entity를 직접 반환을 하면 안된다고 하신걸로 기억을 하는데, API 개발 시 리턴을 할 때만 직접 Entity로만 반환을 하면 안되지는 일반 MVC 형태의 개발을 할 때는 직접 Entity를 반환하는 방식을 실무에서 사용을 하시는지 궁금합니다. 저는 평소 Controller에서 파라미터를 받을 때 @ModelAttribute를 이용해 DTO를 통해 파라미터를 받는 방식을 주로 이용하는데, JPA 사용 시 @ModelAttribute를 이용하여 직접 Entity를 받는 방식은 좋은 방식인지 궁금하여 질문 드립니다. 감사합니다.
- 미해결실전! 스프링 데이터 JPA
@Query를 사용하면 계속 확정적으로 오류가 뜹니다 왜 그런건지 도저히 모르겠네요..
spring jpa사용하지 않고 em.createquery를 사용해서 test를 실행 시켜도 무조건 에러가 뜹니다. 어디서 문제가 발생하는 걸까요? public interface OrderRepository extends JpaRepository<Order,Long> { List<Order> findByMarketAndTransactionPrice(String market,Double transactionPrice); @Query("select o from Order o") List<Order> findPlz(); List<Order> findByMarketAndTransactionPriceAndTradeStatusAndStatus(String market, Double transactionPrice, OrderTradeStatus tradeStatus, OrderStatus status);} @Testpublic void errorTest() throws Exception { //given orderRepository.findPlz(); //when //then} ; 2021-06-15 03:03:55.491 INFO 17208 --- [ main] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test: [DefaultTestContext@31edaa7d testClass = OrderRepositoryTest, testInstance = com.gon.coin.demotradingcoin.repository.OrderRepositoryTest@19c578bf, testMethod = errorTest@OrderRepositoryTest, testException = org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [select order0_.order_id as order_id1_5_, order0_.created_date as created_2_5_, order0_.excution_time as excution3_5_, order0_.market as market4_5_, order0_.member_id as member_i9_5_, order0_.status as status5_5_, order0_.trade_status as trade_st6_5_, order0_.trading_volume as trading_7_5_, order0_.transaction_price as transact8_5_ from order order0_]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement, mergedContextConfiguration = [WebMergedContextConfiguration@26adfd2d testClass = OrderRepositoryTest, locations = '{}', classes = '{class com.gon.coin.demotradingcoin.DemoTradingCoinApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@5656be13, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@71d44a3, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@34123d65, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@4afcd809], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true]] org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [select order0_.order_id as order_id1_5_, order0_.created_date as created_2_5_, order0_.excution_time as excution3_5_, order0_.market as market4_5_, order0_.member_id as member_i9_5_, order0_.status as status5_5_, order0_.trade_status as trade_st6_5_, order0_.trading_volume as trading_7_5_, order0_.transaction_price as transact8_5_ from order order0_]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:279) at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:253) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:527) at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:144) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$ExposeRepositoryInvocationInterceptor.invoke(CrudMethodMetadataPostProcessor.java:364) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) at com.sun.proxy.$Proxy133.findPlz(Unknown Source) at com.gon.coin.demotradingcoin.repository.OrderRepositoryTest.errorTest(OrderRepositoryTest.java:103) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74) at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54) Caused by: org.hibernate.exception.SQLGrammarException: could not prepare statement at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:63) at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113) at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:182) at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:148) at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1984) at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1914) at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1892) at org.hibernate.loader.Loader.doQuery(Loader.java:937) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:340) at org.hibernate.loader.Loader.doList(Loader.java:2689) at org.hibernate.loader.Loader.doList(Loader.java:2672) at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2506) at org.hibernate.loader.Loader.list(Loader.java:2501) at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:504) at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:395) at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:220) at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1508) at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1537) at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1505) at org.hibernate.query.Query.getResultList(Query.java:132) at org.springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:129) at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:91) at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:136) at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:125) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:605) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:295) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ... 42 more Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "SELECT ORDER0_.ORDER_ID AS ORDER_ID1_5_, ORDER0_.CREATED_DATE AS CREATED_2_5_, ORDER0_.EXCUTION_TIME AS EXCUTION3_5_, ORDER0_.MARKET AS MARKET4_5_, ORDER0_.MEMBER_ID AS MEMBER_I9_5_, ORDER0_.STATUS AS STATUS5_5_, ORDER0_.TRADE_STATUS AS TRADE_ST6_5_, ORDER0_.TRADING_VOLUME AS TRADING_7_5_, ORDER0_.TRANSACTION_PRICE AS TRANSACT8_5_ FROM ORDER[*] ORDER0_ "; expected "identifier"; SQL statement: select order0_.order_id as order_id1_5_, order0_.created_date as created_2_5_, order0_.excution_time as excution3_5_, order0_.market as market4_5_, order0_.member_id as member_i9_5_, order0_.status as status5_5_, order0_.trade_status as trade_st6_5_, order0_.trading_volume as trading_7_5_, order0_.transaction_price as transact8_5_ from order order0_ [42001-199] at org.h2.message.DbException.getJdbcSQLException(DbException.java:451) at org.h2.message.DbException.getJdbcSQLException(DbException.java:427) at org.h2.message.DbException.getSyntaxError(DbException.java:243) at org.h2.command.Parser.readColumnIdentifier(Parser.java:4530) at org.h2.command.Parser.readIdentifierWithSchema(Parser.java:4491) at org.h2.command.Parser.readTableFilter(Parser.java:1853) at org.h2.command.Parser.parseSelectSimpleFromPart(Parser.java:2641) at org.h2.command.Parser.parseSelectSimple(Parser.java:2788) at org.h2.command.Parser.parseSelectSub(Parser.java:2636) at org.h2.command.Parser.parseSelectUnion(Parser.java:2469) at org.h2.command.Parser.parseSelect(Parser.java:2440) at org.h2.command.Parser.parsePrepared(Parser.java:814) at org.h2.command.Parser.parse(Parser.java:788) at org.h2.command.Parser.parse(Parser.java:760) at org.h2.command.Parser.prepareCommand(Parser.java:683) at org.h2.engine.Session.prepareLocal(Session.java:627) at org.h2.server.TcpServerThread.process(TcpServerThread.java:270) at org.h2.server.TcpServerThread.run(TcpServerThread.java:175) at java.base/java.lang.Thread.run(Thread.java:831) at org.h2.message.DbException.getJdbcSQLException(DbException.java:451) at org.h2.engine.SessionRemote.done(SessionRemote.java:607) at org.h2.command.CommandRemote.prepare(CommandRemote.java:85) at org.h2.command.CommandRemote.<init>(CommandRemote.java:51) at org.h2.engine.SessionRemote.prepareCommand(SessionRemote.java:477) at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1292) at org.h2.jdbc.JdbcPreparedStatement.<init>(JdbcPreparedStatement.java:77) at org.h2.jdbc.JdbcConnection.prepareStatement(JdbcConnection.java:349) at com.zaxxer.hikari.pool.ProxyConnection.prepareStatement(ProxyConnection.java:311) at com.zaxxer.hikari.pool.HikariProxyConnection.prepareStatement(HikariProxyConnection.java) at com.p6spy.engine.wrapper.ConnectionWrapper.prepareStatement(ConnectionWrapper.java:118) at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:146) at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:172) ... 73 more
- 미해결실전! 스프링 데이터 JPA
JPA 에서 행 연산 방법 질문입니다
안녕하세요 JPA 로드맵을 따라 JPA를 공부하고 있는 수강자 입니다. 만약 테이블에 상품명, 상품 수량, 상품 단가 가 있고 이를 출력할때는 상품명, 상품 수량, 상품 단가, (수량 * 단가 ) as 비용 ...; 이런식으로 데이터 행의 다른 컬럼의 값을 연산해서 출력 한다거나 조건문에 넣을수 있는 방법이 JPA에 있나요? 아니면 네이티브 쿼리나 마이바티스 같이 SQL 자체를 직접 작성해야 하는지 궁금합니다.
- 미해결실전! 스프링 데이터 JPA
Thread 처리 관련 질문이 있습니다
@Entity@Getterpublic class Member { @Id @GeneratedValue private Long id;} @SpringBootTest@Rollback(value = false)class MemberRepositoryTest { @Autowired MemberRepository memberRepository; @BeforeEach void prepareData() { memberRepository.save(new Member()); } @Test @Transactional void findMember() throws InterruptedException { Member findMember = memberRepository.findAll().get(0);// Optional<Member> newFindMember = memberRepository.findById(findMember.getId());// System.out.println("newFindMember.isEmpty() = " + newFindMember.isEmpty()); int threadCount = 1; int latchCount = 1; ExecutorService service = Executors.newFixedThreadPool(threadCount); CountDownLatch countDownLatch = new CountDownLatch(latchCount); for(int i = 0; i < latchCount; i++) { service.execute(() -> { Optional<Member> newFindMember = memberRepository.findById(findMember.getId()); System.out.println("newFindMember.isEmpty() = " + newFindMember.isEmpty()); countDownLatch.countDown(); }); } countDownLatch.await(); }} 위에 처럼 테스트 전에 BeforEach 에서 Member 1건을 생성하고 Thread 환경이라는 전제 하에서 가져오는 부분을 샘플로 짰는데요, 위 코드를 실행하면 궁금한 부분이 2가지 있습니다. 1. findById 를 할 경우 select 쿼리가 실행됩니다. @Transactional 로 선언되어 최초 findAll 에서 가져온 객체들이 영속성 컨텍스트에 남아있을테니 findById 시점에는 영속성컨텍스트의 대상을 가져올 것 같은데 왜 select 쿼리가 실행되는 건가요? 2. findById 를 통해 가져온 대상이 빈객체 입니다. isEmpty 가 true 로 나오는데요, 어찌됐건 DB에서 조회를 해왔을테니 대상을 정상적으로 가져와야 할 것 같은데, 왜 아무것도 가져오지 못하는 건가요? @Transactional 어노테이션을 제거하면 정상적으로 가져 옵니다. Thread 환경이 아닌 일반적인 코드에서는 예상대로 잘 실행이 되는데 Thread 환경에서는 왜 그런지 모르겠네요ㅠ
- 해결됨실전! 스프링 데이터 JPA
실습 프로젝트 배포판(jar) 빌드 및 실행시 트러블
안녕하세요. 영한님. Spring Data JPA 강의를 모두 마쳤고, Query DSL 강의 들은 후 다른 강의들과 함께 다시 들어 볼 계획입니다. 질문은 : 정상적으로 실행되는 Spring Boot 프로젝트를 배포용 jar로 만들어, 커맨드 창에서 실행하면 에러가 발생하는 상황인데 이럴 때 어느 부분을 체크해야 합니까? 자료 1 : 해당 프로젝트 다운로드 (zip) : https://we.tl/t-qsuxCwz4rf이 zip 파일은 영한님의 Spring Data JPA 강의를 끝까지 따라 한 프로젝트입니다. 물론 강의에서의 코드와 완전히 똑같지는 않습니다. 예를 들면 DB는 MySQL을 사용합니다. 제가 배포용 artifact를 설정하고 jar 빌드 후 커맨드 창에서 실행하는 동영상 : https://youtu.be/hAlztOYw9uA 감사합니다.
- 미해결실전! 스프링 데이터 JPA
findById 반환타입 Optional
안녕하세요 선생님. 좋은 강의 매번 감사합니다! 강의를 듣고 토이프로젝트중에 질문이 생겼습니다! 강의 21분쯤에 스프링 데이터 JPA가 제공하는 findById의 반환값을 받아올때 반환값이 없을 수도 있기 때문에 Optional이고 강의에선 편의상 get()으로 가져온다고 하셨습니다! 강의에서 orElse 로 처리한다고 간단하게 언급해주셨는데, 실무에서는 그럼 위와 같은 상황에서 어떤식으로 가져오나요? 관련되어서 조사를 해보았는데 orElse로 값이 없을때 반환할 값을 넣어준다고 하더라구요. 그럼 이 테스트에서는 값이 없을때 반환할 객체 하나 생성해두고 orElse에 넣으면 되는건가요?
- 미해결실전! 스프링 데이터 JPA
서버가 여러대일때 createdDate, updatedDate 처리?
안녕하세요. 보통 createdDate 나 updatedDate 같은걸 등록할때 서버쪽에서 시간을 얻어서 등록하나요? 만약 서버가 여러개 있다고 할때 각 서버마다 아주 미세하게 시간이 다를수도 있을거 같은데요. 그럼 실제 A서버에서 등록한 시간이 B서버에서 등록한 시간보다 느린데 빠르게 등록될수도 있을거같아서요. 차라리 db에서 제공하는 default값이나 트리거등으로 값을 넣어주는게 확실하고 더 낳지 않나요? 실무에선 보통 어떻게 처리하는지 궁금합니다. 1. 외부타임서버에서 가져오는건 시간 텀이 있을수 있으니 내부에 NTP서버를 두고 시간갱신을 한다. 이떄 NTP클라이언트들의 시간갱신을 텀을 보통 얼마나 주는지도 궁금합니다. 24시간씩 두지는 않을거 같고 6시간? 1시간? 10분? 2. 시간이 중요할거 같으면 db에서 처리한다. 그리고 시간업데이트할떄 연관된 엔티티끼리의 createdDate 시간은 같이 맞추나요? 아님 조금 차이가 나둬 상관없으니 각각 시간을 구해서 업데이트하나요? 아님딱히 중요한건 아니니 그냥 개별로 업데이트 하나요? 예를들어 ORDER, ORDER_ITEM, DELIVERY, ORDER_HISTORY(ORDER와 N:1의 crud내역) 엔티티가 있을때 같은 주문일경우 각각의 엔티티의 시간을 똑같이 맞추나요? 아님 시간차이가 조금 나도 상관없으니 그냥 엔티티별로 createdDate, updatedDate를 업데이트하나요? 그리고 새 강의 계획은 언제쯤 인지 알수 있을까요?
- 해결됨실전! 스프링 데이터 JPA
Auditing 관련 질문 입니다
글을 조회하면 조회수가 1 올라가도록 서비스를 짰는데, 문제는 조회수를 올리면서 lastModifiedDate 필드도 함께 업데이트 됩니다. 제가 원하는 동작은 글 작성자가 글을 수정했을 때만 lastModifiedDate 필드가 업데이트되는 것입니다. 이 경우에는 상속을 하지 않고 lastModifiedDate 필드를 따로 만들어 수정 메소드에서 lastModifiedDate 필드를 수정하는게 best practice 일까요? 감사합니다. @MappedSuperclass@Getter@EntityListeners(AuditingEntityListener.class)public class BaseEntity { @CreatedDate private LocalDateTime createdDate; @LastModifiedDate private LocalDateTime lastModifiedDate; public boolean isModified() { return !createdDate.isEqual(lastModifiedDate); }} @Entity@Getter@NoArgsConstructor(access = AccessLevel.PROTECTED)public class Post extends BaseEntity { @Id @GeneratedValue @Column(name = "post_id") private Long id; @ManyToOne(fetch = LAZY) @JoinColumn(name = "member_id") private Member member; private int views; private String title; @Column(length = 2000) private String content; public Post(Member member, String title, String content) { this.member = member; this.title = title; this.content = content; } public void increaseViews() { views += 1; }} @Service@RequiredArgsConstructor@Transactionalpublic class PostService { private final PostRepository postRepository; public Post write(Post post) { return postRepository.save(post); } @Transactional(readOnly = true) public Page<Post> findAll(Pageable pageable) { return postRepository.findAll(pageable); } public Post view(Long id) throws PostNotFoundException { Post post = postRepository.findById(id).orElseThrow(() -> new PostNotFoundException("게시물을 찾을 수 없습니다.")); post.increaseViews(); return post; }} public interface PostRepository extends JpaRepository<Post, Long> { @Override @EntityGraph(attributePaths = {"member"}) Page<Post> findAll(Pageable pageable);}
- 미해결실전! 스프링 데이터 JPA
LAZY 로딩과 Transactional 에 대해 질문드립니다.
안녕하세요 강사님 강의 열심히 수강하고 있습니다 ~~ 질문은 Lazy 로딩은 사용시점에 쿼리가 발생하니까 처음 쿼리 발생 이후 뒷쪽 로직에 LAZY 로딩이 필요한경우 꽤 Transactional 범위가 넓어지더라구요 그래서 Transactional 범위 사이에 update나 insert 있는 경우는 @Transactional(propagation = Propagation.REQUIRES_NEW) 보통 요걸로 처리하고 있는데 이렇게 Transcational을 길게 가져갈 경우혹시 성능 이슈나 다른 문제사항이 있을까요? ( PS. 다른 방법으로 생각한 부분은 미리 get으로 필요한부분을 호출하도록 생각해보았는데 로직상 뜬금없기도 하고 LAZY로딩에 의미가 없어지는것 같기도 한데 아니면 혹은 더 좋은 방법이 있을까요?)
- 미해결실전! 스프링 데이터 JPA
CQRS 관련 질문
안녕하세요. 강의에 걸쳐서 배민은 CQRS를 도입하여 사용한다고 말씀해주신 부분이 기억나서 관련하여 정보들을 많이 찾아보고 있습니다. 그 중 한가지 질문이 생겼는데요. 명령 저장소와 조회 저장소를 분리해서 사용하는 경우 명령 저장소에 데이터 저장 후 이벤트 버스를 통해 조회 저장소에 데이터를 동기화 시킨다고 알고 있습니다. 그런데 만약 조회 쿼리가 발생했는데 해당 데이터가 최신이 아닌 경우 즉, 이벤트를 컨슈밍하여 동기화하기 직전 그 찰나의 순간인 경우를 대비할 수 있는 방법이 있을까요? 1. 이벤트를 처리하지않았기 때문에 조회 당시의 시점으로 봤을 때 동기화되기 이전의 데이터가 최신 데이터라고 보는게 맞음 2. 최신 데이터를 가져오는 것이 정말 중요한 요구사항으로 발생할 가능성이 적음. 3. 이벤트 드리븐의 특성에 따라 최종적 일관성에 대해 집중하는게 좋음. 대략 위 3가지정도의 이유로 인해 최신 데이터에 대한 걱정을 할 필요가 없을 것 같기도합니다만..아직 실무에서 CQRS를 겪어보지 않아서 어떻게 판단해야할지 헷갈리네요. 관련하여 영한님의 생각을 듣고 싶습니다 :) 감사합니다.
- 해결됨실전! 스프링 데이터 JPA
질문 드립니다.
안녕하세요 강사님 업무를 하다가.. 궁금한 부분이 있어서 질문을 남기게 되었습니다. 질문드립니다. 질문1 Repository에서 @Query 어노테이션을 사용해서 nativeQuery = true 옵션을 주고 Entity 객체로 값을 반환을 받는데 네이티브 쿼리에서 추가한 연산한값 또는 rownum 같은 값을 Entity 객체로 받을수 있을까요? 구글링을 해보니까 안된다고 하는것 같아서요 Entity 객체에 @Transient 애노테이션을 붙여서 필드를 생성해 놓았지만 해당 애노테이션은 엔티티를 만들때 컬럼을 생성하지 않는 역할이라서 역시 안될것 같았지만 역시나 안되네요 혹시 다른 방법이 있을까 해서 질문 남깁니다 질문2 Spring Data Slice를 사용해서 페이징을 구현하려고 하는대요 커서의 개념으로 조회를 하려고 합니다. 그래서 PageRequest.of(0,3)으로 고정 해 놓고 id 값을 기준으로 3개씩 조회를 하려고 하였는데 (id < 입력 값) order by 정렬 기준으로 id를 사용할수가 없는 경우에는 혹시 다른 방법이 있을까요? (순차적으로 id 값이 정렬되어 있지 않은 경우) 그래서 rownum을 사용해서 조회를 하였는데 Entity 객체로 rownum이 반환이 안되더라구요;; 읽어 주셔서 감사합니다^^ Projections 이건 지금 처음 보았는데 안되면 Projections 고려해 바야겠습니다^^ 감사합니다.
- 해결됨실전! 스프링 데이터 JPA
em.flush() 궁금한 게 있습니다.
12분 쯤 벌크연산에서 flush()를 날린 뒤에 벌크연산을 수행하니 db에는 41살, 영속성 컨텍스트에는 40살인 채로 남고 그 상태에서 명시적으로 em.flush()를 날리면 db에 40살이 찍히리라 예상 했는데... flush()할 때 단순히 DB와 동기화하는 게 아니라 변경 감지를 통해 쓰기 지연된 부분만 내보내는 것으로 이해했는데 비슷한가요?
- 미해결실전! 스프링 데이터 JPA
복합키 설정시 시퀀스 사용 관련 문의
삭제된 글입니다
- 미해결실전! 스프링 데이터 JPA
DTO조회와 PROJECTION의 차이가 궁금합니다 !
안녕하세요 강사님! 활용편 1,2편에 이어서 이번 실전편도 듣고 있습니다. 활용편 2편에서 DTO로 조회하시는 걸 알려주셨는데요. 언듯 보기에는 DTO로 조회하는 것과 프로젝션으로 조회하는것이 비슷해 보이는데, 제가 지금 이해하기로는 - DTO의 경우 엔티티의 모든 필드를 조회해서 DTO에 넣어준다음에 반환 -프로젝션의 경우 프로젝션에 선언한 필드만 조회해서 반환 하는 차이만 있는 걸까요? 아니면 제가 잘못알고있거나 이해하지 못한 부분이 있는지 궁금합니다 ! 만약 단순하게 하나의 엔티티를 조회한다 할 경우, DTO와 프로젝션 어느 쪽을 더 선호하시는 지 궁금합니다 ! 감사합니다 !
- 해결됨실전! 스프링 데이터 JPA
예제 실행시 이해 안되는 오류 상황 - TransactionRequiredException
안녕하세요. 영한님. 이상한 오류 상황이 있어서 도움을 요청드립니다. 코드는 강의대로 잘 입력했{다고 확신합|습}니다. 예제 코드 JpaEventBaseEntity() 메소드를 study.datajpa.entity.MemberTest class에 두면 TransactionRequiredException 예외가 발생합니다. em.flush() 위치에서, javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'flush' call 라고 합니다. 반면에 똑 같은 test method를 study.datajpa.repository.MemberRepositoryTest 클래스 안에 두면 문제 없이 실행됩니다. 어떤 차이인지를 밝혀 주시면 감사하겠사옵니다. 아래 소스는 상기 2 개 위치 모두에 JpaEventBaseEntity() 메소드를 넣어둔 상태입니다. 프로젝트 소스 : https://github.com/skc1104/data-jpa-qna 감사합니다.
- 미해결실전! 스프링 데이터 JPA
Lazy로딩과 Fetch 조인 관련해서 질문드립니다
안녕하세요 영한님 강의 열심히 듣고 있습니다. Lazy로딩과 Fetch 조인 관련해서 한가지 궁금한 부분이 생겨서 질문 드립니다 OneToMany 의 관계에서 Fetch 조인은 하나의 클래스에 대해서만 가능하고 두개이상에서는 bags Exception 발생하더라구요 그래서 여러개의 OneToMany 관계가 있을때는 가장 많은 데이터가 있는 부분을 Fetch 조인으로 가져오고 나머지는 Lazy 로딩으로 가져온다가 보통 쓰는 방식이더라구요 여기서 궁금한 부분이 모두 OneToMany 관계일때 A에서 B를 EAGER나 Fetch Join 으로 한번에 가져오고 (C, D를 Lazy 로딩)C를 가져올때는 C를 가져오는 쿼리가 나가고 이후에 F를 가져오는 쿼리가 하나 더 (batch size 설정) 나가던데(EAGER로 설정한 경우) PS. 강의보면서 EAGER를 비추천하셨는데 아예사용하지 않는게 좋을까요? )이부분을 한번에 가져오고 싶은데 C를 가져올때 Fetch Join으로 F를 가져올수는 없는 건가요?
- 미해결실전! 스프링 데이터 JPA
벌크성 수정 쿼리 질문
안녕하세요 영한님 벌크성 수정 쿼리 강의해주실 때 em.flush() em.clear() 를해야한다(영속성 컨텍스트에 있는 캐시정보들을 DB에 반영하기 위해)고 말씀해주셨는데 만약 어떤 user들의 age가 20이 넘는 사람의 인원수가 5명이라고 가정하고 이들의 나이를 1씩 올렸다고 가정했을때 캐시에는 반영이 안되고 바로 DB에만 반영이 되는데 이때 제가 궁금했던것은 em.flush를 하면 1치캐시에 있는것을 db에 반영시키는것(더티체킹을 포함하여) 이라고 생각해서 이 경우에는 1차캐시가 반영되면 안되는것 아닌가요? 캐시 DB -> 벌크성수정쿼리-> 캐시 DB -> em.flush() -> 캐시 DB 40 40 40 41 40 40
- 미해결실전! 스프링 데이터 JPA
deleteById 구현체 질문
영한님 안녕하세요. deleteById 와 delete 구현체를 보던 중에 deleteById 를 호출하면 findById 가 호출되고 delete 에서는 find 가 호출되는데요, em.remove 하기 전에 조회 쿼리를 먼저 호출하는게 오로지 삭제하려는 id 나 entity 의 데이터 존재 여부를 확인하기 위해서만인가요?