[김영한 - JPA 기본] #1 소개

문제점

단순한 반복의 SQL 코드가 무한히 반복된다

  • 특히 UPDATE, INSERT, DELETE
  • 자가객체를 SQL로, 그 반대로 계속 바꾸어주어야 한다

예를들어 Member 클래스에 tel이란 컬럼이 생기면 CRUD 쿼리에 모두 추가된 컬럼을 만들어야 한다

OOP의 (추상화, 캡슐화, 정보은닉, 상속, 다형성) 장점을 지키기 힘들고 RDB에 의존적인 설계로 가게 된다

개발자는 SQL 매퍼꾼이다

누가? 너와 내가… ㅠㅠ

RDB에도 같지는 않지만 상속관계와 유사한 슈퍼, 서브타입이란 것이 존재한다

이렇게 상속관계를 이용해서 코딩을하려고 해도

ITEM을 상속한 ALBUM, MOVIE, BOOK 이 있다고 하자

INSERT INTO ITEM

INSERT INTO ALBUM

과 같은 쿼리가 만들어지며 각 쿼리를 VO 객체로 만들어야 한다

mybati.insertQuery("Item 매퍼ID", item);
mybati.insertQuery("Album 매퍼ID", album);

만일

저장

만일 자바 컬렉션에 저장할 수 있다면

list.add(album)

위와 같은 작업으로 album 객체를 저장할 수 있다

조회를 한다면?

Album album = list.get(albumId);

아래처럼 다형성을 활용할 수도 있다

Item item = list.get(albumId);

String itemName = item.getName();
...

연관 관계 비교

객체는 참조를 통해서 연관 있는 객체를 가져올 수 있고

테이블은 외래키(FK)를 통해서 다른 테이블을 가져올 수 있다

class Member {
  Long id;
  String username;
  Team team;

  Team getTeam() {
    return team;
  }
}
class Team {
  Long id;
  String name;
}
member.getTeam().getId();

만일 우리가 member를 조회하는 로직을 만든다면

아래와 같이 Member, Team 뿐만 아니라 연관 관계를 설정하는 부분도 만들어야 할 것이다

public Member find(String memberId) {
  Member member = new Member();
  Team team = new Team();

  member.setTeam(team);

  return member;
}

그런데 만일 Member객체에 Order(주문)이란 테이블의 관계도 있다면??

member.getOrder().getId(); // NPE 문제 발생

Member객체에 Order 연관 관계를 set해주지 않으면 NPE 문제가 발생한다 !!

그러면 Order를 같이 조회하는 메서드도 만들어야 한다

memberDAO.getMemberWithOrder();

위 뿐만 아니라 Delevery 등의 객체가 있다면..

memberDAO.getMemberWithOrderWithDelivery();

서비스단에서 DAO의 메서드를 사용하려는데..

DAO계층을 신뢰할 수 없다면

계층형 아키텍쳐가 어렵게 된다 (계층분할의 어려움)

다른 문제점

class MemberDao {
  public Member getMember(String memberId) {
    String sql = "SELECT * FROM MEMBER WHERE MEMBER_ID = ?";
    ...
    return new Member(..);
  }
}

String memberId = "101";
Member member1 = memberDao.getMember(memberId);
Member member2 = memberDao.getMember(memberId);

member1 == member2; // 다르다

위와같은 로직을 실행하게 되면 new 연산에 의해 나온 Member는 서로 다르게 된다

그러나 이건 지극히 내 생각이지만 Equals And HashCodeOverride하면 같으니 크게 문제가 될 것 처럼 보이지는 않는다..

물론 이마저도 JPA가 알아서 해준다는 것이지만 …

아래와 같이 자바 코드를 작성해보았지만…

hashCode까지 같게 하는 것은 되었지만 주소 비교까지 같게하는 것은 실패되었다

public class IntroThinkingMain {

	private static List<Member> members = new ArrayList<>();

