Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.IllegalStateException: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection] with root cause org.h2.jdbc.JdbcSQLInvalidAuthorizationSpecException: Wrong user name or password
sol ) h2 user name 과 password 를 지정해준다. ( user name 만 등록해둬 된다곤 하는데,,,) 공백 주의 !
spring.jpa.hibernate.ddl-auto=none : JPA는 테이블을 자동으로 자동으로 생성하는 기능을 제공하는데 none를 사용하면 해당 기능을 끈다. create를 사용하면 엔티티 정보를 바탕으로 테이블도 직접 생성해준다.
JPA 엔티티 매핑
@Entity, @Id @GenerateValue(strategy = GenerationType.IDENTITY), @Column 등
JPAMemberRepository 작성. JPQL 언어 사용
서비스 계층 MemberService에 트랜잭션 추가 @Transactional
스프링은 해당 클래스의 메서드를 실행할 때 트랜잭션을 시작하고, 메서드가 정상 종료되면 트랜잭션을 커밋한다. 만약 런타임 예외가 발생하면 롤백한다.
@Transactional
: JPA를 통한 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.
스프링 데이터 JPA
스프링 부트와 JPA 기반 위에, 스프링 데이터 JPA라는 프레임워크를 더한다.
핵심 비즈니스 로직을 개발하는데 집중할 수 있다.
실무에서 관계형 데이터베이스를 사용한다면 스프링 데이터 JPA는 필수 이다.
스프링 데이터 JPA는 JPA를 편리하게 해주는 기술이다. 따라서 JPA를 먼저 학습한 후에 스프링 데이터 JPA를 학습해야 한다.
에러
SpringConfig 수정 시 아래와 같은 빈 중복 등록 문제 에러가 발견되었다.
Could not autowire. There is more than one bean of 'MemberRepository' type
sol ) 현재 SpringDataJpaMemberRepository 를 작성하기 위함이기에, 이전에 사용했던 JdbcTemplateMemberRepository 또는 MemoryMemberRepositoy 등에 @Repository 어노테이션이 등록 되어 있다면 지워 해결.
SpringConfig에서 스프링 컨테이너에서 MemberRepository를 주입하는 것을 알수있다.
MemberRepository는 등록한게 없는데 ?
보면 public interface SpringDataJpaMemberRepository 인터페이스를 만들고 extends JpaRepository<Member,Long>, MemberRepository
스프링이 인터페이스에 대한 구현체를 만들어 내고, 스프링 빈에 등록한다. 그래서 인젝션을 받을 수 있다.
스프링 데이터 JPA 제공 기능
-인터페이스를 통한 CRUD
- 'findByName()' , 'findByEmail()' 처럼 메서드 이름 만으로 조회 기능 제공
- 페이징 기능 자동 제공
참고 : 실무에서는 JPA와 스프링 데이터 JPA를 기본으로 사용하고, 복잡한 동적 쿼리는 Querydsl이라는 라이브러리를 사용하면 된다. Querydsl을 사용하면 쿼리도 자바 코드로 안전하게 작성할 수 있고, 동적 쿼리도 편리하게 작성할 수 있다. 이 조합으로 해결하기 어려운 쿼리는 JPA가 제공하는 네이티브 쿼리를 사용하거나, 앞서 학습한 스프링 jdbcTemplate를 사용하면 된다.
@Aurowired를 사용하면 객체 생성 시점에 스프링 컨테이너에서 해당 스프링 빈을 찾아서 주입한다.
생성자가 1개만 있으면 @Autowired는 생략 할 수 있다.
자바 코드로 직접 스프링 빈 등록하기
기존 소스에서 MemberService 에서 @Service 와 @Autowired 를 제거하고
MemoryMemberRepository 에서 @Repository 를 지움. MemberController는 그대로 둔다.
그리고 SpringConfig 파일을 생성하여
@Configuration , @Bean 을 사용하여 아래와 같이 작성하여 직접 스프링 빈에 등록하는 행위를 한다.
참고 : XML로 설정하는 방식도 있지만 최근에는 잘 사용하지 않음
참고 : DI에는 필드 주입, setter 주입, 생성자 주입으로 3가지 방법이 있다. 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다.
( 의존관계가 실행중에 동적으로 변하는 경우 == 서버가 실행되고 런타임에 바뀌는 경우)
위 사진과 같이 생성자를 통해 들어옴 ==> 생성자 주입
생성자를 빼고, 필드에다가 @Autowired 하는 것을 ==> 필드 주입 (비권장)
==> setter 주입
여기서 단점은 , MemberController 호출했을 때, setMemberService가 public 으로 노출 되어야 한다. 이러므로 setMemberService는 빈에 등록된 후 수정될 일이 거의 없는데, 수정될 위험이 생기고 수정이 된다면 문제가 생길 가능성이 크다.
따라서 요즘 권장하는 스타일은 생성자를 통해 주입함
참고 : 실무에서는 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 그리고 정형화되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.
주의 : @Autowired를 통한 DI는 'helloController', 'MemberService' 등과 같이 스프링이 관리하는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.