Life Developer
인생 개발자
전체 글 (141)
[JPA]프록시객체 - getReference (와 find 차이)

 

프록시는 이렇게 만든다.

 

.getReference(~~~)

 

실제 사용 예는 밑에서..

 

 

 

 

 

 

 

아래 설명도 써놨지만 Member findMember = em.getReference(Member.class, member.getId());

이런 식으로 사용한다.

 

곧 findMember는 프록시이다.


Member findMember = em.find(Member.class, member.getId()); 와는 달리

Member findMember = em.getReference(Member.class, member.getId()); 를 딱 실행하면

 

select 쿼리가 db로 안날라간다.

 

즉, findMember는 진짜 엔티티가 아니라 가짜 엔티티이며, 실제 객체의 참조를 보관한다.

Member findMember = em.getReference(Member.class, member.getId()); 를 실행하면

프록시 객체를 가져온다. (가짜 엔티티, 엔티티가 아님)

그 객체에는 get요청한 Id 정보만 있다.

그래서 System.out.println(findMember.getId()); 를 하면 Id가 출력이 된다.

db에 갈 필요가 없다는것이다.

 

근데 딱 findMember.getUserName() 을 하는순간 가짜 엔티티(프록시객체)에는 name이 없기 때문에

DB를 통해 name 값을 가져온다. 이것을 프록시 객체의 초기화라고 한다.

 

프록시의 초기화는 딱 한번만 이루어진다.

 

 

 

 

Member member = new Member();
member.setUserName("hello");

em.persist(member);

em.flush();
em.clear();

// Member findMember = em.find(Member.class, member.getId());
Member findMember = em.getReference(Member.class, member.getId());
System.out.println(findMember.getClass()); //class hellojpa.Member$HibernateProxy$7XonaGYN 가짜클래스임(proxy가만듦)
System.out.println(findMember.getId());//이미 위에서 id로 찾았기 때문에 db에서 안가져옴
System.out.println(findMember.getUserName()); //모르는 정보니까 이때 db에 쿼리를 날려서 team이랑 조인함

System.out.println(" =============== ");
tx.commit(); //이시점에 DB 쿼리가 날라감

 

 

 

 

======================================================================

 

그리고 주의 할점도 있다.

 

아래처럼 진짜 member entity를 영속성 컨텍스트에 등록하고 clear 없이 getReference를 하면 영속성 컨텍스트에

등록되어 있는 member를 가지고 온다.

그래서 이전것을 재활용한다.

 

하지만 clear()를 호출해 영속성 컨텍스트를 비우고 getReference를 하면 프록시객체를 불러오게 되므로

서로 같은 객체가 아니게 된다.

 

getReference를 했는데 프록시 객체가 아닌 상태가 되는것이다.......ㅎ후

 

Member m1= em.find(Member.class,member1.getId());
System.out.println("m1.getClass() = " + m1.getClass());
em.clear();
Member m2= em.getReference(Member.class,member1.getId());
System.out.println("m2.getClass() = " + m2.getClass());

System.out.println(" =============== ");
tx.commit(); //이시점에 DB 쿼리가 날라감

 

 

여기서 더 심화해보자.

 

Member m1= em.getReference(Member.class,member1.getId());
System.out.println("m1.getClass() = " + m1.getClass());

Member m2= em.find(Member.class,member1.getId());
System.out.println("m2.getClass() = " + m2.getClass());
System.out.println("m1==m2"+(m1.getClass() == m2.getClass()));

System.out.println(" =============== ");

 

그렇다면 이건 어떻게 나올까?

1. 내가 예상한것은 영속성 컨텍스트에 프록시 객체가 담길것이고,

2. find 해서 가져오려 했으나 영속성 컨텍스트에 Member객체가 있기에 db로 안가고

    프록시 객체를 그냥 가져와서 true가 나온다...

 

가 예상이었다.

 

근데 결과는 2번이 약간 달랐다.

일단 db로 쿼리는 select 때린다.

근데 가져온것은 프록시 객체.  ㅡㅡ.... 아니 DB왜 간거지.

 

그냥 DB까지 갔으나 영속성 컨텍스트에 있는것을 가져다 쓴다...기억하자

 

 

 