	static {
		members.add(new Member(1L, "memberA"));
		members.add(new Member(2L, "memberB"));
	}

	public static void main(String[] args) {
		Member member1 = getMember(1L);
		Member member2 = getMember(1L);

		System.out.println("(member1 == member2) = " + (member1 == member2));
		System.out.println("member1.hashCode() = " + member1.hashCode());
		System.out.println("member2.hashCode() = " + member2.hashCode());
		System.out.println("member1 = " + member1);
		System.out.println("member2 = " + member2);
		System.out.println("member1.equals(member2) = " + member1.equals(member2));
	}

	public static Member getMember(Long memberId) {
		Member findMember = members.stream()
			.filter(member -> member.getId().equals(memberId))
			.findAny()
			.get();
		return new Member(findMember.getId(), findMember.getName());
	}

	static class Member {
		Long id;
		String name;

		public Member(Long id, String name) {
			this.id = id;
			this.name = name;
		}

		// getter

    // equals and hashCode

	}
}

실행결과

(member1 == member2) = false
member1.hashCode() = 948882631
member2.hashCode() = 948882631
member1 = jpa.IntroThinkingMain$Member@388eccc7
member2 = jpa.IntroThinkingMain$Member@388eccc7
member1.equals(member2) = true

자바 컬렉션에 저장할 수 있다면

System.out.println("======== 자바 컬렉션 조회 ========");
Member member1 = members.get(0);
Member member2 = members.get(0);

System.out.println("(member1 == member2) = " + (member1 == member2));

위와 같이 주소로도 비교할 수 있게 된다 !!

JPA


  • Java Persistence API
  • 자바 진영 ORM 표준 기술

  • Java 객체는 객체스럽게 설계
  • RDB는 RDB스럽게 설계
  • ORM 프레임워크가 중간에서 매핑한다

EJB 이후에 Hibernate가 , 그 이후에 JPA 표준이 만들어진다

JPA 구현체로는 Hibernate, EclipseLink, DataNucleus가 있다

JPA의 장점

  • SQL 중심 개발에서 객체 중심으로 개발 (패러다임 불일치 해결)
  • 데이터 접근 추상화와 벤더 독립성
    • JPA를 통해서 접근하면 MySQL이든 Oracle, Postgre든 상관 논노데스
  • 유지보수
    • 짧은 코드
    • 여러 군데를 고칠 필요 없음
  • 생산성

JPA와 CRUD

Create

jpa.persist(member)

발생 쿼리

INSERT INTO ITEM VALUES('', '');
INSERT INTO ALBUM VALUES('', '');

상속관계를 가지는 두 쿼리가 동시에 실행된다

  • Read
Album album = jpa.find(Album.class, albumId);

발생 쿼리

SELECT I.*, A.*
FROM ITEM I
  JOIN ALBUM A
    ON I.ITEM_ID = A.ITEM_ID
  • Update
member.setName("변경할 이름");
  • Delete
jpa.remove(member);

JPA로 해결된 문제들

member.getOrder().getDelivery();

위와 같이 객체 그래프 탐색을 할때 NPE문제가 발생하지 않는다

String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
member1 == member2;
  • 객체가 같다 !!

JPA로 인한 성능 최적화

  • 1차 캐시와 동일성 보장

    • 같은 트랜잭션 안에서는 같은 엔티티를 반환
  • 지연로딩과 즉시 로딩

    • 지연로딩 : 객체가 실제로 사용될 떄 로딩
      Team team = member.getTeam()
      String teamName = team.getName(); // 이순간 SELECT ~ TEAM 실행
      
    • 즉시로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회
      • SELECT M.*, T.* FROM MEMBER JOIN TEAM 실행

그외 Java11 설정법 !

javax.xml.bind.JAXBException
https://www.inflearn.com/questions/13985





© 2020.12. by 따라쟁이

Powered by philz