본문 바로가기

Spring&IntelliJ

JPA 객체와 테이블 매핑할때 사용되는 어노테이션2 @Id, @Access

https://www.youtube.com/watch?v=dw3PxjDGkT8

요약

(지금 내 생각으로는 @Entity어노테이션으로 해당 클래스가 영속성 클래스로 jpa에 의해 관리되어 지는 클래스이기는 하지만 영속성 클래스 객체가 PersistenceManager에게 관리되어 지는 것과는 다른 문제임. 영속성 객체로 관리되어 지려면 EntityManager클래스의 persist함수의 매개변수로 해당 객체가 들어가야 한다. 다만 @Entity 어노테이션으로 해당 클래스를 영속성 클래스로 만든다면 다음과 같은 효과가 있다. 

구체적으로 클래스에 @Entity가 붙는것이 영속객체와 어떤 상관관계가 있는 것인가? 예를들어 em.find(Customer.class, "Id001")이라는 EntityManager의 함수find가 있을경우 이 함수는 주워진 매개변수를 이용하여 Entity객체를 생성, 반환해야 한다. 당연히 매개변수와 관계된 데이터는 DB에 있었다는 말이고 특정 레코드에 있는 데이터를 이용해서 객체를 만드는 것이니 객체의 클래스는 그 데이터에 대응하는 생성자가 필요한 것이다. 따라서 클래스 자체에 @Entity라는 어노테이션을 붙여서 모든 필드정보와 생성자 정보를 제공하게 하여 생성된 객체가 영속성 컨텍스트에 의해 관리받는 영속객체가 되게 한다. 이러한 용도로 @Entity어노테이션이 필요한 것이다. 위의 이유말고 내가 찾은 또 다른@Entity어노테이션의 쓰임은 프로그램 시작시에 쿼리를 날리지 않고 Entity어노테이션을 사용한다면 자동으로 JPA에 의해 DB로 테이블 생성 쿼리를 날려준다는 것이다. 이는 persistence.xml파일과 @Entity의 옵션으로 uniqueConstraints를 사용하는 것과 관련되어 있다. 잉 밖에도 @Entity어노테이션의 쓰임은 더 있을것이다.

중요한 것은 나는 처음에 EntityManager에 의해 영속객체로 관리되어 지는데 왜 @Entity라는 어노테이션을 따로 사용하는지 이해가 되지 않았다. 하지만 둘의 차이점을 파악하려고 시도한 것이 가장 중요한 지점이었다.

@Access란? 영속성 객체의 멤버변수에 대한 접근 방식을 나타내는 나타내는 어노테이션

@Id란? 기본키 적용을 위한 어노테이션

주로 @Id가 쓰임. @Access는 잘 안쓰임.

 

@Id어노테이션과 함께 쓰이는 어노테이션이 있다. 예를들면 @GeneratedValue어노테이션으로, @GeneratedValue어노테이션이 있다면 이는 곧 DB에서 생성된 기본키를 받아서 사용하겠다는 의미이고 이 어노테이션에  적용되는 속성이 DB에서 기본키를 생성하는 방식과 연관되어 있음(본문참고)

1. @Access 어노테이션.

Access? 접근? 객체에 대한 접근을 의미함. JPA에 의해 어노테이션(@Entity)이 붙은 객체가 생성이 되고 그 객체의 데이터를 얻거나 저장할때 필드를 통해 바로접근을 할 것이냐 아니면 setter,getter등과 같은 메서드를 통해 접근할 것인가 하는 접근 방식을 달리할 수 있습니다. 그 접근 방식을 결정할 수 있는 어노테이션이 이 @Access어노테이션 입니다. (즉 아래에서 말하는 필드 접근 방식은 맴버변수를 통한 접근이고 프라퍼티 접근 방식은 메서드를 통한 접근방식임)

ex)db에서 데이터(record) 하나를 가져오면 JPA는 그 테이블과 매핑되어 있는 객체를 생성함. 그 생성된 객체의 필드에 기존의 가져온 데이터값을 넣음. 여기서 자바의 reflection을 이용해서 필드 접근 방식으로 바로 값을 넣는가 아니면 getter, setter메서드와 같은 property접근방식으로 값을 넣는가. 그 방식을 결정해 주는 어노테이션.

위 글처럼 @Id어노테이션도 객체에 대한 접근방식에 관한 어노테이션임. @Id가 필드에 붙어있으면 필드 접근방식으로 데이터에 접근하겠다는 것이고 메서드에 붙어있으면 프라퍼티 접근방식으로 데이터에 접근하겠다는 의미임. 보통 실무에서는 @Access어노테이션을 사용하지 않습니다. 보통은 @Id어노테이션을 필드에 직접 붙여서 사용하는 것이 보통입니다.

이와 같이 필드에 직접 @Id어노테이션을 붙여 필드 접근방식을 사용하는 이유는 가독성과 메서드를 통한 데이터 접근시 일부 문제점이 있을 수 있어 그렇습니다.

@Access어노테이션이 어떠한 의미를 가지고 있는지만 아시고 계시면 됩니다.

----------------------------------------------------------------------------------------------------------------------------------------------------------

 

기본키 적용 어노테이션

기본키란? 하나의 데이터, 하나의 영속객체, DB에서 하나의 레코드 (다 같은 말임) 를 구분할 수 있는 유일한 식별자

@IdClass, @EmbeddedId는 복합키와 관련된 어노테이션입니다.(지금은 다루지 않겠습니다. @Id만 다루겠습니다)

@Id를 생성(발행)하는 방식에 대해서 알아보기에 앞서 DB에서 기본키를 생성하는 방식들에 대해 알아보겠습니다.

