JPA 소개
- 이전의 데이터 중심의 모델에서 벗어나 객체 중심의 개발을 위해
객체와 관계형 데이터베이스 간의 차이를 중간에서 해결해주는 ORM (Object Relational Mapping) 프레임워크 사용 - JPA는 자바 전영의 ORM 기술 표준으로 지루하고 반복적인 CRUD SQL을 알아서 처리해줄 뿐만 아니라
객체 모델링과 관계형 데이터베이스 사이의 차이점도 해결하고 자동으로 SQL을 만들어서 실행 - 이로 인해 생산성과 유지보수가 확연히 좋아졌으며 테스트를 작성하기도 편리해져 버그도 많이 줄어듦
또한 데이터베이스 정책 변경에 영향이 적으므로 효율적
SQL을 직접 다룰 때 발생하는 문제점
- 데이터베이스에 데이터를 관리하려면 SQL을 사용해야 하며,
자바로 작성한 애플리케이션은 JDBC API를 사용해서 SQL을 데이터베이스에 전달하고 결과를 반환받음 - 이처럼 SQL을 직접 다룰 때의 3가지 문제점이 존재
1. 반복, 반복 그리고 반복
예) 회원 관리 기능
아래처럼 데이터베이스는 객체 구조와는 다른 데이터 중심의 구조를 가지므로
객체를 데이터베이스에 직접 저장하거나 조회할 수 없으므로
개발자가 객체지향 애플리케이션과 데이터베이스 중간에서 SQL과 JDBC API를 사용해서 변환 작업을 직접 해주어야 함
이로인해 객체를 데이터베이스에 CRUD하려면 너무 많은 SQL과 JDBC API를 코드로 작성해야 한다는 문제점이 발생
→ 데이터 접근 계층 (DAO)를 개발하는 일이 지루함과 반복의 연속
// 자바에서 사용할 회원 객체
public class Member {
private String memberId;
private String name;
...
}
// 회원 객체를 데이터베이스에 관리할 목적으로 회원용 DAO (데이터 접근 객체) 생성
public class MemberDAO {
// 회원 조회 기능
public Member find(String memberId) {
// 1. 회원 조회용 SQL 작성
String sql = "SELECT MEMBER_ID, NAME FROM MEMBER M WHERE MEMBER_ID = ?"
// 2. JDBC API를 사용해서 SQL을 실행
ResultSet rs = stmt.excuteQuery(sql);
// 3. 조회 결과를 Member 객체로 매핑
String memberId = rs.getString("MEMBER_ID");
String name = rs.getString("NAME");
Member member = new Member();
member.setMemberId(memberId);
member.setName(name);
}
// 회원 등록 기능
public void save(Member member) {
// 1. 회원 등록용 SQL 작성
String sql = "INSERT INTO MEMBER(MEMBER_ID, NAME) VALUES(?,?)";
// 2. 회원 객체의 값을 꺼내서 등록 SQL에 전달
pstmt.setString(1, member.getMemberId());
pstmt.setString(2, member.getName());
// 3. JDBC API를 사용해서 SQL을 실행
pstmt.executeUpdate(sql);
}
... // 회원 수정, 삭제 기능
}
2. SQL에 의존적인 개발
예1) 회원 객체를 관리하는 MemberDAO를 완성한 후 회원의 연락처도 함께 저장해달라는 요구사항이 추가되었을 때
필드를 추가하기 위해 많은 SQL과 JDBC API 코드 수정이 필요
예2) 회원은 어떤 한 팀에 필수로 소속되어야 한다는 요구사항이 추가 + 회원 정보를 출력할 때 연관된 팀 이름도 함께 출력
Member 객체가 연관된 Team 객체를 사용할 수 있을지 없을지가 전적으로 사용하는 SQL에 달리게 되므로
데이터 접근 계층 (DAO)를 사용해 SQL을 숨기더라도 어쩔 수 없이 DAO를 열어서 어떤 SQL이 실행되는지에 대한 확인이 필요
즉, SQL에 모든 것을 의존하는 상황에서는 개발자들이 엔티티를 신뢰하고 사용할 수 없는 대신 DAO를 열어서
어떤 SQL이 실행되고 어떤 객체들이 함께 조회되는지 일일이 확인해야 하므로 진정한 의미의 계층 분할이 아님
또한, 강한 의존 관계 때문에 회원을 조회할 때는 물론이고 회원 객체에 필드를 하나 추가할 때도
DAO의 CRUD 코드와 SQL 대부분을 변경해야하는 문제가 발생
// 자바에서 사용할 회원 객체
public class Member {
private String memberId;
private String name;
private Sting tel; // 연락처 필드 추가
private Team team; // 연관된 객체인 team 필드 추가
...
}
// 회원 객체를 데이터베이스에 관리할 목적으로 회원용 DAO (데이터 접근 객체) 생성
public class MemberDAO {
// 회원 조회 기능
public Member find(String memberId) {
// 1. 회원 조회용 SQL 작성 (연락처 컬럼을 조회하도록 수정)
String sql = "SELECT MEMBER_ID, NAME, TEL FROM MEMBER M WHERE MEMBER_ID = ?"
// 2. JDBC API를 사용해서 SQL을 실행
ResultSet rs = stmt.excuteQuery(sql);
// 3. 조회 결과를 Member 객체로 매핑 (연락처의 조회 결과를 Member 객체에 추가로 매핑)
String memberId = rs.getString("MEMBER_ID");
String name = rs.getString("NAME");
String tel = rs.getString("TEL");
Member member = new Member();
member.setMemberId(memberId);
member.setName(name);
member.setTel(tel);
}
// 팀 이름과 함께 회원 조회 기능
public Member findWithTeam(String memberId) {
// 1. 회원과 회원과 연관된 팀을 함께 조회하는 SQL 작성
SELECT M.MEMBER_ID, M.NAME, M.TEL, T.TEAD_ID, T.TEAM_NAME
FROM MEMBER M
JOIN TEAM T
ON M.TEAM_ID = T.TEAM_ID
...
}
// 회원 등록 기능
public void save(Member member) {
// 1. 회원 등록용 SQL 작성 (연락처를 저장할 수 있도록 수정)
String sql = "INSERT INTO MEMBER(MEMBER_ID, NAME, TEL) VALUES(?,?,?)";
// 2. 회원 객체의 값을 꺼내서 등록 SQL에 전달 (연락처 값을 꺼내서 전달할 수 있도록 수정)
pstmt.setString(1, member.getMemberId());
pstmt.setString(2, member.getName());
pstmt.setString(3, member.getTel());
// 3. JDBC API를 사용해서 SQL을 실행
pstmt.executeUpdate(sql);
}
... // 회원 수정, 삭제 기능 (연락처가 수정될 수 있도록 수정)
}
3. JPA와 문제 해결
객체를 데이터베이스에 저장하고 관리할 때, 개발자가 직접 SQL을 작성하는 것이 아니라 JPA가 제공하는 API를 사용하면
JPA가 개발자 대신 적절한 SQL을 생성해서 데이터베이스에 전달
// 저장 기능
// persist() 메소드가 객체를 데이터베이스에 저장
// JPA가 객체와 매핑정보를 보고 적절한 INSERT SQL을 생성해서 데이터베이스에 전달
jpa.persist(member);
// 조회 기능
// find() 메소드가 객체 하나를 데이터베이스에서 조회
// JPA가 객체와 매핑정보를 보고 적절한 SELECT SQL을 생성해서 데이터베이스에 전달하고
// 그 결과로 Member 객체를 생성해서 반환
String memberId = "helloId";
Member member = jpa.find(Member.class, memberId);
// 수정 기능
// JPA는 별도의 수정 메소드를 제공하지 않음
// 대신 객체를 조회해서 값을 변경만 하면 트랜잭션을 커밋할 때 데이터베이스에 적절한 UPDATE SQL이 전달
Member member = jpa.find(Member.class, memberId);
member.setName("이름변경")
// 연관된 객체 조회
// JPA는 연관된 객체를 사용하는 시점에 적절한 SELECT SQL을 실행
Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();
패러다임의 불일치
- 객체지향 프로그래밍은 추상화, 캡슐화, 정보은닉, 상속, 다형성 등 시스템의 복잡성을 제어할 수 있는 다양한 장치들을
제공하기 때문에 현대의 복잡한 애플리케이션은 대부분 객체지향 언어로 개발됨 - 비즈니스 요구사항을 정의한 도메인 모델도 객체로 모델링하면 객체지향 언어가 가진 장점들을 활용할 수 있게 되나,
이렇게 정의한 도메인 모델을 저장할 때 객체가 단순하지 않고 상속받거나 참조할 경우 문제가 발생함 - 이러한 저장 문제를 해결하기 위한 현실적인 대안은 관계형 데이터베이스에 객체를 저장하는 것인데
객체와 관계형 데이터베이스는 지향하는 목적이 서로 다르므로 둘의 기능과 표현 방법이 달라 패러다임 불일치 문제가 발생 - 그러므로 객체지향 언어로 개발하고 데이터는 관계형 데이터베이스에 저장해야 한다면,
패러다임의 불일치 문제를 개발자가 중간에서 해결해야 하며 이는 너무 많은 시간과 코드를 소비함 - 패러다임의 불일치로 발생하는 문제를 살펴보고 JPA를 통한 해결책을 탐색
JPA는 패러다임의 불일치 문제를 해결해주고 정교한 객체 모델링을 유지하게 도와줌
1. 상속
객체는 상속이라는 기능을 가지고 있지만 테이블은 상속이라는 기능이 없음
그나마 데이터베이스 모델링에서 이야기하는 슈퍼타입 서브타입 관계를 사용하면 객체 상속과 유사한 형태로 테이블 설계 가능
예) 아래의 경우 Album 객체를 저장하려면 객체를 분해해서 두 SQL을 만들어야 저장 가능
INSERT INTO ITEM ···
INSERT INTO ALBUM ···
그 후 Album을 조회를 하기 위해서는 ITEM과 ALBUM 테이블을 조인해서 그 결과로 Album 객체를 생성해야 함
// 객체 모델 코드
abstract class Item {
Long id;
String name;
int price;
}
class Album extends Item {
String artist;
}
class Movies extends Item {
String director;
String actor;
}
class Book extends Item {
String author;
String isbn;
}
이럴 경우 작성해야 할 코드량이 만만치 않고, 패러다임의 불일치를 해결하려는 과정 속에서 소모 비용이 발생
JPA와 상속
JPA는 상속과 관련된 패러다임의 불일치 문제를 개발자 대신 해결해 주며,
마치 자바 컬렉션 (list 등) 에 객체를 저장하듯이 JPA에 객체를 저장
// JPA를 사용해 Item을 상속한 Album 객체를 저장
// persist() 메소드를 사용해 객체 저장
jpa.persist(album);
// JPA가 알아서 SQL을 실행해서 객체를 ITEM, ALBUM 두 테이블에 나누어 저장
INSERT INTO ITEM ···
INSERT INTO ALBUM ···
// find() 메소드를 사용해서 객체 조회
String albumId = "id100";
Album album = jpa.find(Album.class, albumId);
// JPA가 알아서 ITEM과 ALBUM 두 테이블을 조인해서 필요한 데이터를 조회하고 결과를 반환
SELECT I.*, A.*,
FROM ITEM I
JOIN ALBUM A ON I.ITEM_ID = A.ITEM_ID
2. 연관관계
객체는 참조를 사용해서 다른 객체와 연관관계를 가지고 참조에 접근해서 연관된 객체를 조회
테이블은 외래 키를 사용해서 다른 테이블과 연관관계를 가지고 조인을 사용해서 연관된 테이블을 조회
참조를 사용하는 객체와 외래 키를 사용하는 관계형 데이터베이스 사이의 패러다임 불일치 발생
또한 객체는 참조가 있는 방향으로만 조회가 가능하지만, 테이블은 외래키 하나로 양쪽 방향으로 조인 가능
// Member 객체는 Member.team 필드에 Team 객체의 참조를 보관해서 Team 객체와 관계를 맺음
// 참조 필드에 접근하면 Member와 연관된 Team 조회 가능
class Member {
Team team;
...
Team getTeam() {
return team;
}
}
class Team {
...
}
// MEMBER 테이블은 MEMBER.TEAM_ID 외래 키 컬럼을 사용해서 TEAM 테이블과 관계를 맺음
// 외래 키를 사용해서 MEMBER 테이블과 TEAM 테이블을 조인해 MEMBER 테이블과 연관된 TEAM 테이블 조회
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
객체를 테이블에 맞추어 모델링
객체와 테이블의 차이를 알아보기 위해
TEAM_ID 외래 키의 값을 그대로 teamId 필드에 보관하는 방법으로 객체를 단순히 테이블에 맞추어 모델링할 경우,
관계형 데이터베이스는 조인이라는 기능을 통해 외래 키의 값을 그대로 보관하지만
객체는 연관된 객체의 참조를 보관해야 참조를 통해 연관된 객체를 찾을 수 있으므로
Member 객체와 연관된 Team 객체를 참조를 통해 조회할 수 없음
// 테이블에 맞춘 객체 모델
class Member {
String id; // MEMBER_ID 컬럼 사용
Long teamId; // TEAM_ID FK 컬럼 사용
Strign username; // USERNAME 컬럼 사용
}
class Team {
Long id; // TEAM_ID PK 사용
String name; // NAME 컬럼 사용
}
// 이처럼 객체의 참조를 보관해야 참조를 통해 연관된 객체를 찾을 수 있음
Team team = member.getTeam();
객체지향 모델링
객체는 참조를 통해서 관계를 맺으므로 참조를 사용하도록 모델링해야 함
외래 키의 값을 그대로 보관하는 것이 아니라 연관된 Team의 참조를 보관하기 때문에 회원과 연관된 팀 조회 가능
하지만 객체지향 모델링의 경우, 객체 모델은 외래 키가 필요 없고 단지 참조만 있으면 되는 반면
테이블은 참조가 필요 없고 외래 키만 있으면 되므로 개발자가 중간에서 변환 역할을 해야 함
// 참조를 사용하는 객체 모델
class Member {
String id; // MEMBER_ID 컬럼 사용
Team team // 참조로 연관관계를 맺음
Strign username; // USERNAME 컬럼 사용
Team getTeam() {
return team;
}
}
class Team {
Long id; // TEAM_ID PK 사용
String name; // NAME 컬럼 사용
}
// 저장
// 객체를 데이터베이스에 저장하려면 team 필드를 TEAM_ID 외래 키 값으로 변환해야 함
// 그 후 INSERT SQL문을 만듦
member.getId(); // MEMBER_ID PK에 저장
member.getTeam().getId(); // TEAM_ID FK에 저장
member.getUsername(); // USERNAME 컬럼에 저장
// 조회
// TEAM_ID 외래 키 값을 Member 객체의 team 참조로 변환해서 객체에 보관하기 위해
// MEMBER와 TEAM을 조회하는 SQL을 실행한 후
// SQL의 결과로 객체를 생성하고 연관관계를 설정해서 반환
public Member find(String memberId) {
// SQL 실행
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
Member member = new Member();
...
// 데이터베이스에서 조회한 회원 관련 정보를 모두 입력
Team team = new Team();
....
// 데이터베이스에서 조회한 팀 관련 정보를 모두 입력
...
// 회원과 팀 관계 설정
member.setTeam(team);
return member;
}
이런 과정 속에서 패러다임의 불일치를 해결하려는 소모 비용이 발생
JPA와 연관관계
JPA는 연관관계와 관련된 패러다임의 불일치 문제를 해결해 줌
개발자는 회원과 팀의 관계를 설정하고 회원 객체를 저장하면 되고,
JPA는 team의 참조를 외래 키로 변환해서 적절한 INSERT SQL을 데이터베이스에 전달
즉, JPA가 객체를 조회할 때 외래 키를 참조로 변환하는 일과 SQL 생성 모두를 처리
// 개발자는 회원과 팀의 관계를 설정하고 회원 객체를 저장
member.setTeam(team); // 회원과 팀 연관관계 설정
jpa.persist(member); // 회원과 연관관계 함께 저장
// 조회의 경우
Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();
3. 객체 그래프 탐색
객체에서 회원이 소속된 팀을 조회할 때 참조를 사용해서 연관된 팀을 찾는 것을 객체 그래프 탐색이라 함
// 자유로운 객체 그래프 탐색의 예
Team team = member.getTeam();
member.getOrder().getOrderItem();
객체는 마음껏 객체 그래프를 탐색할 수 있어야 하지만,
SQL을 직접 다루어 실행하면 처음 실행하는 SQL에 따라 객체 그래프를 어디까지 탐색할 수 있는지 정해지므로
객체지향 개발자에겐 너무 큰 제약이 되어 언제 끊어질지 모르는 객체 그래프를 함부로 탐색할 수 없어짐
그러므로 어디까지 객체 그래프 탐색이 가능한지 알아보려면 데이터 접근 계층인 DAO를 열어서 SQL을 직접 확인해야 하며
이는 엔티티가 SQL에 논리적으로 종속되어 있어 발생하는 문제
이를 위해 모든 객체 그래프를 데이터베이스에서 조회해서 애플리케이션 메모리에 올려두는 것은 현실성이 없는 방안이므로
상황에 따라 조회하는 메소드를 만들어서 사용해야 하는 번거로움
// MemberDAO에서 member 객체를 조회할 때의 SQL로 회원과 팀에 대한 데이터만 조회
// 이 경우 member.getTeam()은 성공하지만
// 다른 객체 그래프인 member.getOrder()은 데이터가 없으므로 null이 되어 탐색 불가
SELECT M.*, T.
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
JPA와 객체 그래프 탐색
JPA는 연관된 객체를 사용하는 시점에 적절한 SELECT SQL을 실행하므로 연관된 객체를 마음껏 조회할 수 있음
이러한 기능은 실제 객체를 사용하는 시점까지 데이터베이스 조회를 미룬다고 해서 지연 로딩이라고 함
또한 JPA는 지연 로딩을 투명하게 처리하므로 어떠한 코드의 작성이 필요하지 않음
// 투명한 엔티티
class Member {
private Order order;
public Order gerOrder() {
return order;
}
}
// 지연 로딩 사용
// 처음 조회 시점에 SELECT MEMBER SQL
Member member = jpa.find(Member.class, memberId);
Order order = member.getOrder();
order.getOrderDate(); // Order를 사용하는 시점에 SELECT ORDER SQL
만약 Member를 사용할 때마다 Order를 함께 사용한다면, 한 테이블씩 조회하는 것보다는
Member를 조회하는 시점에 SQL 조인을 사용해서 Member와 Order를 함께 조회하는 것이 효과적
이를 위해서 JPA는 연관된 객체를 즉시 함께 조회할지 아니면 실제 사용되는 시점에 지연해서 조회할지를 간단한 설정으로 정의
// 만약 Member와 Order를 즉시 함께 조회하겠다고 설정하면
// JPA는 Member를 조회할 때 연관된 Order도 함께 조회하도록 SQL 실행
SELECT M.*, O.*
FROM MEMBER M
JOIN ORDER O ON M.MEMBER_ID = O.MEMBER_ID
4. 비교
데이터베이스는 기본 키의 값으로 각 로우를 구분
객체는 동일성 비교 (==, 객체 인스턴스의 주소 값을 비교)와 동등성 비교 (equals(), 객체 내부의 값 비교) 두 가지 방법이 존재
그러므로 테이블의 로우를 구분하는 방법과 객체를 구분하는 방법에는 차이가 존재
만약 객체를 컬렉션인 list에 보관했다면 동일성 비교에 성공했을 것이므로 패러다임 불일치 문제 발생
// MemberDAO 코드
class MemberDAO {
public Member getMember(String memberId) {
String sql = "SELECT * FROM MEMBER WHERE MEMBER_ID = ?"
...
// JDBC API, SQL 실행
return new Member(...);
}
}
// 조회한 회원 비교하기
String memberId = "100";
Member member1 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);
member1 == member2; // false
// 같은 데이터베이스 로우에서 조회했지만, 객체 측면에서 볼 때는 둘은 다른 인스턴스
// MemberDAO.getMember()를 호출할 때마다 new Member()로 인스턴스가 새로 생성되므로
JPA와 비교
JPA는 같은 트랜잭션일 때 같은 객체가 조회되는 것을 보장
// 조회한 회원 비교하기
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
member1 == member2; // true
JPA란 무엇인가?
- JPA는 자바 진영의 ORM 기술 표준으로 애플리케이션(자바 애플리케이션)과 JDBC(JDBC API) 사이에서 동작
- ORM(Object-Relational Mapping)은 객체와 관계형 데이터베이스를 매핑한다는 뜻이며,
ORM 프레임워크는 객체와 테이블을 매핑해서 패러다임 불일치 문제를 개발자 대신 해결해줌
객체를 데이터베이스에 저장하면 ORM 프레임워크가 적절한 INSERT SQL을 생성해서 데이터베이스에 객체를 저장 - 저장을 위해 PERSIST 시, Entity 분석, INSERT SQL 생성, JDBC API 사용, 패러다임 불일치 해결을 JPA가 개발자 대신 해줌
jpa.persist(member); // 저장
조회를 위해 FIND 시, SELECT SQL 생성, JDBC API 사용, ResultSet 매핑, 패러다임 불일치 해결을 JPA가 개발자 대신 해줌
Member member = jpa.find(memberId); // 조회
1. JPA 소개
JPA는 하이버네이트라는 오픈소스 ORM 프레임워크를 기반으로 만들어진 새로운 자바 ORM 기술 표준
JPA는 자바 ORM 기술에 대한 API 표준 명세로써 인터페이스를 모아둔 것
따라서 JPA를 사용하려면 JPA를 구현한 ORM 프레임워크를 선택해야하며, 하이버네이트가 가장 대중적인 ORM 프레임워크
JPA라는 표준 덕분에 특정 구현 기술에 대한 의존도를 줄일 수 있고 다른 구현 기술로 손쉽게 이동할 수 있는 장점이 존재
2. 왜 JPA를 사용해야 하는가?
생산성
JPA를 사용하면 JPA에 저장할 객체를 전달하면 SQL을 작성하고 JDBC API를 사용하는 지루하고 반복적인 일을
JPA가 대신 처리해 주므로 데이터베이스 설계 중심의 패러다임을 객체 설계 중심으로 역전시킬 수 있음
유지보수
SQL을 직접 다루면 엔티티에 필드를 하나만 추가해도 관련된 SQL과 결과를 매핑하기 위한 JDBC API 코드를 모두 변경해야 함
반면 JPA를 사용하면 이러한 과정을 JPA가 처리해주므로 수정할 코드가 줄어들어 유지보수해야 하는 코드 수가 줄어듦
또한 패러다임의 불일치 문제를 해결해주므로 객체지향 언어를 가지고 유연하고 유지보수하기 좋은 도메인 모델 설계 가능
패러다임의 불일치 해결
JPA는 상속, 연관관계, 객체 그래프 탐색, 비교하기 같은 패러다임의 불일치 문제를 해결
성능
JPA는 애플리케이션과 데이터베이스 사이에서 동작하므로 다양한 성능 최적화 기회를 제공
예) 같은 트랜잭션 안에서 같은 회원을 두 번 조회할 시에
SELECT SQL을 사용해 데이터베이스와 두 번 통신했던 것과 다르게
JPA를 사용하면 한 번만 SELECT SQL을 데이터베이스에 전달하고 두 번째는 조회한 회원 객체를 재사용
String memberId = "helloId";
Member member1 = jpa.find(memberId);
Member member2 = jpa.find(memberId);
데이터 접근 추상화와 벤더 독립성
관계형 데이터베이스는 같은 기능도 벤더마다 사용법이 다른 경우가 많아
결국, 애플리케이션은 처음 선택한 데이터베이스 기술에 종속되고 다른 데이터베이스로 변경하기가 어려움
하지만 JPA는 애플리케이션과 데이터베이스 사이에 추상화된 데이터 접근 계층을 제공해서
애플리케이션이 특정 데이터베이스 기술에 종속되지 않도록 하므로
만약 데이터베이스를 변경하더라도 JPA에게 다른 데이터베이스를 사용한다고 알려주기만 하면 됨
표준
JPA는 자바 진영의 ORM 기술 표준이므로 다른 구현 기술로 손쉽게 변경 가능
'Java-Spring > 자바 ORM 표준 JPA 프로그래밍' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 연관관계 매핑 기초 (0) | 2022.03.29 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] 엔티티 매핑 - 실전 예제 (0) | 2022.03.26 |
[자바 ORM 표준 JPA 프로그래밍] 엔티티 매핑 (0) | 2022.03.23 |
[자바 ORM 표준 JPA 프로그래밍] 영속성 관리 (0) | 2022.03.20 |
[자바 ORM 표준 JPA 프로그래밍] JPA 시작 (0) | 2022.03.15 |