JPA
📌 객체 지향 프로그래밍 언어인 Java와 관계형 데이터베이스 간의 패러다임 불일치 문제를 해결하여 데이터베이스 작업을 객체 지향적으로 수행할 수 있도록 지원한다.
- 객체 지향적으로 설계하면 SQL코드가 점점 복잡해진다. JDBC로 외래키로 참조를 구현해본 경험이 있으니 뭔 말인지 잘 알것이다. 이러한 문제를 해결하기 위해 JPA가 등장했다.
- Java 애플리케이션에서 객체를 관계형 데이터베이스와 매핑하여 데이터를 처리할 수 있도록 지원하는 ORM(Object-Relational Mapping) 표준이다.
- 대표적인 구현체로 Hibernate를 주로 사용한다.
- 표준으로 만들어지면 더욱 명확하게 정의하고 사용할 수 있는 장점이 생긴다.
1. JPA의 주요 개념
- ORM(Object-Relational Mapping):
- 객체지향 프로그래밍의 객체와 관계형 데이터베이스의 테이블 간 매핑을 제공.
- 예: Java 클래스의 필드 ↔ 데이터베이스 테이블의 컬럼.
- Java EE 표준:
- JPA는 Java EE 표준 사양으로, 특정 구현체(Hibernate, EclipseLink 등)에 독립적.
- 추상화 제공:
- SQL 작성 없이 데이터베이스 작업 가능.
- JPA는 JPQL(Java Persistence Query Language)을 통해 객체 중심의 쿼리 제공.
2. JPA 주요 구성 요소
- Entity:
- 데이터베이스 테이블과 매핑되는 Java 클래스.
- @Entity 어노테이션을 사용하여 매핑.
@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String email; // Getters and Setters }
- EntityManager:
- JPA의 핵심 인터페이스로, 엔티티를 관리.
- 데이터베이스의 CRUD 작업을 처리.
- 주요 메서드:
- persist(): 엔티티 저장.
- find(): 엔티티 조회.
- merge(): 엔티티 갱신.
- remove(): 엔티티 삭제.
- Persistence Context:
- 엔티티를 관리하는 메모리 공간.
- 1차 캐시로 작동하여 같은 엔티티를 중복 조회할 때 데이터베이스를 재호출하지 않음.
- JPQL:
- JPA에서 사용하는 객체지향 쿼리 언어.
- SQL과 유사하지만 엔티티와 속성을 대상으로 동작.
String jpql = "SELECT u FROM User u WHERE u.name = :name"; List<User> users = entityManager.createQuery(jpql, User.class) .setParameter("name", "John") .getResultList();
3. JPA의 동작 방식
- 객체를 생성하고 저장:
- EntityManager를 사용해 Java 객체를 데이터베이스에 저장.
- SQL은 JPA가 자동 생성.
EntityManager em = entityManagerFactory.createEntityManager(); em.getTransaction().begin(); User user = new User(); user.setName("John"); user.setEmail("john@example.com"); em.persist(user); // INSERT SQL 생성 및 실행 em.getTransaction().commit();
- 데이터 읽기:
- EntityManager의 find() 메서드를 사용해 데이터를 조회.
- SQL은 자동 생성.
User user = em.find(User.class, 1L); // SELECT SQL 생성 및 실행
- 데이터 갱신:
- 엔티티 객체의 속성을 변경하면 JPA가 자동으로 데이터베이스에 반영(Dirty Checking).
em.getTransaction().begin(); user.setEmail("newemail@example.com"); // UPDATE SQL 생성 및 실행 em.getTransaction().commit();
- 데이터 삭제:
- remove() 메서드를 호출해 엔티티 삭제.
em.getTransaction().begin(); em.remove(user); // DELETE SQL 생성 및 실행 em.getTransaction().commit();
4. JPA와 Hibernate의 관계
- JPA는 표준이고, Hibernate는 JPA의 구현체입니다.
**JPA (Java Persistence API)**는 Java에서 ORM(Object-Relational Mapping)을 위한 인터페이스와 규약을 정의한 표준입니다. |
Hibernate는 JPA 표준을 구현한 ORM 프레임워크입니다. |
- JPA를 사용하면 코드가 구현체에 종속되지 않으므로, 필요에 따라 Hibernate, EclipseLink 등을 교체 가능.
- Hibernate는 JPA 기능 외에도 고유한 추가 기능을 제공.
5. JPA의 장점
- 생산성 향상:
- SQL 작성이 줄어들고, 객체 중심으로 데이터를 처리.
- 데이터베이스 독립성:
- 애플리케이션 코드는 데이터베이스에 종속되지 않음.
- 자동 관리:
- 엔티티 상태(생성, 수정, 삭제 등)를 JPA가 관리.
- JPQL 제공:
- 객체 중심의 쿼리 언어로 개발자 친화적.
- 캐싱:
- 1차 캐시를 통해 불필요한 데이터베이스 호출을 최소화.
6. JPA의 단점
- 학습 곡선:
- JPA는 설정이 많고, 내부 동작 원리를 이해해야 효율적으로 사용할 수 있음.
- 복잡한 쿼리:
- JPQL은 복잡한 SQL(특히 여러 테이블의 JOIN) 작성 시 한계가 있음.
- 이 경우 Native SQL을 사용해야 함.
- 성능 최적화 필요:
- N+1 문제, 캐싱 전략, 데이터베이스 스키마 설계 등을 제대로 관리하지 않으면 성능 문제가 발생.
- 추가 오버헤드:
- JPA가 SQL을 생성하고 실행하므로, 직접 SQL 작성보다 약간의 오버헤드 발생.
7. Spring Data JPA
Spring Data JPA는 JPA를 기반으로 한 스프링 프레임워크의 데이터 접근 계층 라이브러리로, JPA를 더욱 쉽게 사용할 수 있게 도와줍니다.
특징
- 기본 CRUD 메서드 자동 제공.
- 메서드 이름 기반 쿼리 생성.
- JPQL 및 Native SQL 지원.
예제: Repository 인터페이스 정의
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByName(String name);
}
사용 예제
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public List<User> getUsersByName(String name) {
return userRepository.findByName(name);
}
}
8. JPA와 다른 데이터 접근 방식 비교
항목 JPA MyBatis JDBC
주요 방식 | ORM | SQL 매퍼 기반 | 직접 SQL 작성 |
생산성 | 높음 | 중간 | 낮음 |
유지보수 | 상대적으로 쉬움 | 중간 | 어렵다 |
성능 | 설정에 따라 최적화 가능 | 효율적 (SQL 직접 작성 가능) | 효율적 |
사용 사례 | 객체 중심 데이터 처리 | 복잡한 SQL 처리 | 단순한 데이터 처리 |
9. JPA를 사용할 때 주의사항
- N+1 문제:
- 연관 관계에서 데이터가 예상보다 더 많이 로드될 수 있음.
- 해결책: Fetch Join 또는 @BatchSize 사용.
- 캐싱 전략 관리:
- 1차 캐시와 2차 캐시 사용 시 일관성을 유지해야 함.
- 데이터베이스 트랜잭션 관리:
- JPA는 트랜잭션이 필요하므로 Spring과 함께 사용하는 경우 @Transactional로 트랜잭션 범위를 명시.
- 복잡한 비즈니스 로직:
- JPA만으로 모든 문제를 해결할 수 없으므로, 복잡한 쿼리는 Native Query 활용.
ORM(Object-Relational Mapping)
- 객체와 관계형 DB를 자동으로 Mapping하여 패러다임 불일치 문제를 해결한다.
- JDBC API
ORM(Object-Relational Mapping)
🐳 객체 지향 프로그래밍의 객체와 관계형 데이터베이스의 테이블을 자동으로 매핑해주는 기술
1. ORM의 주요 개념
- 객체-관계 불일치 해결:
- 객체지향 언어에서는 데이터를 객체로 표현하지만, 관계형 데이터베이스는 데이터를 테이블로 저장.
- ORM은 객체와 테이블 간의 매핑을 통해 이 불일치를 해결.
- 자동화된 데이터 처리:
- SQL 쿼리 작성 없이 객체를 사용하여 데이터 저장, 조회, 수정, 삭제를 처리.
- 데이터베이스 독립성:
- ORM을 사용하면 코드가 특정 데이터베이스에 종속되지 않음.
2. ORM의 주요 기능
- CRUD 작업 자동화:
- Create, Read, Update, Delete 작업을 객체를 통해 처리.
- SQL 대신 메서드를 호출하여 데이터 조작.
- 객체와 테이블 매핑:
- 클래스 → 테이블, 필드 → 컬럼으로 매핑.
- 연관 관계 매핑:
- 객체의 연관 관계(1:1, 1:N, N:M)를 데이터베이스 테이블의 외래 키 관계로 매핑.
- 캐싱 지원:
- 1차 캐시(영속성 컨텍스트) 및 2차 캐시를 통해 데이터베이스 호출 최소화.
- 쿼리 생성:
- JPQL이나 Query DSL 등을 통해 객체 기반의 쿼리 작성 가능.
3. ORM의 장점
- 생산성 증가:
- SQL 쿼리를 작성하지 않아도 CRUD 작업을 수행 가능.
- 데이터베이스 처리 로직의 코드 양을 크게 줄임.
- 유지보수성 향상:
- 객체 중심의 코드 작성으로 가독성이 높아지고, 데이터베이스 변경 시 코드 변경이 최소화.
- 데이터베이스 독립성:
- 코드가 특정 DBMS에 의존하지 않으므로 DB를 쉽게 교체 가능.
- 보안 강화:
- SQL 인젝션 같은 보안 취약점을 예방하기 쉬움.
- 연관 관계 처리:
- 객체 간의 관계를 직접 정의하고 활용 가능.
4. ORM의 단점
- 학습 곡선:
- ORM 도구의 설정 및 사용법을 이해하는 데 시간이 필요.
- 성능 문제:
- ORM이 자동 생성한 쿼리가 비효율적일 수 있음.
- 복잡한 쿼리는 직접 작성해야 할 수도 있음(Native Query 사용).
- 디버깅 어려움:
- ORM이 생성한 SQL을 디버깅하거나 최적화하기 어렵다.
- 추상화 한계:
- 데이터베이스 고유 기능(예: 저장 프로시저, 특정 SQL 구문) 사용 시 어려움.
5. ORM 동작 방식
1) 매핑 설정
객체를 데이터베이스 테이블에 매핑.
- 클래스는 테이블에 매핑.
- 필드는 테이블의 컬럼에 매핑.
Java JPA 예제:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column(unique = true, nullable = false)
private String email;
// Getters and Setters
}
2) 데이터 저장
객체를 데이터베이스에 저장하면, ORM이 INSERT 쿼리를 생성하고 실행.
User user = new User();
user.setName("John Doe");
user.setEmail("john@example.com");
entityManager.persist(user); // INSERT SQL 실행
3) 데이터 조회
객체를 조회하면 ORM이 자동으로 SELECT 쿼리를 생성.
User user = entityManager.find(User.class, 1L); // SELECT SQL 실행
6. JPA와 ORM의 관계
- JPA는 Java에서 ORM 표준을 정의한 API.
- Hibernate, EclipseLink 같은 구현체는 JPA의 표준을 따르며, 이를 기반으로 ORM 기능 제공.
7. ORM 사용 시 주의점
- N+1 문제:
- 연관된 데이터를 가져올 때 추가적인 쿼리가 반복 실행될 수 있음.
- 해결책: Fetch Join, @BatchSize 사용.
- 복잡한 쿼리:
- 복잡한 JOIN 쿼리나 데이터베이스 고유 기능 사용 시 Native Query 작성 필요.
- 캐싱 전략 관리:
- 1차 캐시와 2차 캐시 사용을 적절히 조정해야 성능을 최적화.
- 스키마 설계:
- ORM은 데이터베이스 설계를 자동화하지 않으므로, 스키마 설계는 여전히 중요.
8. ORM이 적용된 데이터 처리 흐름
- 애플리케이션 개발:
- 개발자는 객체(Entity) 중심으로 코드를 작성.
- ORM 매핑:
- ORM이 객체와 테이블 간의 매핑을 수행.
- 쿼리 생성 및 실행:
- CRUD 작업 시 ORM이 SQL을 생성하여 데이터베이스와 통신.
- 결과 매핑:
- SQL 실행 결과를 객체로 변환하여 반환.
Java EE 표준
🐳 Java를 사용하여 대규모 엔터프라이즈 애플리케이션을 개발하기 위한 표준 플랫폼입니다.
1. Java EE의 주요 개념
- 표준화된 사양:
- 엔터프라이즈 애플리케이션 개발을 위한 API와 스펙이 표준화되어 있어 다양한 구현체에서 동일하게 작동.
- 컨테이너 기반 실행 환경:
- Java EE 애플리케이션은 애플리케이션 서버(Java EE 컨테이너)에서 실행되며, 컨테이너가 리소스 관리, 보안, 트랜잭션 등을 처리.
- 엔터프라이즈 개발 도구:
- 데이터베이스 액세스, 메시징, 웹 서비스, 의존성 주입 등 대규모 애플리케이션에 필요한 기능 제공.
2. Java EE의 주요 구성 요소
1) Web Tier
- Servlet:
- HTTP 요청/응답을 처리하는 자바 기반 웹 컴포넌트.
- JSP (Java Server Pages):
- HTML과 Java 코드를 결합하여 동적인 웹 콘텐츠 생성.
- JSF (Java Server Faces):
- MVC 패턴을 기반으로 한 컴포넌트 기반 UI 프레임워크.
2) Business Tier
- EJB (Enterprise JavaBeans):
- 트랜잭션, 보안, 동시성 관리 등을 포함한 비즈니스 로직 실행.
- CDI (Contexts and Dependency Injection):
- 의존성 주입(Dependency Injection)을 제공하여 객체 간의 결합도 감소.
3) Persistence Tier
- JPA (Java Persistence API):
- ORM(Object Relational Mapping) 표준으로, 데이터베이스와 객체 간의 매핑을 제공.
4) Integration Tier
- JMS (Java Messaging Service):
- 메시징 시스템과 통합을 지원.
- JAX-RS (Java API for RESTful Web Services):
- RESTful 웹 서비스 개발을 위한 API.
- JAX-WS (Java API for XML Web Services):
- SOAP 기반 웹 서비스 개발을 지원.
5) Other APIs
- JTA (Java Transaction API):
- 분산 트랜잭션 관리.
- JavaMail:
- 이메일 송수신 기능 제공.
- Batch API:
- 대량 데이터 처리(batch processing) 지원.
3. Java EE의 특징
- 표준 기반:
- 모든 Java EE 애플리케이션은 표준화된 API를 사용하므로 다양한 애플리케이션 서버에서 동작.
- 확장성:
- 모듈 기반 설계를 통해 애플리케이션을 확장하기 쉽고, 대규모 애플리케이션 개발에 적합.
- 컨테이너 관리:
- Java EE 컨테이너가 보안, 트랜잭션, 리소스 관리, 의존성 주입 등을 자동으로 처리.
- 플랫폼 독립성:
- Java 기반으로 설계되었기 때문에 플랫폼에 독립적.
- 다양한 구현체 지원:
- 다양한 Java EE 구현체(Hibernate, WildFly, GlassFish, Apache TomEE 등)에서 실행 가능.
4. Java EE의 주요 애플리케이션 서버
Java EE 사양은 여러 애플리케이션 서버에서 구현됩니다. 대표적인 서버는 다음과 같습니다:
서버 | 특징 |
GlassFish | Oracle가 Java EE의 참조 구현체로 제공한 서버. 현재는 Eclipse 재단에서 관리. |
WildFly | JBoss의 오픈소스 애플리케이션 서버. |
Apache TomEE | Tomcat 기반 경량 Java EE 서버. |
WebLogic | Oracle에서 제공하는 상용 애플리케이션 서버. |
WebSphere | IBM의 상용 엔터프라이즈 애플리케이션 서버. |
5. Jakarta EE와의 관계
- 2017년, Oracle이 Java EE의 관리를 Eclipse 재단에 넘기면서 이름이 Jakarta EE로 변경됨.
- 최신 표준은 Jakarta EE이며, 기존 Java EE의 발전된 버전.
6. Java EE의 주요 사용 사례
- 엔터프라이즈 애플리케이션:
- 대규모 비즈니스 로직을 처리하는 시스템 개발.
- 예: 금융, 통신, 물류 시스템.
- 웹 애플리케이션 개발:
- JSP, Servlet, JSF 등을 사용하여 웹 애플리케이션 구현.
- 분산 시스템:
- EJB, JMS를 활용하여 여러 시스템 간의 통합.
- RESTful API 개발:
- JAX-RS를 사용하여 RESTful 웹 서비스를 개발.
- 데이터 처리:
- JPA와 JDBC를 통해 데이터베이스와 상호작용.
7. Java EE의 장단점
장점
- 표준화:
- 다양한 구현체에서 동일한 사양을 따름.
- 풍부한 기능:
- 엔터프라이즈 애플리케이션 개발에 필요한 대부분의 기능 포함.
- 확장성과 유연성:
- 대규모 시스템 설계 및 확장에 적합.
- 컨테이너 관리:
- 트랜잭션, 보안 등 복잡한 작업을 컨테이너가 자동 처리.
단점
- 복잡성:
- 학습 곡선이 높으며, 초기 설정과 구조 설계가 복잡.
- 무거운 사양:
- Java EE의 모든 기능을 사용하지 않더라도 큰 오버헤드가 있을 수 있음.
- 의존성:
- 애플리케이션 서버의 성능 및 설정에 영향을 받음.
8. Java EE의 기본 동작 흐름
- 클라이언트 요청:
- HTTP 요청 또는 메시지 큐를 통해 서버로 전달.
- Web Tier 처리:
- Servlet, JSP 또는 JSF에서 요청을 처리.
- Business Tier 처리:
- EJB 또는 POJO에서 비즈니스 로직 실행.
- Database와 상호작용:
- JPA 또는 JDBC를 통해 데이터베이스에 접근.
- 응답 생성:
- 처리 결과를 클라이언트로 반환.
생산성
// 저장
jpa.persist(tutor);
// 조회
Tutor tutor = jpa.find(Tutor.class, tutorId);
// 수정
tutor.setName("수정할 이름");
// 삭제
jpa.remove(tutor);
- persist란 영구히 저장한다는 뜻
- 마치 컬렉션에 저장한듯 객체가 저장되어 조회가 간편하다.
- 수정하고자 할 때 꺼낸 객체에 setName() 하면된다.
유지보수성
// 기존
public class Tutor {
private String id;
private String name;
}
// 필드 수정
public class Tutor {
private String id;
private String name;
private Integer age;
}
- 객체 필드가 수정 되어도 SQL은 JPA가 알아서 처리한다.
패러다임 불일치 문제 해결
상속
연관관계
tutor.setCompany(company);
jpa.persist(company);
- Collection처럼 사용할 수 있다.
객체 그래프 탐색
Tutor tutor = jpa.find(Tutor.class, tutorId);
Company company = tutor.getCompany();
- JPA를 사용하면 신뢰할 수 있는 엔티티, 계층이 된다.
- JOIN or SQL Query 두번 실행은 JPA로 편하게 할 수 있다.
객체 비교
Tutor tutor1 = jpa.find(Tutor.class, tutorId);
Tutor tutor2 = jpa.find(Tutor.class, tutorId);
tutor1 == tutor2; // true
- 단, 전제조건으로 동일한 트랜잭션 필수
성능
- 1차 캐시
- SQL Query 한번만 실행
Tutor tutor1 = jpa.find(Tutor.class, tutorId); // 실행 결과 1차 캐시에 저장
Tutor tutor2 = jpa.find(Tutor.class, tutorId); // 캐시에서 조회
tutor1 == tutor2; // true
- 쓰기 지연
- ORM과 같은 중간 기술이 있으면 한번에 모아서 요청을 보내는것이 가능하다.
- 네트워크 통신이 한번만 발생하여 비용이 감소된다.
// 트랜잭션 시작
transaction.begin();
jpa.persist(company);
jpa.persist(tutor1);
jpa.persist(tutor2);
// 트랜잭션 제출, JDBC BATCH SQL
transaction.commit();
- 지연 로딩, 즉시 로딩
// 지연 로딩
Tutor tutor = tutorRepository.find(tutorId); // SELECT * FROM tutor
Company company = tutor.getCompany();
String companyName = company.getName(); // SELECT * FROM company
// 즉시 로딩
// SELECT t.*, c.* FROM tutor t JOIN Company c ON
Tutor tutor = tutorRepository.find(tutorId);
Company company = tutor.getCompany();
String companyName = company.getName();
- 지연로딩
- 필요할 때만 조회하기 때문에 통신 비용 감소
- 즉시로딩
- 한번만 조회하기 때문에 네트워크 통신 비용 감소
hibernate.dialect
📌 Hibernate가 사용하는 데이터베이스 방언(dialect)을 지정하는 설정으로 데이터베이스와 Hibernate가 상호작용할 때 특정 데이터베이스에 맞게 SQL 구문을 자동으로 조정하는 역할을 수행한다.
- 데이터베이스 방언(Dialect) = 서로 다른 데이터베이스 시스템의 SQL 문법과 기능 차이를 처리하기 위해 제공
- 방언 : SQL 표준을 지키지 않는 특정 데이터베이스 만의 고유한 기능
ex) 페이징
- MySQL : LIMIT
- Oracle : ROWNUM
- JPA는 특정 데이터베이스에 종속되지 않는다.
- MySQL → Oracle 마음대로 변경이 가능하다.
- 실무에서 사용되는 DB의 Dialect는 대부분 이미 존재한다.