=============================================================

 

프록시 초기화가 되었는지 확인 하는법도 있다.

EntityManageFactory 객체 .getPersistenceUnitUtil().isLoaded(프록시객체) 를 하면 boolean 값으로 나오게 된다.!

 

Member m1 = em.getReference(Member.class, member1.getId());
System.out.println("m1.getClass() = " + m1.getClass());

m1.getId();
System.out.println("초기화 되었냐?" + emf.getPersistenceUnitUtil().isLoaded(m1));
m1.getUserName();
System.out.println("초기화 되었냐?" + emf.getPersistenceUnitUtil().isLoaded(m1));

System.out.println(" =============== ");
tx.commit(); //이시점에 DB 쿼리가 날라감

 

 

 

여기까지 이해했으면 프록시 정복한거임 푸헤ㅔㅎ

 

 

  Comments,     Trackbacks
[JPA]@MappedSuperClass - 공통된 컬럼 관리

공통의 매핑정보가 필요할때 사용.

 

 

공통된놈이 객체마다 있으면 이것도 중복이다.

 

이것을 JPA에서 공통클래스를 만들어 관리할수 있다.

 

아래처럼 클래스 하나 만들어서 @MappedSuperClass를 해주면 된다.

물론 @Column을 사용해서 이름도 바꿀수있다.

 

그런다음 사용할 클래스에 extends를 쓰면됨.

 

근데 상속관계 매핑은 아니다!!

 

Entity가 아니다. 테이블도 만들지 않음.

 

MappedSuperClass는 조회 검색 불가함.

 

직접 생성해서 사용하는게 아니라 abstract 클래스로 만드는것 추천

 

 

 

 

 

@MappedSuperclass
public class BaseEntity {

@Column(name="Insert_Member")
private String createBy;
private LocalDateTime createDate;
private String lastModifiedBy;
private LocalDateTime lastModifiedDate;

public String getCreateBy() {
return createBy;
}

public void setCreateBy(String createBy) {
this.createBy = createBy;
}

public LocalDateTime getCreateDate() {
return createDate;
}

public void setCreateDate(LocalDateTime createDate) {
this.createDate = createDate;
}

public String getLastModifiedBy() {
return lastModifiedBy;
}

public void setLastModifiedBy(String lastModifiedBy) {
this.lastModifiedBy = lastModifiedBy;
}

public LocalDateTime getLastModifiedDate() {
return lastModifiedDate;
}

public void setLastModifiedDate(LocalDateTime lastModifiedDate) {
this.lastModifiedDate = lastModifiedDate;
}
}

  Comments,     Trackbacks
[JPA] 상속매핑(Inheritance) 전략 - TABLE_PER_CLASS

쓰면 안되는 전략..

ORM세계는 객체지향 프로그래머, DBA 의 역할이있는데 둘다 꺼려함.

뭔가 묶어낼게 없음.

만약 정산같은거 한다고 하면 복잡해지는거임. 노답이다 그냥.

 

장점 : not null제약조건 사용가능. (DBA가 이딴 이유로 이걸 쓰라고 하면 . 뺨떄리면됨)

 

 

 

상속전략에 마지막이 TABLE_PER_CLASS이다.

이 전략을 간단히 말하면 부모 테이블은 그냥 만들지도 않는다.

부모테이블의 속성을 자식테이블에 그냥 테이블 마다 다때려박는다. 무식하다.

아래처럼 테이블이 만들어진다.

DTYPE 그딴거 필요도 없음.(@Discriminator)

 

 

 

 

뭐 이런식이다.

 

뭔가 비효율적인 느낌이 든다.

 

우리가 select 때려서 조회한다고 치면 만약 ITEM_ID가 5라고 하자.

그럼 5를 찾으려면 테이블 3개 다뒤져봐야한다. 미치고 환장하는거다ㅋ

 

직접 실험해보았다.

 

결과는?

 

 

ㅋㅋ union all하고 난리남.

 

아주 골떄리는 놈이다. 그걸 자동으로 해주는 JPA 이놈도 미띤놈임

  Comments,     Trackbacks
[JPA] 상속매핑(Inheritance) 전략 - JOINED*

이게 정석 전략임

 