알고 계시듯이 Sequence, Auto Increment와 같은 자동 증가시키면서 기본키를 생성시켜주는 기능이 db자체적으로도 존재

합니다. 기본키의 생성 주체가 Application에 있는지 DB에 있는지 먼저 결정해 주어야 합니다. 아래 슬라이드를 보면 알수 있듯이 @GeneratedValue어노테이션이 있다면 이는 곧 DB에서 생성된 기본키를 받아서 사용하겠다는 의미이고 이 어노테이션의 속성이 DB에서 기본키를 생성하는 방식과 연관되어 있음.

 

기본키 값을 자동 생성한다는 말은 DB를 통해 기본키를 생성한것을 적용한다는 말입니다.

위의 @GeneratedValue옵션 표를 보면 알수 있듯이 auto가 default임. 즉 DB에 따라 기본키 생성 전략을 알아서 선택하도록 되어 있음.

정석은 persist메서드를 실행하면 실제 DB에 데이터가 저장되어지는 것이 아니라 영속성 컨텍스트의 1차 cache에 데이터가 저장됨. 즉 JPA가 내부적으로 관리하는 영속성 컨텍스트의 모습을 보면 아래와 같은데, 원래 persist메서드를 호출하면 그 시점에는 JPA의 내부 영속성 컨텍스트안의 1차 캐시에는 아래와 같이 ID는 없고 Object만 있음. 하지만 DB에서 기본키를 생성하게 하고 Identity 기본키 생성 전략(@GeneratedValue의 strategy속성)을 사용하면 persist시점에 DB에 데이터를 insert하고 그 과정에서 생성되고 사용된 id값을 가져와서 아래 그림의 id값을 채운다는 것.

데이터가 생성된다는 것은 db의 테이블에 하나의 새로운 레코드가 추가되는 시점을 말하는 것임. 즉, Identity 기본키 생성전략을 사용하면 EntityManager가 persist함수를 호출하면 insert쿼리가 실행되므로 이 시점에 (DB에)데이터가 생성되면서 덩달아 기본키도 생성되는 것임.

DB에서 자동으로 pk가 생성되는 SQL명령어

위 슬라이드의 왼편에 있는 자바코드에서 처럼 내가 직접 pk인 id를 지정해 주고 있지 않음. 즉 PK는 DB에서 부여하는 것임.

 

persist()해주면 해당 객체는 jpa에 의해 영속성 객체로 관리되어 지기 시작하지만 실제 DB에 insert가 되는 시점은 (flush()되지 않는 이상은)coomit()되는 시점임. 그런데 jpa에 의해 영속성 객체로 관리되어 지는 객체 또한 그 객체의 pk값이 있어야 함. 즉 원래는 commit이 되어야 데이터가 테이블에 insert되어 기본키가 발급이 되는데 Identity기본키 생성전략이 사용되면 persist되는 시점에 insert 쿼리가 실행되어지고 기본키가 생성되어 집니다. 생성된 기본키는 해당 객체의 id변수에 전달되어져서 완전한 영속성 객체로 jpa에 의해 관리되어지는 것임(즉 결과적으로는 원래는 commit이 되어야 DB에 insert되고 기본키도 생성되는 것이지만 identity 기본키 생성전략에 의해 commit되기도 전에 persist를 하는 시점에 해당 객체의 영속성이 보장되는 것에 더해서 기본키가 생성되어지고 테이블에 데이터가 insert되는 것임. 즉 commit전에 해당 영속객체는 모든 데이터를 갖춘 상태임).

(지금 내 생각으로는 @Entity어노테이션으로 해당 클래스가 영속성 클래스로 jpa에 의해 관리되어 지는 클래스이기는 하지만 객체의 생성은 다른 문제인 것같음. @Entity로 등록된 클래스라도 그 객체가 영속 상태가 되려면 persist(object)메서드의 매개변수로 전달되어야 하는 것같음)

Insert SQL쿼리가 Commit되기 전에 Hibernate에 의해 작성, 실행된것을 볼수 있음. 위의 커맨트와 isnert쿼리가 바로 Hibernate가 직접 만들어준 것입니다.

참고로,

Identity 기본키 생성 전략에 의해 JPA는 위와 같은 테이블 생성 쿼리를 작성하면서 generated by default as identity라는 "기본키가 자동으로 생성"되도록 하는 SQL문이 JPA에 의해 작성된 것을 확인할 수 있음.

반대로 @Id 어노테이션만 붙으면 당연히 insert쿼리는 commit이후에 나가게됨.

반면 @Id 어노테이션 마저 없으면 commit 이전에 DB에서 Id(Primary Key)를 얻어와야 하는 이유가 없어지기 때문에 commit 이전에는 Insert Query마저 DB로 날라가지 않고 commit이후에 날아감(실제로 다 해봄).

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

아래는 내가 실습한 내용이다.

중요 내용을 정리하면 이렇다.

1. find(select)문은 어떤 어노테이션 붙든 안붙든지를 떠나 1차 케시에서 찾고 만약 없다면 commit이전에라도 DB에 select쿼리를 날린다.

2. @Entity, @Table 어노테이션이 붙으면 프로그램이 시작되면서 테이블 생성에 관한 쿼리(create Table)를 날린다.

3. @GeneratedValue 어노테이션이 붙으면 commit이전에 DB에 데이터가 삽입되는 isnert구문이 나간다.(참고로 내가 찾는 데이터가 DB에 있건 없건간에 select(find)쿼리는 항상 우선시되어 DB로 나갈필요가 있으면 나가게 된다.

아래 코드는 내가 이 3가지를 정리하기 위해 실험해 본 실습코드와 그 결과이다.