11월 21일
스프링 JDBC Template은 MyBatis 와 같은 라이브러리 인데 JDBC API에서 반복적인 코드를 대부분 제거해 준다. 하지만 SQL은 직접 작성해 준다.
왜?Template이냐면 디자인패턴중에 Template메서드 패턴이 있는데 그게 많이 적용이되어 만들어 졌기때문에 memberRepository() 구현체를 바꿔줘야한다.
public class JdbcTemplateMemberRepository implements MemberRepository{
private final JdbcTemplate jdbcTemplate;
@Autowired //jdbcTemplate은 컨테이너로부터 주입을 받지 않기 때문에 @Autowired 를 이용해서 생성자로 직접 주입함.
public JdbcTemplateMemberRepository(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public Member save(Member member) {
//JdbcTemplate에는 SimpleJdbcInsert 라는 기능이 있어 쿼리문을 작성하지 않아도 알아서 insert문을 작성해 준다.
SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
jdbcInsert.withTableName("member1023").usingGeneratedKeyColumns("id");
Map<String,Object> parameters= new HashMap<>();
parameters.put("name",member.getName());
Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));
member.setId(key.longValue());
return member;
}
@Override
public Optional<Member> findById(Long id) {
List<Member> result= jdbcTemplate.query("select * from member1023 where id=?",memberRowMapper(),id);
return result.stream().findAny();
}
@Override
public Optional<Member> findByName(String name) {
List<Member> result= jdbcTemplate.query("select * from member1023 where name=?",memberRowMapper(),name);
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
return jdbcTemplate.query("select * from member", memberRowMapper());
}
private RowMapper<Member> memberRowMapper(){
return new RowMapper<Member>() {
@Override
public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return member;
}
};
}
}
위에 JdbcTemplate으로 구현체를 작성하였기 때문에 SpringConfig에서
//Configuration 한것도 스프링 빈으로 관리가 됨
@Configuration
public class SpringConfig {
//스프링부트가 application.properties의 데이터정보를 보고 DataSource빈을 만들어줌
private DataSource dataSource;
//DataSource를 주입시켜줌
@Autowired
public SpringConfig(DataSource dataSource) {
this.dataSource=dataSource;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
//return new MemoryMemberRepository(); 임시로 메모리로 사용했던 구현체
//return new JdbcMemberRepository(dataSource); DB로 만든 구현체
return new JdbcTemplateMemberRepository(dataSource); //JdbcTemplate으로 만든 구현체
}
}
(1)JPA는 기존의 반복코드는 물론이고,기본적인 SQL도 JPA가 직접 만들어서 실행해준다.
(2)JPA를 사용하면 SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환을 할수 있다.
(3)JPA를 사용하면 개발 생산성을 크게 높일수 있다.
(4)JPA는 인터페이스 이고, 구현체로 하이버네이트를 사용하고 있으며,
JPA는 인터페이스라서 자바에서 유일하게 제공하며 구현체는 여러업체에서 제공하고 있다.
(5)JPA는 객체+ORM(Object Relation Mapping)기술 이다.
(6)어노테이션을 통해 데이터베이스와 맵핑한다.
JpaMemberRepository 작성
public class JpaMemberRepository implements MemberRepository{
//JPA는 EntityManager라는 걸로 모든게 동작을 한다.
//build.gradle에 jpa 라이브러리를 받는걸 설정을 해줘서 스프링부트가 자동으로 EntityManager라는걸 생성을 해줌.
//DB연결정보와 application.properties정보를 합쳐서 EntityManager를 만들고 개발자는 EntityManager 주입받기만 하면 됨.
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em=em;
}
@Override
public Member save(Member member) {
//persist가 영구 저장하다
em.persist(member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
//Member.class는 조회하는 타입,id는 식별자
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
@Override
public Optional<Member> findByName(String name) {
List<Member> result= em.createQuery("select m from Member m where m.name = :name",Member.class)
.setParameter("name", name)
.getResultList();
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
//JPQL이라는 쿼리 언어인데 Entity 대상으로 쿼리를 날리는거다. 그래서 Member객체로 지정
List<Member> result= em.createQuery("select m from Member m",Member.class).getResultList();
return result;
}
}
Member 클레스에도 @Entity 어노테이션을 붙여줘서 JPA가 관리하는 Entity 임을 스프링에 알려줘야한다. 그외 @Id 어노테이션을 설정해서 pk맵핑작업을 하고 @GeneratedValue(strategy = GenerationType.IDENTITY)을 써서 오라클의 시퀀스 처럼 DB에서 알아서 넣어주는걸 IDENTITY를 설정하여 숫자를 넣어줌.
@Entity //JPA가 관리하는 Entity 이다.
public class Member {
@Id//pk맵핑작업
@GeneratedValue(strategy = GenerationType.IDENTITY)//오라클의 시퀀스 처럼 DB에서 알아서 넣어주는걸 IDENTITY
private Long id; //고객이 정하는 ID가 아니라 시스템이 정하는 ID로 데이터를 구분하기위함.
private String name; //고객이 회원가입을 할때 적는 이름
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
MemberService 클레스에도 @Transactional 어노테이션을 설정해야함.
JPA는 모든 변경이 @Transactional안에서 변경이 되어야 하기 때문에 어노테이션을 붙여 줘야한다.
SpringConfig 클래스에 Repository설정도 다시하고 JPA 는 주입도 EntityManager로 객체를 주입 받아 쓰기 때문에 주입받는 객체도 바꿔줘야 한다.
//Configuration 한것도 스프링 빈으로 관리가 됨
@Configuration
public class SpringConfig {
// //스프링부트가 application.properties의 데이터정보를 보고 DataSource빈을 만들어줌
// private DataSource dataSource;
//
// //DataSource를 주입시켜줌
// @Autowired
// public SpringConfig(DataSource dataSource) {
// this.dataSource=dataSource;
// }
private EntityManager em;
@Autowired
public SpringConfig(EntityManager em) {
this.em=em;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
//return new MemoryMemberRepository(); 임시로 메모리로 사용했던 구현체
//return new JdbcMemberRepository(dataSource); DB로 만든 구현체
//return new JdbcTemplateMemberRepository(dataSource); JdbcTemplate으로 만든 구현체
return new JpaMemberRepository(em);
}
}
스프링데이터JPA는 리포지토리에 구현클래스 없이 인터페이스만으로 개발을 할수 있다.
그리고 반복적으로 개발해온 CRUD기능도 스프링 데이터 JPA에서 제공한다.
스프링데이터JPA는 JPA를 쉽게 쓸수 있게 도와주는 라이브러리 이다.
JpaRepository,MemberRepository 인터페이스를 다중상속을 받을수 있게
SpringDataJpaMemberRepository클레스를 작성한다.
//JpaRepository,MemberRepository 인터페이스를 다중상속으로 상속 받는다.
public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository{
@Override
Optional <Member> findByName(String name);
}
SpringConfig 클레스의 주입받는 객체를 memberRepository로 바꿔줌
//Configuration 한것도 스프링 빈으로 관리가 됨
@Configuration
public class SpringConfig {
private final MemberRepository memberRepository;
@Autowired
public SpringConfig(MemberRepository memberRepository) {
this.memberRepository=memberRepository;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository);
}
}
스프링데이터JPA가 SpringDataJpaMemberRepository 인터페이스를 보고 스프링빈을 자동으로 만들어서 그것을 SpringConfig에서 인젝션해서 썼음.
그렇다면 기본적인 CRUD는 어디에 있어서 자동으로 만들어 주나?
JpaRepository에서 제공을 한다.