묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨스프링 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.*는 아예 리플래쉬가 안되는것 같은 느낌이 듭니다. 감사합니다.
-
해결됨토비의 스프링 부트 - 이해와 원리
섹션 9 세번째 강의 문의
안녕하세요. 강의 정말 잘 보고 있습니다. 다름이 아니라 섹션9 세번째 강의 jdbc transaction manager 설정 및 테스트 하는 과정에서 마지막 부분에 첫번째 테스트는 통과 하지만 두번째 테스트는 실패합니다라고 설명 해주셨는데요, 아래 실제 콘솔을 보면 두번째 테스트가 성공 하고 첫번째 테스트가 실패 하고 있습니다. 그래서 궁금한 점은 junit에서 각 테스트는 코드상에서 위아래와 무관 하게 각 테스트 메서드가 병렬적으로 동작 하고, 그 과정에서 먼저 디비를 터치하고 트랜잭션을 완료 한 테스트가 먼저 성공 하는 것 인지 궁금 합니다.
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
예외처리
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]강의에서 예외를 직접 잡을 일이 별로 없다고 말씀해주셨는데, 예를 들어 회원가입을 할 때 중복된 아이디를 입력해서 오류가 발생해서 오류 메시지를 보여줘야 하는 경우는 개발자가 잡아야 하는 예외라고 봐도 될까요?
-
해결됨스프링 DB 1편 - 데이터 접근 핵심 원리
왜 계속 커넥션 풀 내 새로운 커넥션을 사용하는 건가요?
트랜잭션 매니져 사용하는 service 테스트 하는 MemberServiceV3_1Test 실행시키면 나오는 로그입니다. 제가 이해가 안가는 것은find by id , save, delete 같은 repository 함수들은 항상 close를 마지막으로 끝내잖아요?그럼 항상 커넥션 하나 사용한 다음에 다시 pool에 반환되니, 매번 conn0만 사용해야하는거 아닌가요? 그리고 지금 여러명이 DB에 들락거리는 것도 아니고 저 혼자만 테스트해보는건데왜 한개의 커넥션이 아닌 여러개의 커넥션을 사용하게 되는 건가요? 커넥션 풀에 있는 다른 커넥션을 사용해야한다는 것은 특정 커넥션들이 트랜잭션 상태에 있기 때문에어쩔 수 없이 풀에 남아있는 커넥션을 사용해야하는거잖아요?근데 저 혼자만 테스트하면 매번 DB작업한 다음에 커넥션을 다시 커넥션풀에 갖다 놓을텐데(모든 repository 함수들이 close로 끝나기 때문에) 왜 conn0이 아닌 5 6 7번까지 사용하게 되는걸까요??
-
해결됨스프링 DB 1편 - 데이터 접근 핵심 원리
DriverManagerDataSource 질문
이 DriverManagerDataSource가 커넥션 풀은 아닌거죠? 매번 getConnection할때마다 새 커넥션을 생성하긴 하지만URL USERNAME PASSWORD 를 매번 전달하지는 않아도 되는그냥 좀 더 편리한 DriverManager 라고 생각하면 되는 걸까요
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
빈 등록
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]기본적인건데 애매해서 질문드립니다. test코드에서 RepositoryV3와 ServiceV3_3를 @Bean으로 등록하지 말고 실제 RepositoryV3클래스와 ServiceV3_3클래스에 @RequiredArgsConstructor와 각각 @Repository, @Service를 추가해서 빈으로 등록을 해도 똑같이 내부에서 작동하는 것으로 봐도 괜찮을까요? test를 실행했을 때 오류가 발생하지 않아서 질문드립니다.