1. DB 접근 기술
JDBC(Java Database Connectivity)
- JDBC API를 이용하여 JDBC Driver의 변경에따른 DB 접근
- 동작원리
- 예제)
package sec01.ex01.dao;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import sec01.ex01.dto.MemberDTO;
public class MemberDAO {
String driver = "oracle.jdbc.OracleDriver";
String url = "jdbc:oracle:thin:@localhost:1521:xe";
String user = "user1";
String pwd = "1234";
private Connection conn = null;
private Statement stmt = null;
private PreparedStatement pstmt = null;
private ResultSet rs = null;
private DataSource ds;
public List<MemberDTO> listMember(){
List<MemberDTO> list = new ArrayList<>();
try {
Class.forName(driver);
System.out.println("Oracle 드라이버 로딩 성공");
conn = DriverManager.getConnection(url, user, pwd);
System.out.println("Connection 성공");
stmt = conn.createStatement();
System.out.println("Statment 생성 성공");
String sql = "SELECT *FROM MEMBERTEST";
rs = stmt.executeQuery(sql);
while(rs.next()) {
String id = rs.getString("id");
String pwd = rs.getString("pwd");
String name = rs.getString("name");
String email = rs.getString("email");
Date joinDate = rs.getDate("joinDate");
MemberDTO dto = new MemberDTO();
dto.setId(id);
dto.setPwd(pwd);
dto.setName(name);
dto.setEmail(email);
dto.setJoinDate(joinDate);
list.add(dto);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {if(rs != null) rs.close();} catch (Exception e2) {}
try {if(stmt != null)stmt.close();} catch (Exception e2) {}
try {if(conn != null)conn.close();} catch (Exception e2) {}
}
return list;
}
}
- 단점
- 쿼리를 실행하기 전, 후 많은 코드를 작성해야한다.
- DB와의 연결 설정 및 객체 자원을 사용후 종료(close)해야한다.
- DB 관련 로직에서 예외처리코드를 작성해야한다.
- DB연결 설정에서 예외처리가 필수가 된다.
- 중복코드가 많다.
- 가독성이 좋지않다.
- 유지,보수가 어렵다.
- 쿼리를 실행하기 전, 후 많은 코드를 작성해야한다.
SQL Mapper
-
객체와 테이블 간의 관계를 직접 매핑하는 것이 아닌, SQL문을 실행해 쿼리 수행 결과를 어떤 객체에 바인딩 하는 방법이다.
-
따라서 DBMS에 종속적인 방법이라고 할 수 있으며, 대표적으로 MyBatis가 있다
ORM
- ORM 기술은 객체(Object)와 DB테이블을 매핑하여 데이터를 객체화하는 기술이다.
- 개발자가 직접 SQL을 작성하지 않아도 자동으로 SQL문을 만들어내기 때문에 DBMS에 종속적이지 않다. 대표적으로 JPA가 있다.
2. MyBatis
- Concept : SQL과 Java 코드를 분리
MyBatis는 개발자에게 유연하고 효율적인 데이터베이스 액세스를 제공한다. SQL 중심적인 접근 방식과 매핑 기능을 통해 개발자는 데이터베이스 조작을 세밀하게 제어할 수 있으며, 성능 최적화와 단순성을 통해 개발 생산성과 코드의 가독성을 높인다.
3. JPA
- Concept : DB 테이블을 매핑해서 데이터를 하나의 객체로 간주하고자 하며, 이러한
사상을 통해서 DB와 객체지향의 패러다임 불일치를 해결하고자 한다.
JPA는 객체 지향적인 프로그래밍 모델을 사용하여 생산성을 향상시키고 유지보수를 용이하게 한다.
또한, 데이터 베이스에 대한 종속성을 줄이고 이식성을 높여서 유연성을 제공한다.
4. MyBatis 동작과정 및 장단점
동작과정
MyBatis를 사용하기 위한 기본적인 자바 인터페이스는 SqlSession이다. 이 인터페이스를 통해 명령어를 실행하고, 메퍼를 얻으며, 트랜잭션을 관리 할 수 있다. SqlSession은 인터페이스로 구현체인 SqlSessionTemplate이 있다. SqlSessionTemplate은 필요한 시점에 세션을 닫고, 커밋하거나 롤백하는 것을 포함한 세션의 생명주기를 관리한다.
장단점
예시) 간략한 Team, Member라는 테이블이 있다.
DB테이블 구성도
Java Entity
두 테이블을 Join하여 모든 컬럼을 가져올때 객체지향적이지 않은, DB에 종속적인 방법을 사용하게된다.
이때 조회 결과값을 담을 Entity가없기때문에, 하나의 Entity를 만들어 사용하게된다.
※ 물론 Join 결과값을 Result <resultMap> 이라는 방법으로 1:1, 1:N. N:N 사용이 가능하나,
실무 프로젝트 기준으로 생각하였을때 매우 복잡하다. 그렇기에 대다수의 사람들이 MyBatis를 이러한 방식으로 사용한다.
또한 insert시 Team에 대한 insert 후 useGeneratedKey방식을 이용하여 PK값을 얻어온이후 다시 Member에 대한 insert를 사용해야한다.
- 장점
-
SQL 쿼리를 직접 작성하므로, 최적화된 쿼리를 구현 할 수 있다.
-
•복잡한 쿼리도 구현 가능하다.
-
- 단점
- 컴파일 시 오류를 확인 할 수없다.
- 쿼리를 직접 작성하기 때문에 JPA에 비해 생산성이 떨어진다.
- DB, Entity 변경 시 소스 및 쿼리 수정이 필요하다.
- 중복 쿼리가 발생하기 쉽다.
★ MyBatis 사용시 객체 지향적인 Java와 데이터 지향적인 DB의 DB에 종속적인 패러다임의 불일치가 발생한다.
5. JPA 동작과정 및 장단점
EntityManger
EntityManager는 데이터베이스와 상호 작용하는 주요한 인터페이스로, 엔티티의 영속성과 일관성을 유지하며, 데이터베이스 작업을 단순화하고 효율적으로 처리하는 데 도움을 준다.
- Entity 관리
- EntityManager를 사용하여 엔티티를 영속성 컨텍스트에 등록하고, 상태 변경을 추적한다.
- 엔티티를 조회, 저장, 수정, 삭제할 수 있는 기능을 제공한다.
- Transaction 관리
- 트랜잭션의 시작과 종료를 관리하고, 트랜잭션 내에서 엔티티 작업을 수행한다.
- 트랜잭션을 커밋 또는 롤백하여 데이터베이스의 일관성을 보장한다.
- 영속성 컨텍스트 관리
영속성 컨텍스트
영속성 컨텐스트란 엔티티를 영구 저장하는 환경이라는 뜻이다. 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 한다. 엔티티 매니저를 통해 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.
영속성 컨텍스트는 내부적으로 1차 캐시영역과 쓰기지연 SQL 저장소 영역이 있다.
영속성 컨텍스트 조회
- 1차 캐시를 조회하여, 1차 캐시에 저장된 데이터가 있을경우 그대로 조회
- 1차 캐시를 조회하여, 1차 캐시에 저장된 데이터가 없을경우 DB 조회 후, 1차 캐시에 저장한다.
엔티티 등록 - 쓰기 지연
엔티티 매니저는 데이터 변경 시 반드시 트랜잭션을 시작해야 한다.
EntityManager em = EntityManagerFactory.createEntityManager();
EntityTransaction tx = em.getTransaction(); // 트랜잭션
// 트랜잭션 시작
tx.begin();
// 비영속
Member member = new Member();
member.setId("member1");
member.setUsername("홍길동");
// 영속
em.persist(member);
// 엔티티 등록
tx.commit();
- em.persist(member); : member 엔티티를 영속 컨텍스트에 저장하지만, 데이터베이스에는 반영되지 않는다.
- tx.commit(); : 트랜잭션을 커밋하는 순간 데이터베이스에 INSERT SQL을 보내 저장하게 된다.
- em.persist()를 실행할 때, 영속 컨텍스트의 1차 캐시에는 member 엔티티가 저장되고, 쓰기 지연 SQL 저장소에는 member 엔티티의 INSERT SQL 쿼리문이 저장된다.
- tx.commit()을 실행하는 순간 쓰기 지연 SQL 저장소에 저장된 INSERT SQL 쿼리를 보내 데이터베이스에 저장하는 것이다.
엔티티 수정 - 변경 감지
EntityManager em = EntityManagerFactory.createEntityManager();
EntityTransaction tx = em.getTransaction(); // 트랜잭션
// 트랜잭션 시작
tx.begin();
// member 조회
Member member = em.find(Member.class, "member");
member.setUsername("hello");
member.setAge("20");
// 엔티티 등록
tx.commit();
- 영속 컨텍스트의 1차 캐시에는 member의 초기 데이터가 저장되어 있을 것이다.
- 이후 set 메서드를 통해 데이터를 변경한다.
- 트랜잭션 커밋 시 flush()가 발생하면서 1차 캐시에서 엔티티와 스냅샷을 비교하여 변경에 대한 감지를 한다.
- 이후 SQL UPDATE 쿼리를 생성하여 쓰기 지연 SQL 저장소에서 쿼리를 보낸다.
엔티티 삭제
Member member = em.find(Member.class, "member");
// 엔티티 삭제
em.remove(member);
- 엔티티 삭제는 remove() 메서드를 통해 데이터를 삭제할 수 있다.
- 영속성 컨텍스트와 데이터베이스에서 모두 제거된다.
엔티티 생명주기
- 비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태
- 영속(managed): 영속성 컨텍스트에 저장된 상태
- 준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태
- 삭제(removed): 삭제된 상태
장단점
- 장점
- 객체 지향적 프로그래밍 설계가 가능하다.
- 기본적인 SQL문법이 지원되기 때문에 생산성이 향상된다.
- 데이터베이스 방언이 지원되기 때문에 특정 데이터베이스에 종속되지 않는다.
- 단점
- QueryDsl을 사용하여도 MyBatis에 비해 복잡한 쿼리문이 구현하기 힘들다.
- 러닝커브가 높다.
JPA의 경우 개발 생산성이 향상되는 장점은 있으나, 복잡한 쿼리문(통계)의 경우 myBatis 가 직관적이고 효율적이라고 생각하여
프로젝트의 특성에 맞게 사용하는것을 추천한다.