웹 어플리캐이션에서 RDBMS를 사용해 데이터를 관리하려면 SQL을 다뤄야 한다. 그 언어가 자바라면, JDBC API를 사용할 때 SQL을 작성하는데 많은 시간을 할애해야 한다.
비록 이후 발전한 MyBatis나 JdbcTemplate와 같은 SQL 매퍼를 통해 코드량을 줄일 수 있지만 여전히 SQL 의존적인 개발을 할 수밖에 없다.
자바와 RDBMS가 직접 연동된 코드를 작성한다면, 다음과 같다. 참고로 Id, name을 컬럼으로 갖는 Member 테이블이 있다고 가정하자.
public class MemberDAO {
private final DataSource dataSource;
public JdbcMemberRepository(DataSource dataSource) {
this.dataSource = dataSource;
}
private Connection getConnection() {
return DataSourceUtils.getConnection(dataSource);
}
public Member signup(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);
pstmt.setString(1, member.getName());
pstmt.executeUpdate();
rs = pstmt.getGeneratedKeys();
if(rs.next()) member.setId(rs.getLong(1));
else throw new SQLException("id 조회 실패");
return member;
}
catch (SQLException e) {
throw new IllegalStateException(e);
}
finally {
close(conn, pstmt, rs);
}
}
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) conn.close();
}
catch (SQLException e) {
e.printStackTrace();
}
}
private void close(Connection conn) throws SQLException {
DataSourceUtils.releaseConnection(conn, dataSource);
}
}
위와 같은 방대한 방식은 너무 많은 SQL과 JDBC API를 코드로 작성해야 하고, 이는 불필요한 반복 작업만 야기해 개발자의 성장에 하등 도움이 안 될 것이다.
만약 Member 테이블의 컬럼이 추가된다면 어떻게 될까?
이런 불필요한 작업을 계속 반복해야 할 것이다.
이렇게 데이터에서 변동이 생기면, 데이터 접근 계층(DAO)을 사용해 SQL을 숨겨도 결과적으로 엔티티에 대한 신뢰도가 떨어지고, 진정한 의미의 계층 분할이 이뤄지지 않는 문제가 생긴다.
결과적으로 SQL 의존적인 개발을 피할 수 없게 된다.
그래서, 지금 내가 이 글을 쓰고 있다.
JPA가 제공하는 메소드로 간편하게 CRUD는 물론 낮은 의존성으로 데이터를 관리할 수 있다.
이러한 최신 기술 JPA를 공부해서 멋진 스프링개발자가 되자.
자바는 대표적인 객체지향언어이다. 하지만, RDBMS는 데이터 중심으로 구조화되어 있고 집합적인 사고를 요구한다. 객체지향과 완전히 지향점이 다른 것이다.
그래서 개발자가 중간에서 해결해야 하지만 두 패러다임 불일치 문제를 해결하는데 너무 많은 시간과 코드를 소비한다. 그러한 문제를 구체적으로 보고 JPA를 통한 해결책도 알아보자.
객체의 상속 관계와 유사한 모델이 RDBMS의 슈퍼타입-서브타입 관계이다. 자바 코드로는 추상 클래스인 ITEM을 상속하는 각각의 객체를 만들 수 있을 것이다.
만약 JDBC API만으로 Album 객체를 저장한다면, 객체를 분해해서 ITEM과 ALBUM 테이블에 각각 INSERT문으로 데이터를 넣어줘야 할 것이다.
이건 너무 비효율적이다!
객체는 참조를 통해서, DB는 외래 키를 사용해 연관 관계를 맺는다.
여기서 나타나는 패러다임 불일치는,
만약 객체의 멤버 변수를 테이블에 맞춰 타입을 지정하면 객체지향의 특징을 잃기 쉽다.
그래서 참조를 보관하는 방식으로 모델링하면 객체지향으로 가능하다.
class Member {
String id;
Team team; // Long teamId 가 아닌, 참조로 연관관계를 맺는다.
String username;
}
class Team {
Long id;
String name;
}
객체 그래프 탐색을 통해 참조에 참조로 여러 객체를 탐색할 수 있다. 하지만, SQL에선 선언문에 따라 탐색 범위가 제한된다. 결국 DAO를 열어서 하나하나 SQL을 직접 확인해야 할 것이다. 이는 패러다임 불일치를 야기한다.
DB는 기본 키의 값으로 각 row를 구분한다. 반면 객체는 동일성==
과 동등성equals()
으로 비교한다.
이런 패러다임의 불일치 문제를 위해 같은 로우를 조회할 때마다 같은 인스턴스를 반환하도록 구현하는 건 쉽지 않다. 여기에 트렌젝션이 동시에 실행되면 더 어려워 진다.
EJB 3.0가 hibernate 기반으로 만든 새로운 자바 진영의 Object-Relational-Mapping
기술 표준.
JPA는 자바 ORM 기술에 대한 API 표준 명세서이다.
김영한, 『자바 ORM 표준 JPA 프로그래밍』 에이콘(2015)