장점 : 테이블 정규화, 외래키 참조 무결성 제약조건 활용 가능,저장공간 효율, 설계가 깔끔함

단점 : 조회시 조인을 많이함. 성능저하(조회쿼리 복잡), 저장시 insert 쿼리 2번 호출(생각보다 단점 아님)

 

싱글 테이블 상속 전략과는 약간 차이가 있다.

그냥 @Inheritance에 타입을 JOINED라고만 고치면 된다.

그럼 테이블이 각각 생성이된다.

뭐 이런식으로.

 

 

여기서 나는 DTYPE 의 이름을 TYPE_NAME으로 내맘대로 정해주었다.

그럼 컬럼명이 바뀐다.

JOINED 은 SINGLE_TABLE과는 달리 @DiscriminatorColumn 을 안써줘도 된다.

나는 굳이 써준거다.

빼면 이렇다.

 

 

굳이 타입을 정하지 않아도 뭐 개발하는 사람 입장에서는 무엇인지 유추가 가능하니까 안써도 된다.

시스템 운영할때는 사용하는것이 편하다.

 

JOINED는 테이블이 어떻게 생성될까.

create table Item (
       id bigint not null,
        name varchar(255),
        price integer not null,
        primary key (id)
    )

 

create table Movie (
       actor varchar(255),
        director varchar(255),
        id bigint not null,
        primary key (id)
    )

 

알아서 이렇게 생성해준다.

FK는? 이것도 뭐 자동이지 뭐.

alter table Movie 
       add constraint FK5sq6d5agrc34ithpdfs0umo9g 
       foreign key (id) 
       references Item

 

ㅋㅋ대박이네!

 

 

 

 

 

 

 

아래는 실습코드다.

 

 

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name="TYPE_NAME")
public class Item {
@Id
@GeneratedValue
private Long id;

private String name;
private int price;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}
}

 

 

 

@Entity
@DiscriminatorValue("MV")
public class Movie extends Item{
private String director;
private String actor;

public String getDirector() {
return director;
}

public void setDirector(String director) {
this.director = director;
}

public String getActor() {
return actor;
}

public void setActor(String actor) {
this.actor = actor;
}
}

  Comments,     Trackbacks
[JPA] 상속매핑(Inheritance) 전략 - SINGLE_TABLE

장점 : 조인이 필요가없다. 성능빠름, 조회쿼리가 단순함

단점 : 자식 entity가 안가지는 컬럼의 값을 null 허용해야함....(치명적, DBA 개빡침), 테이블이 커질수있다. 오히려 역효과 날수 있다.

 

 

상속을 받아서 테이블을 만들수도 있다.

 

기본적으로 @Entity는 당연히 써줘야 하는거고.

단순히 Movie클래스가 Item 클래스를 extends로 상속받는다고 입력하면 됨.

그리고 부모에 @Inheritance를 입력하면 default 값으로 상속 타입을 SINGLE_TABLE을 사용한다.

그럼 성능상 좋긴하다. 

그리고 싱글 타입은 DTYPE이라고 자동 생성을해준다.

무슨 클래스를 가져왔는지 표시를 해준다는 것이다.

실행해보면 테이블 생성시 DTYPE이라고 자동 생성된다.

그리고 DTYPE이 아니라 컬럼명을 바꾸고 싶다면 @DiscriminatorColumn(name="~~~") 을 써주면 된다.

진짜 너무 JPA에서 자동으로 이놈이 무슨클래스지, 어디서 온놈이지? 라는것을 자동으로 생각해주니까 좀 이상하다.

적응이 안된다.

 

 

설마 이것까지 자동으로 해줄까? 라는 의심을 품고 실행하면 해주고 있다.

테이블을 DB에서 쿼리칠 필요가 없어서 미친듯이 편하다. 이런 미띤 ㅡㅡ

 

아래는 싱글 테이블 실습.

 

 

create table Item (
       TYPE_NAME varchar(31) not null,
        id bigint not null,
        name varchar(255),
        price integer not null,
        artist varchar(255),
        autor varchar(255),
        isbn varchar(255),
        actor varchar(255),
        director varchar(255),
        primary key (id)
    )

 

 

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="TYPE_NAME")
public class Item {
@Id
@GeneratedValue
private Long id;

private String name;
private int price;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}
}

 

 

 

