JDBC, SQL Mapper, ORM은 모두 데이터베이스와 애플리케이션 간의 상호작용을 위해 사용되는 기술들이지만, 각기 다른 추상화 수준과 개발 방식이 적용됩니다. 간단한 코드 예시와 함께 이 세 가지의 차이점을 비교해보겠습니다.
1. JDBC (Java Database Connectivity)
특징
- 직접적인 SQL 사용: JDBC는 SQL 쿼리를 직접 작성하여 데이터베이스와 통신하는 방식입니다.
- 낮은 추상화 수준: 데이터베이스와의 상호작용을 상세하게 제어할 수 있으며, 데이터베이스 특화된 기능을 쉽게 사용할 수 있습니다.
장점
- 제어력: 모든 SQL 쿼리와 연결 관리를 세밀하게 제어할 수 있습니다.
- 성능 최적화: 성능에 민감한 작업에서 SQL 쿼리를 최적화하기 쉽습니다.
단점
- 반복 코드: 자주 사용하는 코드(예: 연결, 자원 해제 등)가 반복될 수 있습니다.
- 유지보수 어려움: SQL과 자바 코드가 밀접하게 결합되어 유지보수가 어려워질 수 있습니다.
예시 코드 (JDBC)
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class JDBCExample {
public static void main(String[] args) {
// DB 연결을 위한 URL, 사용자 이름, 비밀번호를 설정합니다.
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "username";
String password = "password";
// 데이터베이스 연결을 시도하고, try-with-resources 문법을 사용해 자원 관리를 자동으로 처리합니다.
try (Connection conn = DriverManager.getConnection(url, user, password)) {
// SQL 쿼리문을 작성합니다. 여기서는 ID가 1인 사용자를 조회합니다.
String sql = "SELECT * FROM users WHERE id = ?";
// PreparedStatement를 생성하고, SQL 쿼리의 인자에 값을 바인딩합니다.
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, 1); // 첫 번째 '?'에 1이라는 값을 설정 (ID 값)
// 쿼리를 실행하고 결과(ResultSet)를 가져옵니다.
try (ResultSet rs = stmt.executeQuery()) {
// 결과가 있을 때까지 반복하면서 데이터를 출력합니다.
while (rs.next()) {
System.out.println("User ID: " + rs.getInt("id"));
System.out.println("User Name: " + rs.getString("name"));
}
}
}
} catch (Exception e) {
// 예외가 발생한 경우 예외 스택 트레이스를 출력합니다.
e.printStackTrace();
}
}
}
2. SQL Mapper (예: MyBatis)
특징
- SQL과 객체 매핑: SQL 쿼리를 직접 작성하면서 객체와 데이터를 매핑할 수 있는 프레임워크입니다.
- 중간 추상화 수준: SQL은 직접 작성하지만, 매핑과 트랜잭션 관리는 자동으로 처리됩니다.
장점
- SQL에 대한 유연성: 복잡한 SQL 쿼리를 쉽게 작성할 수 있습니다.
- 자동 매핑: SQL 쿼리 결과를 객체에 자동으로 매핑해줍니다.
단점
- SQL 유지보수: SQL을 직접 작성해야 하므로, SQL이 복잡해지면 유지보수가 어려워질 수 있습니다.
- 설정: XML 또는 애노테이션을 사용해 SQL과 매핑을 정의해야 합니다.
예시 코드 (MyBatis)
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
public class SQLMapperExample {
public static void main(String[] args) {
try {
// MyBatis 설정 파일을 읽어 SqlSessionFactory를 생성합니다.
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
// SqlSession을 열고 데이터베이스와 상호작용을 시작합니다.
try (SqlSession session = sqlSessionFactory.openSession()) {
// SQL 쿼리와 매핑된 Java 메서드를 호출하여 데이터를 조회합니다.
// "UserMapper.selectUser"는 MyBatis XML 파일에 정의된 매퍼로, ID가 1인 사용자를 조회합니다.
User user = session.selectOne("UserMapper.selectUser", 1);
// 조회된 사용자의 정보를 출력합니다.
System.out.println("User ID: " + user.getId());
System.out.println("User Name: " + user.getName());
}
} catch (Exception e) {
// 예외가 발생한 경우 예외 스택 트레이스를 출력합니다.
e.printStackTrace();
}
}
}
* Spring에서는 SqlSession 직접 사용 대신 자동으로 매핑된 Mapper 인터페이스를 주입받아 사용.
3. ORM (예: JPA/Hibernate)
특징
- 객체-관계 매핑: 데이터베이스 테이블과 자바 객체를 매핑하여 SQL을 작성하지 않고도 데이터베이스와 상호작용할 수 있습니다.
- 높은 추상화 수준: 데이터베이스와의 상호작용을 고수준으로 추상화하며, SQL은 자동으로 생성됩니다.
장점
- 객체 지향적 접근: 코드가 객체 지향적으로 유지되며, 유지보수와 확장이 용이합니다.
- 표준화: JPA는 자바 표준이며 여러 구현체(Hibernate, EclipseLink 등)가 있어 이식성이 높습니다.
단점
- 복잡한 쿼리: 복잡한 쿼리 작성이 불편할 수 있으며 성능 최적화가 어려울 수 있습니다.
- createQuery() / createNamedQuery() 같은 JPQL을 사용하여 복잡한 조회 쿼리를 실행할 수 있습니다.
- 예: em.createQuery("SELECT u FROM User u WHERE u.name = :name")
- 학습 곡선: JPA/Hibernate의 동작 방식과 매핑을 이해하는 데 시간이 걸릴 수 있습니다.
- find(), persist(), merge(), remove() 등과 같은 JPA 또는 ORM 프레임워크에서 제공하는 주요 함수들을 알고 있어야 합니다.
예시 코드 (JPA/Hibernate)
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class ORMExample {
public static void main(String[] args) {
// EntityManagerFactory를 생성하여 JPA와 데이터베이스 간의 상호작용을 시작합니다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-persistence-unit");
// EntityManager는 데이터베이스와의 상호작용을 담당합니다.
EntityManager em = emf.createEntityManager();
// 데이터베이스 트랜잭션을 시작합니다.
em.getTransaction().begin();
// ID가 1인 사용자를 조회합니다. find() 메서드를 사용해 엔티티(User)를 가져옵니다.
User user = em.find(User.class, 1);
// 조회된 사용자의 정보를 출력합니다.
System.out.println("User ID: " + user.getId());
System.out.println("User Name: " + user.getName());
// 트랜잭션을 커밋하여 데이터베이스 변경 사항을 확정합니다.
em.getTransaction().commit();
// EntityManager와 EntityManagerFactory를 닫아 자원을 해제합니다.
em.close();
emf.close();
}
}
비교
JDBC | SQL Mapper (MyBatis) | ORM (JPA/Hibernate) | |
추상화 수준 | 낮음 | 중간 | 높음 |
SQL 작성 | 직접 작성 | 직접 작성 | 자동 생성 (필요 시 직접 작성 가능) |
유연성 | 매우 높음 | 높음 | 낮음 (쿼리 자동화) |
생산성 | 낮음 (반복 코드) | 중간 (매핑 자동화) | 높음 (자동화) |
성능 최적화 | 매우 용이 | 용이 | 어려울 수 있음 |
유지보수성 | 어려울 수 있음 | 중간 | 높음 |
- JDBC는 최대한의 제어력과 유연성을 제공하지만, 반복적이고 복잡한 코드를 작성해야 합니다.
- SQL Mapper (MyBatis)는 SQL 작성의 유연성을 유지하면서도 객체와 SQL을 쉽게 매핑해줍니다.
- ORM (JPA/Hibernate)는 객체 지향적 프로그래밍에 집중할 수 있게 해주며, 데이터베이스 상호작용을 고도로 추상화합니다.