wasup
Spring) JDBC / jdbcTemplate / JPA / spring data JPA 본문
순수 JDBC : 데이터를 DB에 저장하고 관리하기 위해 서버와 디비를 연결!
JdbcTemplate : 순수 JDBC보다 소스 중복내용이 줄어들지만 쿼리를 모두 직접 작성해야함
JPA : JdbcTemplate보다 기본적인 CRUD를 지원한다.
스프링 데이터 JPA : 아예 구현 클래스 필요없이 인터페이스로 해결.
순수 JDBC
데이터를 데이터베이스에 저장하고 관리하기위해 애플리케이션 서버와 디비를 연결
예제)
Repository.java
public class JdbcMemberRepository implements MemberRepository {
private final DataSource dataSource;
public JdbcMemberRepository(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Member save(Member member) {
String sql = "insert into member(name) values(?)";
Connection conn = null;//
PreparedStatement pstmt = null;
ResultSet rs = null;//결과를 받기
try {
conn = getConnection();//커넥션 가져옴
pstmt = conn.prepareStatement(sql,//sql넣기
Statement.RETURN_GENERATED_KEYS);
pstmt.setString(1, member.getName());//sql문의 '?'와 매칭
pstmt.executeUpdate();//db에 실제 쿼리 날리기
rs = pstmt.getGeneratedKeys();//RETURN_GENERATED_KEYS와 매칭하여 키를 반환
if (rs.next()) {//값이 있으면
member.setId(rs.getLong(1));//가져옴
} else {
throw new SQLException("id 조회 실패");
}
return member;
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);//역순으로 리소스 종료
}
}
@Override
public Optional<Member> findById(Long id) {
String sql = "select * from member where id = ?";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, id);
rs = pstmt.executeQuery();
if(rs.next()) {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return Optional.of(member);
} else {
return Optional.empty();
}
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
@Override
public List<Member> findAll() {
String sql = "select * from member";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
List<Member> members = new ArrayList<>();
while(rs.next()) {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
members.add(member);
}
return members;
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
@Override
public Optional<Member> findByName(String name) {
String sql = "select * from member where name = ?";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, name);
rs = pstmt.executeQuery();
if(rs.next()) {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return Optional.of(member);
}
return Optional.empty();
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
private Connection getConnection() {
return DataSourceUtils.getConnection(dataSource);
}
private void close(Connection conn, PreparedStatement pstmt, ResultSet rs)
{
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null) {
close(conn);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
private void close(Connection conn) throws SQLException {
DataSourceUtils.releaseConnection(conn, dataSource);
}
}
SpringConfig.java
@Configuration
public class SpringConfig {
private DataSource dataSource;
@Autowired
public SpringConfig(DataSource dataSource){
this.dataSource = dataSource;
}
@Bean
public MemberRepository memberRepository(){
return new JdbcMemberRepository(dataSource);
}
}
스프링 jdbcTemplate
순수 JDBC와 동일한 환경설정 필요
스프링 jdbcTemplate, MyBatis같은 라이브러리는
JDBC API에서 반복 코드를 대부분 제거해주지만 SQL은 직접 작성해야 한다.
예제)
public class JdbcTemplateMemberRepository implements MemberRepository {
private final JdbcTemplate jdbcTemplate;
//@Autowired //생성자가 하나면 @Autowired 생략 가능
public JdbcTemplateMemberRepository(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public Member save(Member member) {
SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
jdbcInsert.withTableName("member").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 member where id=?", memberRowMapper(), id);
return result.stream().findAny();
}
@Override
public Optional<Member> findByName(String name) {
List<Member> result = jdbcTemplate.query("select * from member 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 (rs, rowNum) -> {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return member;
};
}
}
@Configuration
public class SpringConfig {
private DataSource dataSource;
@Autowired
public SpringConfig(DataSource dataSource){
this.dataSource = dataSource;
}
@Bean
public MemberRepository memberRepository(){
return new JdbcTemplateMemberRepository(dataSource);
}
}
JPA
기본적인 sql을 직접 만들어서 실행해준다.
객체를 쿼리없이 바로 DB에 저장하고 관리할 수 있음.
* JPA: 인터페이스 제공, hibernate : 구현체
* JPA는 객체와 ORM(Object Relational Mapping:객체 관계 매핑)기술이다.
* JPA를 사용하려면 트랜젝션(@Transactional)이 꼭 있어야 함.
build.gradle
dependencies {
//implementation 'org.springframework.boot:spring-boot-starter-jdbc'//java는 db를쓰려면 jdbc가 꼭 있어야함.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'//jdbc를 포함하는 jpa!
runtimeOnly 'com.h2database:h2'//db와 연결할 때 database가 제공하는 클라이언트가 필요.
}
application.properties
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
//spring.jpa.hibernate.ddl-auto=create //create로 하면 테이블까지 생성해줌.
Member.java
//@Entity : JPA가 관리하는 Entity
@Entity
public class Member {
//@Id : pk매핑
//@GeneratedValue : 주키의 값을 위한 자동 생성 전략 명시.
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long 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;
}
}
Repository.java
public class JpaMemberRepository implements MemberRepository{
//JPA는 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 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() {
//List<Member> result = em.createQuery("select m from Member m", Member.class).getResultList();
//return result;
//->
return em.createQuery("select m from Member m", Member.class).getResultList();
}
}
Service.java
@Transactional
public class MemberService {
}
SpringConfig.java
@Configuration
public class SpringConfig {
/*JPA를 사용하려면 기존에 받았던 dataSource 대신
private DataSource dataSource;
@Autowired
public SpringConfig(DataSource dataSource){
this.dataSource = dataSource;
}
*/
//스팩에서는 @PersistenceContext 를 사용해줘야함.
//스프링에서는 없어도 사용가능.
private EntityManager em;
public SpringConfig(EntityManager em) {
this.em = em;
}
@Bean
public MemberService memberService(){
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
return new JpaMemberRepository(em);
}
}
스프링 데이터 JPA
기본 CRUD 기능도 스프링 데이터 JPA가 모두 제공.
* JPA를 먼저 공부한 다음에 스프링데이터JPA를 사용해야함.
* JPA와 스프링 데이터 JPA를 기본으로 사용하고 복잡한 동적 쿼리는 Querydsl이라는 라이브러리를 사용한다.
* Querydsl을 사용하면 쿼리도 자바 코드로 안전하게 작성하고 동적 쿼리도 편리하게 작성할 수 있다.
* 이런 조합으로도 해결하기 어려운 쿼리는 jdbcTemplate를 사용한다.
package com.was.waspj.repository;
import com.was.waspj.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository{
@Override
Optional<Member> findByName(String name);
}
: SpringDataJpa가 JpaRepository를 받고있으면 스프링빈을 자동으로 등록해서 구현체를 알아서 만들어준다.
[출처]
참고링크1 : ( https://jsaver.tistory.com/entry/Id%EC%99%80-GeneratedValue-%EC%95%A0%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98 )
'IT > Java' 카테고리의 다른 글
Spring) AOP( Aspect Oriented Programming ) / 관점지향 프로그래밍 (0) | 2021.08.09 |
---|---|
Spring Legacy MiNi Project - STS / member view (0) | 2021.08.09 |
JUnit) 단위테스트와 통합테스트 (0) | 2021.08.07 |
Spring Legacy MiNi Project - STS / member java (0) | 2021.08.05 |
Spring) Note - 개념, 관계, 테스트케이스 JUnit (0) | 2021.07.31 |