묻고 답해요
130만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
커넥션 생성
초기에 트랜잭션이 시작되면 커넥션은 누가 얻어오는건가요?Repository에서DataSourceUtils.getConnection(dataSource); 실행되고 doGetConnection 메서드에서ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); Connection con = fetchConnection(dataSource);이렇게 얻어오는거같은데 트랜잭션 동기화매니저가 커넥션을 얻어오고 이 커넥션을 트랜잭션 매니저가 사용하는건가요??
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
실제 테스트
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예[질문 내용]강의 잘 듣고 있습니다. 현재는 h2 database를 사용해서 구현하는 테스트인데 실제로 테스트 할 때 데이터베이스를 건들면 문제가 생길 것같습니다. 그럴 때에는 어떤 방식으로 테스트를 진행하나요??
-
미해결토비의 스프링 부트 - 이해와 원리
http api test
http -v 명령어가 작동이 안되는데 혹시 제가 잘못하고 있는 것이 있을까요..?
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
DataSource와 트랜잭션 매니저
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]안녕하세요. 해당 강의를 들으면서 DataSource 와 트랜잭션 매니저의 역할이 좀 헷갈립니다. 강의 중에는 트랜잭션 매니저는 데이터소스를 통해 커넥션을 생성하므로 DataSource가 필요하다는 말씀과 PDF에도 그렇게 적혀있는데요. DataSource는 커넥션 풀이나 DriverManagerDataSource 같은 구현체를 다루기 위한 인터페이스로 이전 강의들로 통해 알고 있었습니다. 그리고 실제 구현체는 이 인터페이스를 통해 구현체가 들어오는 것이고요. [질문 내용] 근데 왜 트랜잭션 매니저를 사용하면서 한번 더 인자를 트랜잭션 매니저로 넘겨주어야 하는 것일까요..? 아래 코드입니다.PlatformTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);그리고 트랜잭션 매니저는 추상화와 동기화를 진행한다고 했는데 이미 추상화는 DataSource도 되고 있는 것 같은데 파라미터를 통한 동일한 커넥션 넘겨주는 부분 때문에 트랜잭션 매니저를 사용하는 것일까요?
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
SQLExceptionTranslator DI 관련 질문
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]1-1. 선생님 방식 코드 (MemberRepositoryV4_2)private final DataSource dataSource; private final SQLExceptionTranslator exTranslator; @Autowired public MemberRepositoryV4_2(DataSource dataSource){ this.dataSource = dataSource; this.exTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource); } 1-2. 선생님 방식 코드 (MemberServiceV4Test)@TestConfiguration static class TestConfig{ @Bean public MemberRepository memberRepository(){ return new MemberRepositoryV4_2(dataSource) } } 2-1.제가 생각한 방식 코드 (MemberRepositoryV4_2)private final DataSource dataSource; private final SQLExceptionTranslator exTranslator; @Autowired public MemberRepositoryV4_2(DataSource dataSource, SQLExceptionTranslator exTranslator) { this.dataSource = dataSource; this.exTranslator = exTranslator; } 2-2.제가 생각한 방식 코드(MemberServiceV4Test)@TestConfiguration static class TestConfig{ @Bean public SQLExceptionTranslator sqlExceptionTranslator(){ return new SQLErrorCodeSQLExceptionTranslator(dataSource); } @Bean public MemberRepository memberRepository(){ return new MemberRepositoryV4_2(dataSource , sqlExceptionTranslator()); } }안녕하세요 "스프링 예외 추상화 적용" 관련 강의보다 궁금한 점이 생겨 질문드립니다. 지금까지 코드 설계 시에 수동 빈 등록을 하는 경우 해당 영역은 설정 영역으로 간주되어, @Configuration을 사용하는 설정 영역 / 애플리케이션 영역 으로 나누어서 설계하는 것이 객체지향적인 관점에서 훨씬 좋은 코드라고 이해하고 설계해왔습니다. 즉 TestConfig와 같이 수동 빈 등록을 해주는 설정 영역 코드가 테스트 상황이 아닌 실제 상황이라고 가정하였을 때, 저는 2-2. 제가 구현한 코드 처럼 SQLErrorCodeSQLExceptionTranslator을 설정 영역(TestConfig)에서 수동 빈 등록을 해주고 2-1.제가 구현한 코드 처럼 리포지토리(MemberRepositoryV4_2) 에서 해당 객체를 생성자의 파라메터로 DI 받는 형태로 구현하였습니다. 하지만 1-1. 선생님 코드에선 SQLErrorCodeSQLExceptionTranslator 객체를 직접 리포지토리에서 생성하여 변수에 주입하고 1-2. 선생님 코드 처럼 설정 영역에선 파라메터의 변경 없이 그저 구현체만 MemberRepositoryV4_1 에서 MemberRepositoryV4_2로 갈아끼우는 것을 확인할 수 있었습니다. [핵심 질문]선생님께서 1-1 및 1-2 와 같이 코드를 구현하신 이유가 1-2의 설정 영역 코드(TestConfig)에서 서비스에선 MemberRepository라는 인터페이스를 구현하니, 파라메터의 변경 없이 그냥 구현체를 MemberRepositoryV4_1 => MemberRepositoryV4_2 로 갈아끼울 수 있다는 것을 보여주시기 위해 하신 건지 아니면 SQLErrorCodeSQLExceptionTranslator 객체의 의존관계 주입은 선생님 방식대로 하는 게 맞는 건지 궁금해서 여쭤봅니다 !
-
해결됨스프링 DB 1편 - 데이터 접근 핵심 원리
직접 RuntimeException을 상속한 예외를 만든 이유
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]리포지토리에선 체크예외(Exception)를 언체크예외(RuntimeException)로 변환함으로써 서비스 계층에선 더 이상 throws SQLException을 할 의무가 사라져 더 이상 JDBC 기술에 종속적이지 않을 수 있으며 순수 비즈니스 로직만 남길 수 있었습니다.여기서 RuntimeException으로 직접 변환하지 않고, 그를 상속받은 MyDuplicateKeyException , MyDbException 예외를 만들어서 해당 예외로 변환한 이유는 명시적으로 어디서 어떤 예외가 터졌는지 알기 위함이라고 이해해도 괜찮을까요?
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
트랜잭션 어노테이션
[질문 내용]트랜잭션 애너테이션을 적용하면트랜잭션 템플릿도 자동으로 적용이 되는걸까요 ??그리고 트랜잭션 애노테이션을 적용하지 않고 템플릿만 적용하는건 어떤경우에 쓸수있나요 ??
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
NullPointerException
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. java.lang.NullPointerException: Cannot invoke "hello.jdbc.repository.MemberRepositoryV1.save(hello.jdbc.domain.Member)" because "this.memberRepository" is nullmemberRepositoryV1.save 하는 과정에서 널포인터익셉션이 났습니다. package hello.jdbc.repository; import hello.jdbc.connection.DBConnectionUtil; import hello.jdbc.domain.Member; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.jdbc.support.JdbcUtils; import javax.sql.DataSource; import java.sql.*; import java.util.NoSuchElementException; /** * JDBA - DataSource 사용, JdbcUtils 사용 */ @Slf4j public class MemberRepositoryV1 { private final DataSource dataSource; public MemberRepositoryV1(DataSource dataSource) { this.dataSource = dataSource; } public Member save(Member member) throws SQLException { String sql = "insert into member(member_id, money) values (?,?)"; Connection con = null; PreparedStatement pstmt = null; try { con = getConnection(); pstmt = con.prepareStatement(sql); pstmt.setString(1, member.getMemberId()); pstmt.setInt(2, member.getMoney()); pstmt.executeUpdate(); return member; }catch (SQLException e) { log.info("db error", e); throw e; }finally { close(con, pstmt, null); } } public Member findById(String memberId) throws SQLException { String sql = "select * from member where member_id=?"; Connection con = null; PreparedStatement pstmt = null; ResultSet rs = null; try { con = getConnection(); pstmt = con.prepareStatement(sql); pstmt.setString(1, memberId); rs = pstmt.executeQuery(); if (rs.next()) { Member member = new Member(); member.setMemberId(rs.getString("member_id")); member.setMoney(rs.getInt("money")); return member; } else { throw new NoSuchElementException("member not found memberId=" + memberId); } } catch (SQLException e) { log.info("db error", e); throw e; }finally { close(con, pstmt, rs); } } public void update(String memberId, int money) throws SQLException { String sql = "update member set money=? where member_id=?"; Connection con = null; PreparedStatement pstmt = null; try { con = getConnection(); pstmt = con.prepareStatement(sql); pstmt.setInt(1, money); pstmt.setString(2, memberId); int resultSize = pstmt.executeUpdate(); log.info("resultSize={}", resultSize); }catch (SQLException e) { log.info("db error", e); throw e; }finally { close(con, pstmt, null); } } public void delete(String memberId) throws SQLException { String sql = "delete from member where member_id=?"; Connection con = null; PreparedStatement pstmt = null; try { con = getConnection(); pstmt = con.prepareStatement(sql); pstmt.setString(1, memberId); pstmt.executeUpdate(); }catch (SQLException e) { log.info("db error", e); throw e; }finally { close(con, pstmt, null); } } private void close(Connection con, Statement stmt, ResultSet rs) { JdbcUtils.closeResultSet(rs); JdbcUtils.closeStatement(stmt); JdbcUtils.closeConnection(con); } private Connection getConnection() throws SQLException { Connection con = dataSource.getConnection(); log.info("get connection={}, class={}", con, con.getClass()); return con; } }package hello.jdbc.repository; import com.zaxxer.hikari.HikariDataSource; import hello.jdbc.connection.ConnectionConst; import hello.jdbc.domain.Member; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.jdbc.datasource.DriverManagerDataSource; import java.net.URL; import java.sql.DriverManager; import java.sql.SQLException; import java.util.NoSuchElementException; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @Slf4j class MemberRepositoryV1Test { MemberRepositoryV1 repository; @BeforeEach void beforeEach() { //기본 driverManager - 항상 새로운 커넥션을 획득 // DriverManagerDataSource dataSource = new DriverManagerDataSource(ConnectionConst.URL, ConnectionConst.USERNAME, ConnectionConst.PASSWORD); //커넥션 풀링 HikariDataSource dataSource = new HikariDataSource(); dataSource.setJdbcUrl(ConnectionConst.URL); dataSource.setUsername(ConnectionConst.USERNAME); dataSource.setPassword(ConnectionConst.PASSWORD); repository = new MemberRepositoryV1(dataSource); } @Test void crud() throws SQLException { //save Member member = new Member("memberV2", 10000); repository.save(member); //findById Member findMember = repository.findById(member.getMemberId()); log.info("findMember={}", findMember); log.info("member != findMember {}", member == findMember); assertThat(findMember).isEqualTo(member); //update repository.update(member.getMemberId(), 20000); Member updateMember = repository.findById(member.getMemberId()); assertThat(updateMember.getMoney()).isEqualTo(20000); //delete repository.delete(member.getMemberId()); assertThatThrownBy(() -> repository.findById(member.getMemberId())) .isInstanceOf(NoSuchElementException.class); } }
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
테스트 할 때 궁금한점입니다!
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]테스트 코드를 작성하다가 궁금한점입니다! assertion을 사용할 때 JUnit이 아닌 AssertJ 를 추천하는 이유가 있을까요?
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
h2 에서 연결 시험에서 오류가 나시는 분들께
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]연결 시험에서 실패하시는 분들은 바로 연결 을 누르시면 됩니다. 저는 연결 시험 에서 실패하길래 뭐가 문제인지 찾다가 연결 을 누르니 db 가 정상적으로 생성되네요. 참고 하세요!
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
h2 데이터베이스는 preparestatement를 구현하고 있는 것일까요?
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.JDBC 개발에 있어 제일 핵심이 되는 내용은 JDBC는 인터페이스이고 각각의 데이터베이스는 이 JDBC인터페이스를 구현하여 제공하고 있기에, 개발자들은 JDBC를 통해 다양한 데이터베이스를 용이하게 사용할 수 있는 점인 것 같습니다. preparedStatement는 JDBC 의 3가지 기능인1. Connection 획득SQL 전달결과 조회 중 두 번째 SQL 전달과 관련된 기능이라 생각되는데요. 그런 prepareStatement는 Connection 인터페이스 안에 있는 메서드였고, Statement를 상속하고 있는 또 다른 인터페이스인 PreparedStatement 타입인 것을 알게 되었습니다. 1) 그렇다면 Preparedstatement 또한 JDBC의 일부분이고, h2데이터베이스는 이 PreparedStatement에서 지정해놓은 메서드들을 구현하고 있다고 생각해도 되는지 궁금합니다. 2) Connection 인터페이스든 Statement 인터페이스이든 package를 보면 java.sql 에 속해 있는데, java.sql 패키지 자체가 jdbc라고 생각해도 되는지 궁금합니다.
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
repository관련 질문
repository 코드입니다. private static long sequence = 0L; private DataSource dataSource; public DbMemberRepository(DataSource dataSource) { this.dataSource = dataSource; } public void save(String memberId, String password) throws SQLException { String sql = "insert into member(id, member_id, password) values(?,?,?)"; try { Connection con = null; PreparedStatement pstmt; con = getConnection(); pstmt = con.prepareStatement(sql); pstmt.setLong(1, ++sequence); pstmt.setString(2, memberId); pstmt.setString(3, password); log.info(String.valueOf(sequence)); pstmt.execute(); } catch (SQLException e) { throw new SQLException("sql exception"); } }@Test void save() throws SQLException { memberRepository.save("test1", "test1!"); //memberRepository.save("test2", "test2!"); } 위 코드에서 save테스트를 돌릴 때 save test를 두번 누르면 sequence(id)가 1, 2로 저장된다고 생각했는데 에러가 나고 test코드내에서 memberRepository.save를 두번적으면 되더라고요. 왜 전자처럼 두번누르는 것은 예외가 나나요?
-
해결됨스프링 DB 1편 - 데이터 접근 핵심 원리
DB 변경
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]DB를 H2에서 MySql로 변경하고 싶으면섹션 1의 예제에서는 ConnectionConst 클래스의 URL을 MySql 관련 URL로 바꾸기만 하면 되나요?
-
해결됨토비의 스프링 부트 - 이해와 원리
자동구성 관련해서 질문드립니다.
안녕하세요 토비님.자동구성하는 방법에서 궁금한점이 있어 글드립니다.@MyAutoConfiguration 과 같이 imports 를 통해 자동구성을 설정해줄때 Configuration 외에 Service 혹은 Repository(JPA) 등 여러 다른 Component 들이 있을때는 어떤방식으로 자동구성을 해줄수 있을까요?단순히 생각했을때는 import 에 모두 기입하거나 @Import 의 방법이 생각이 들긴한데 너무 많은 정의가 있을경우에는 좋은방법이 있을까해서 질문드립니다.@ComponentScan 을 사용하는 방법은 권장하지 않는다고 들었습니다. (아마 스캔에 따른 불명확함 때문이 아닌가 생각합니다)
-
해결됨토비의 스프링 부트 - 이해와 원리
신규강의와 스프링 3.1 책관련 질문드립니다
다른 질문글에서 스프링 신규강의 오픈 예정인 답변을 보았습니다.기존에 출판하신 스프링 3.1 책과 새로 오픈할 강의 같이 봐도 될까요? 강의만 보는걸 추천하시나요?
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
delete 오류
안녕하세요 트랜잭션 적용1 강의 10분 40초 정도에서 쿼리로 delete from member를 적었는데 Timeout trying to lock table "MEMBER"; SQL statement:delete from member [50200-224] HYT00/50200 (도움말) 이러한 오류가 떠서요 어떻게 하면 될까요?
-
해결됨스프링 DB 1편 - 데이터 접근 핵심 원리
javax.transaction.Transactional 질문
안녕하세요.javax.transaction.Transactional annotation에 대해 추가 질문드립니다. @Transactional annotation은 총 2가지가 있습니다.Spring에서 제공: org.springframework.transaction.annotation.TransactionalJava에서 제공: javax.transaction.Transactional 강의에서는 spring에서 제공하는 annotation을 사용으로 추천해주셨고, 대부분 org.springframework.transaction.annotation.Transactional으로 사용을 하더라구요. 직접 찾아본 바로는 두가지 모두 트랙잭션 자체에 대한 동작은 동일하지만, spring에서 제공하는 Transactional이 더 많은 부가 기능을 가지고 있어 사용을 권장한다고 하네요. 이렇게 이해하고 넘어가면 될까요?!의견이 궁금해서 질문드립니다.
-
해결됨토비의 스프링 부트 - 이해와 원리
안녕하세요. 토비님
안녕하세요. 혹시 다음 강의 계획에 대해 여쭤봐도 될까요? 스프링 학습을 좀 더 깊게하고 싶어 토비의 스프링3.1을 구매하려고 했는데 혹시라도 조만간 토비님의 강의가 나온다면 강의부터 보고 싶은 마음에 질문 드립니다.
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
xml파일과 properties에 로그레벨 질문
<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="STDOUT" /> </root> </configuration>이 코드를 쓰면 DataSource 커넥션풀에서 별도의 Thread가 동작하는 코드를 볼수가 있는데 application.properties에서 logging.level.root=debug 로 설정을하니까 안보이더라구요... 왜 안되는지 이유를 알수잇을까요?
-
미해결토비의 스프링 부트 - 이해와 원리
GenericWebApplicationContext : boot 2.* 와 3.* 차이
안녕하세요 토비님 토비님에 강의 어노테이션 매핑 정보 사용강의 내용 중 GenericWebApplicationContext를 사용해서 @RequestMapping이 스프링 부트 2.7에서는 정상적으로 동작을 하는데요 스프링 3.* 에서는 404가 뜨는 상황입니다. GenericWebApplicationContext가 뭔가 변한걸까요? 로그를 보면 3.*는 아예 리플래쉬가 안되는것 같은 느낌이 듭니다. 감사합니다.