@Entity
@DiscriminatorValue("MV")
public class Movie extends Item{
private String director;
private String actor;

public String getDirector() {
return director;
}

public void setDirector(String director) {
this.director = director;
}

public String getActor() {
return actor;
}

public void setActor(String actor) {
this.actor = actor;
}
}

 

 

  Comments,     Trackbacks
[JPA]연관관계의 주인을 어떻게 정할까

 

연관관계의 주인은 어떻게 정할까.

 

일단 주인이 아닌 쪽의 키에 mappedBy는 필수이다.

 

@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();

 

이렇게 말이다.

 

멤버와 팀 중에 주인이 Member라는 뜻이고 team의 List members는 member의 team 프로퍼티 객체를 찬양한다.

 

수많은 이유가 있지만 일단은 외래키를 가지고 있는 쪽을 주인으로 두자.

 

Member 테이블의 team이 FK이기 때문에 team이 주인이며, mappedBy를 쓰지 않는다.

 

나로썬 그냥 한 집합체를 주인으로 보고, List 형태인 많은 자식들을 일단 자식으로 생각을 하고있지만 무조건 이런것

 

같지도 않다.

 

그리고 주인만 등록 수정이 가능하며 자식은 조회만 가능하다.

'Developer' 카테고리의 다른 글

[JPA] 상속매핑(Inheritance) 전략 - JOINED*  (0) 2020.09.19
[JPA] 상속매핑(Inheritance) 전략 - SINGLE_TABLE  (0) 2020.09.19
[JPA]identity전략 특성  (0) 2020.09.18
[JPA]DDL 생성기능  (0) 2020.09.16
[JPA]스키마 자동생성  (0) 2020.09.16
  Comments,     Trackbacks
[JPA]identity전략 특성

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

 

GenerationType.IDENTITY를 사용하면

회원 등록할경우 pk가 어떤값이 들어가는지 알수가 없기 떄문에 바로 find를 못한다.

 

identity경우에만 em.persist(member) 를 하면 쿼리가 db로 들어간다.

(원래 em.persist(member)를 하면 영속상태가 되지 db에 쿼리가 날라가는것은 commit할 경우였다)

 

이게 특성임.

아래는 코드

 

 

Member member = new Member();
member.setUsername("C");

System.out.println(" =============== ");
em.persist(member);
System.out.println("member.getId() = " + member.getId());
System.out.println(" =============== ");
tx.commit(); //이시점에 DB 쿼리가 날라감

'Developer' 카테고리의 다른 글

[JPA] 상속매핑(Inheritance) 전략 - SINGLE_TABLE  (0) 2020.09.19
[JPA]연관관계의 주인을 어떻게 정할까  (0) 2020.09.18
[JPA]DDL 생성기능  (0) 2020.09.16
[JPA]스키마 자동생성  (0) 2020.09.16
[JPA]Entity  (0) 2020.09.16
  Comments,     Trackbacks
[JPA]DDL 생성기능

@Column(name="aaa")

로 하면 이름이 aaa로 생성, 매핑됨.

 

@Column(unique=true, length=10)

으로 하면

 

create table Member (
       id bigint not null,
        age integer not null,
        name varchar(10),
        primary key (id)
    )

 

alter table Member 
       add constraint UK_ektea7vp6e3low620iewuxhlq unique (name)

 

 

이렇게 테이블이 자동생성됨. ㅋㅋㅋ

 

@Table(name="aa") 은 실행할때도 영향을 주는 반면

@Column(unique=true, length=10) 이런것은 DB에 영향을주는 것이다.

실행자체에 영향을 주지 않고 DDL 생성할때만 영향을 준다는 것이다.

'Developer' 카테고리의 다른 글

[JPA]연관관계의 주인을 어떻게 정할까  (0) 2020.09.18
[JPA]identity전략 특성  (0) 2020.09.18
[JPA]스키마 자동생성  (0) 2020.09.16
[JPA]Entity  (0) 2020.09.16
[JPA]영속성과 준영속성 관리, flush  (0) 2020.09.16
  Comments,     Trackbacks