순수 JDBC
- 아래와 같이 초기 설정을 해준다.
- JDBC 표준 인터페이스를 통해 표준화하여 여러 개의 데이터베이스에 동일하게 적용할 수 있다.
- DataSource 를 주입받아, `getConnection( )`을 통해 데이터베이스와 연결된 소켓을 얻을 수 있다.
- 반복 코드가 굉장히 길고 많다.
(Connection, PreparedStatement, ResultSet 을 항상 열고 닫는 try-finally 구조가 필요)
- SQL 문을 직접 작성해야 한다.
> 참고 <
- 스프링 부트가 자동으로 데이터베이스 접속 정보로 DataSource을 생성하여 스프링 빈으로 등록한다.
초기 설정
- build.gradle 파일에 jdbc, h2 데이터베이스 관련 라이브러리 추가
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'
- 스프링 부트 데이터베이스 연결 설정 추가
resources/application.properties
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
이때, 스프링 부트 2.4 이상은 `spring.datasource.username=sa`를 추가해야 한다.
예시 코드
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, Statement.RETURN_GENERATED_KEYS);
// 1 > sql의 ? 와 매칭되어 member.getName()으로 값 삽입
pstmt.setString(1, member.getName());
pstmt.executeUpdate(); // DB의 실제 sql 문이 전달
rs = pstmt.getGeneratedKeys(); // RETURN_GENERATED_KEYS와 매칭되어 rs에 저장
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); // 리소스 반환
}
}
}
JDBC 표준 인터페이스, JDBC 드라이버
`java.sql.Connection` - 연결
`java.sql.Statement` - SQL 을 담은 내용
`java.sql.ResultSet` - SQL 요청 응답
JDBC 표준 인터페이스는 위의 3가지를 정의하여 제공한다.
이를 각자의 DB 에 맞도록 구현하여 제공하는 라이브러리를 JDBC 드라이버라고 한다.
(예를 들어, MySQL DB 라면 MySQL JDBC 드라이버 / Oracle DB 라면 Oracle JDBC 드라이버)
DriverManager
라이브러리에 등록된 DB 드라이버들의 목록을 관리하고, 커넥션을 획득하는 기능을 제공한다.
매번 새로운 커넥션을 생성하여 성능에 비효율적이다.
DataSource
커넥션을 획득하는 방법을 추상화 하는 인터페이스이다.
- `DBCP2 커넥션 풀` / `HIKARICP 커넥션 풀` 로 커넥션 풀을 사용
- `DriverMaganerDataSource` 를 사용하여 매번 새로운 커넥션을 생성하여 사용
(DriverManager 는 DataSource 를 구현하지 않기 때문에, DataSource 를 구현한 DriverManagerDataSource 클래스이다. )
어느 방법을 사용해도 애플리케이션 로직이 DataSource 인터페이스에만 의존하여 사용할 수 있다.
JDBC 를 편리하게 사용하는 기술
데이터베이스마다 SQL, 데이터타입 등 일부 사용법이 다르므로 해당 기술과 함께 편리하게 사용한다.
- SQL Mapper
- ORM 기술
SQL Mapper
- SQL 응답 결과를 객체로 편리하게 반환해 준다.
- JDBC 반복 코드를 제거해 준다.
- SQL 문을 직접 작성해야 하지만, 객체 매핑을 도와주는 유틸리티가 있다.
- `스프링 Jdbc Template`, `MyBatis`
ORM 기술
- 객체를 관계형 데이터베이스 테이블과 매핑해주는 기술
- SQL 문을 직접 작성하지 않고, 동적으로 만들어 실행해 준다.
- `JPA`, `하이버네이트`, `이클립스 링크`
( JPA 는 자바의 ORM 표준 인터페이스, 이를 구현한 것이 하이버네이트 / 이클립스 링크 등)
스프링 JdbcTemplate
- 순수 Jdbc 설정과 같게 한다. (위에 있음.)
- Jdbc API에서 본 반복 코드 대부분을 제거한다.
- DataSource 를 주입받아 JdbcTemplate를 생성하여 사용한다.
- SQL 문을 직접 작성해야 한다.
> 참고<
- Insert 문은 데이터 추가라는 단순한 작업이므로, 테이블 이름과 칼럼-값 정보로 SQL을 생성하기 쉽다.
따라서 Spring 이 자동화하기 위해 SimpleJdbcInsert 같은 유틸리티를 제공한다.
그렇지만 Select 는 조건, 그룹화, 정렬, 조인 등 형태가 다양하므로 자동화하기 어려워서 직접 작성한다.
Insert 문도 직접 작성해도 된다.
예시 코드
public class JdbcTemplateMemberRepository implements MemberRepository {
private final JdbcTemplate jdbcTemplate;
public JdbcTemplateMemberRepository(DataSource dataSource) {
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()); // 여기까지 table 이름, pk, name 으로 insert 문 생성
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();
}
private RowMapper<Member> memberRowMapper() {
return (rs, rowNum) -> {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return member;
};
}
}
JPA
- 아래와 같이 초기 설정을 해준다.
- SQL, 데이터 중심의 설계에서 객체 중심의 설계로 전환할 수 있다.
- 개발 생산성이 크게 향상된다.
- 기존의 반복 코드, 기본적인 SQL 문도 JPA 가 직접 만들어 실행한다.
- PK 기반이 아닌 SQL 문은 직접 작성해야 한다.
- 도메인에 @Entity 를 추가한다.
- PK에 @Id와 @GeneratedValue 를 추가한다.
- EntityManger 을 주입받아 동작한다.
- JPA 를 통한 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.
@Transactional 을 서비스 계층에 추가해야 함
@GenerationType(strategy = GenerationType.IDENTITY) 에서 IDENTITY 는 DB가 알아서 생성해 주는 것을 말한다.
@Column : 칼럼명을 명시할 때 사용(ex. Column(name = "칼럼명"))
> 참고 <
- 스프링 부트가 JPA 라이브러리를 다운로드할 때, EntityManger를 생성하여 스프링 빈으로 만든다.
초기 설정
- build.gradle 파일에 JPA, h2 데이터베이스 관련 라이브러리 추가
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
- 스프링 부트에 JPA 설정 추가
resources/application.properties
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.jpa.show-sql=true // JPA가 생성하는 SQL 출력
spring.jpa.hibernate.ddl-auto=none // JPA의 테이블을 자동으로 생성하는 기능을 none
이때, 스프링 부트 2.4 이상은 `spring.datasource.username=sa`를 추가해야 한다.
예시 코드
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
// @Column(name = "칼럼명")
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;
}
}
public class JpaMemberRepository implements MemberRepository {
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
@Override
public Member save(Member member) {
em.persist(member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
}
스프링 데이터 JPA
- JPA 설정과 같게 한다. (위에 있음.)
- JPA 를 편리하게 사용하도록 도와주는 프레임워크이다.
- 리파지토리의 구현 클래스 없이 인터페이스만으로 개발이 가능하다.
- 기본 CRUD 기능도 제공된다.
- 페이징 기능도 자동 제공된다.
save, count, findByName, exists 등 공통 메서드가 제공된다.
> 참고 <
- 스프링 데이터 JPA 의 JpaRepository 가 인터페이스에 구현체를 자동으로 만들어 스프링 빈으로 등록한다.
예시 코드
public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {
@Override
Optional<Member> findByName(String name);
}
출처 | 스프링 입문(김영한) - 인프런
'💠프로그래밍 언어 > Java' 카테고리의 다른 글
[Spring] 스프링 빈 조회하기, 스프링 빈 설정 메타 정보(BeanDefinition) (0) | 2025.02.25 |
---|---|
[Spring] AOP 는 무엇일까 ?? (0) | 2024.12.30 |
[Spring] H2 데이터베이스 설치, 스프링 통합 테스트 (0) | 2024.12.30 |
[Spring] 스프링 빈 등록하는 2가지 방법 (컴포넌트 스캔, 직접 등록) !! (1) | 2024.12.20 |
[Spring] View 를 처리하는 방법들 (Static, Template, API) (2) | 2024.12.20 |