Hibernate ORM 6.6 ๋ณ๊ฒฝ ์ฌํญ

Table of contents
- 1. Oracle ์์์ ๋ฐฐ์ด ํ์ ์ด๋ฆ ๊ฐ์
- 2. UserDefinedType ๊ตฌ์กฐ ๋ณ๊ฒฝ
- ๊ธฐ์กด ๋ฌธ์ ์
- ๊ฐ์ ๋ชฉ์
- AS-IS (Hibernate 6.5 ์ดํ)
- TO-BE (Hibernate 6.6)
- ์ด๋ค ์ํฉ์ ์ฌ์ฉํ ๊น?
- UserDefinedObjectType<T>
- UserDefinedArrayType<T>
- Hibernate๊ฐ ์ดํดํ๋ ๋ด๋ถ ๋ชจ๋ธ
- ์ด ๊ตฌ์กฐ์ ์ฅ์
- ๋งคํ ์์
- ๊ทธ๋ ๋ค๋ฉด Oracle UDT์ ๋ํ DDL ์๋ ์์ฑ์ด ๊ฐ๋ฅํ ๊น?
- Hibernate๊ฐ ์๋์ผ๋ก ์์ฑํ๋ ๊ฒ VS ์ํ๋ ๊ฒ
- Hibernate๊ฐ CREATE TYPE์ ์ง์ํ์ง ์๋ ์ด์
- Hibernate์ Oracle UDT๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
- Hibernate 6.6์ ์ญํ
- 3. ๋ฐฐ์ด ๋ถ๋ถ ํฌํจ ์ฌ๋ถ ๊ฒ์ฌ ํจ์ ๋ณ๊ฒฝ
- 4. ์ญ์ ๋ Entity ๋ณํฉ ์ ์์ธ ์ฒ๋ฆฌ ๊ฐํ
- 5. ํด๋์ค ์ด๋ ธํ ์ด์ ์ค๋ณต ์ฌ์ฉ ๊ธ์ง
- 6. @Embeddable ๋คํ์ฑ ์ง์ (Discriminator ๊ธฐ๋ฐ)
- 7. H2 Bulk Mutation ์ ๋ต ๋ณ๊ฒฝ
- 8. Expression#as(Class) โ case() ๋ช ํํ
- 9. @Table + SINGLE_TABLE ์์ ์ ๋ต ์ถฉ๋ ๊ฒ์ถ

1. Oracle ์์์ ๋ฐฐ์ด ํ์ ์ด๋ฆ ๊ฐ์
์ฃผ๋ก ์ฌ์ฉ๋๋ ์ํฉ
Oracle DB์์ Java ๋ฐฐ์ด ํ์ (
Array<T>
)์ ์ปฌ๋ผ ํ์ ์ผ๋ก ์ฌ์ฉํ ๋์๋ฅผ ๋ค์ด Oracle์ ์ฌ์ฉ์ ์ ์ ๋ฐฐ์ด ํ์ ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
ex) NUMBER[], VARCHAR2[], STRUCT[] ๊ฐ์ Oracle์ Collection ํ์
์์
CREATE OR REPLACE TYPE string_array AS TABLE OF VARCHAR2(100);
@Entity
@Table(name = "my_entity")
class MyEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
@Column(name = "tags")
var tags: Array<String>? = null
)
์ด ๋ Hibernate๊ฐ Oracle์ tags ์ปฌ๋ผ์ ๋งคํํ๋ ค๊ณ ํ ๋, ๋ด๋ถ์ ์ผ๋ก StringArray
๋ผ๋ ์ด๋ฆ์ Oracle ๋ฐฐ์ด ํ์
์ด ํ์ํจ.
์ด๋ ์ด๋ฆ ์ถฉ๋๊ณผ ๋ช ํ์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Hibernate 6.6 ๋ถํฐ StringArray โ StringVarcharArray OR StringArrayUsingMyConverter ์ฒ๋ผ ๋ ๋ช ํํ ์ด๋ฆ์ ์๋์ผ๋ก ์์ฑํ๋ค.
DB๋ฅผ Array ํ์ ์ผ๋ก ์ฐ๋ ๊ฒ VS Join Table VS Join ์๋์ ์ฅ๋จ์
์ฃผ๋ก MySQL์ ์ฐ๋ค๋ณด๋ ์ปฌ๋ผ ์์ฒด์ Array๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ์์ด์ ๊ถ๊ธํด์ ๊ฐ ํญ๋ชฉ์ ์ฅ๋จ์ ์ ๋ํด์ ์ฐพ์๋ณด์๋ค.
์ผ๋ฐ์ ์ผ๋ก Join์ ๋ง์ด ์ฐ๋, ์ฐ๋ฆฌ ํ์ฌ / ํ์์๋ Join์ ์ง์ ์ฌ์ฉํ์ง ์๊ณ ์๋์ผ๋ก Join์ ์ฌ์ฉํ๋ ํธ์ด๋ผ ํด๋น ๋ถ๋ถ๋ ํจ๊ป ๋น๊ตํด๋ณด์๋ค.
- PersonArray / Person + Phone์ด ์กด์ฌํ๋ ๊ฒฝ์ฐ ์์
ํญ๋ชฉ | โ ๋ฐฐ์ด ์ปฌ๋ผ ๋ฐฉ์ (Oracle ARRAY) | โก ์ ๊ทํ ๋ฐฉ์ (Join ์ฌ์ฉ) @OneToMany | โข ์ ๊ทํ ๋ฐฉ์ (Join ์์ด ์๋ ์กฐํ) |
DB ๊ตฌ์กฐ | PersonArray ํ๋์ ํ
์ด๋ธ์ ๋ฐฐ์ด ์ปฌ๋ผ ์ฌ์ฉ | ๋ ๊ฐ์ ํ ์ด๋ธ + ์ธ๋ ํค | ๋ ๊ฐ์ ํ ์ด๋ธ + ์ธ๋ ํค |
์ฟผ๋ฆฌ ๋ณต์ก๋ | ๋จ์ผ ์ฟผ๋ฆฌ๋ก ์กฐํ ๊ฐ๋ฅ | Hibernate๊ฐ ๋ด๋ถ์ ์ผ๋ก JOIN ์ํ | ๋ ๊ฐ์ ๋ช
์์ ์ฟผ๋ฆฌ ํ์ (person , phone ) |
์ฝ๊ธฐ ์ฑ๋ฅ | โ ๋น ๋ฆ (์ ์ฒด ๋ฐฐ์ด ์ฝ๊ธฐ) | โ JOIN ๋น์ฉ ์์ (N+1 ๊ฐ๋ฅ์ฑ ์์) | โ ํจ์จ์ (JOIN ์ต์ํ) |
์ฐ๊ธฐ ๋ณต์ก๋ | โ ์ ์ฒด ๋ฐฐ์ด ๊ฐฑ์ ํ์ | โ ๊ฐ๋ณ ์ํฐํฐ ์ฝ์ /์ญ์ ๊ฐ๋ฅ | โ ๋ก์ง์ ์๋์ผ๋ก ๊ตฌํํด์ผ ํจ |
๊ฒ์ ์ ์ฐ์ฑ | โ ๋ฐฐ์ด ๋ด๋ถ ์กฐ๊ฑด ๊ฒ์ ์ด๋ ค์ | โ phone.number ๋ก ํํฐ๋ง ๊ฐ๋ฅ | โ ๋ช ์์ ์กฐ๊ฑด ์ฟผ๋ฆฌ ์์ฑ ๊ฐ๋ฅ |
ORM ๋งคํ ๋์ด๋ | โ ๋งค์ฐ ๋จ์ (ํ๋๋ง ์ ์ธ) | โ ์๋ฐฉํฅ ์ฐ๊ด ์ค์ ๋ฑ ๋ณต์ก | โ ๋งคํ์ ๋จ์, ์กฐํ ๋ก์ง์ ์๋ |
์ํฐํฐ ์ผ๊ด์ฑ ๋ณด์ฅ | โ ์ฝํจ (์์์ฑ ์ ์ด ์์) | โ ์ ์ด, ๊ณ ์ ๊ฐ์ฒด ์ ๊ฑฐ ๋ฑ ๊ฐ๋ฅ | โ ์ผ๊ด์ฑ ์๋ ๋ณด์ฅ ํ์ |
์ ์ง๋ณด์์ฑ | ์ค๊ฐ (๋จ์ํ์ง๋ง ํ์ฅ์ฑ ๋ฎ์) | ๋์ (ํํ๋ ฅ, ์ ์ฐ์ฑ ์ฐ์) | ๋ฎ์ (์ง์ ์ฟผ๋ฆฌ ๊ด๋ฆฌ ํ์) |
๋ฉํ๋ฐ์ดํฐ ํ์ฅ ์ฉ์ด์ฑ | โ ๋ฐฐ์ด์ ์ถ๊ฐ ํ๋ ๋ฃ๊ธฐ ์ด๋ ค์ | โ ํ์ฅ ์ฉ์ด (์: ํ์ , ๋ฑ๋ก์ผ ๋ฑ) | โ ํ์ฅ ์ฉ์ด |
์ถ์ฒ ์ฌ์ฉ ์์ | ๋จ์ ์ ์ฅ/์กฐํ, ๊ฐ ์ ์ ๊ณ ์ ์ฒด๋ง ๋ค๋ฃฐ ๋ | ๊ด๊ณ๊ฐ ๋ช ํํ๊ณ ํ์ฅ์ด ํ์ํ ๊ฒฝ์ฐ | JOIN ์ต์ํ + ์๋ ํ๋์ด ํ์ํ ๊ฒฝ์ฐ |
2. UserDefinedType
๊ตฌ์กฐ ๋ณ๊ฒฝ
๊ธฐ์กด ๋ฌธ์ ์
Oracle UDT(ex. STRUCT, VARRAY, NESTED TABLE)์ ๋งคํํ ๋ ๊ตฌ์กฐ์ ์ฐจ์ด๋ฅผ ๋ช ํํ ๋ฐ์ํ์ง ๋ชปํจ.
ex) STRUCT๋ ํ๋ ์ด๋ฆ/ํ์ ์ ๊ฐ์ง๊ณ ์์ง๋ง ARRAY๋ ๊ทธ๋ ์ง ์์
ํ์ง๋ง Hibernate ๋ด๋ถ์์ ๋ชจ๋ ๋์ผํ UserDefinedType ์ธํฐํ์ด์ค๋ก ์ฒ๋ฆฌ๋จ
๊ฐ์ ๋ชฉ์
STRUCT, ARRAY๋ฅผ ๋ด๋ถ์ ์ผ๋ก ๋ช ํํ ๊ตฌ๋ถ
์ฌ์ฉ์ ์ ์ ํ์ ๊ฐ ์ข ์์ฑ๋ ํํํ ์ ์๊ฒ ํ๊ธฐ ์ํจ.
(ex. STRUCT์ ํ๋๊ฐ ARRAY์ธ ๊ฒฝ์ฐ)
AS-IS (Hibernate 6.5 ์ดํ)
interface UserDefinedType<T> {
fun getUserTypeName(): String
fun getJdbcType(): JdbcType
...
}
TO-BE (Hibernate 6.6)
interface UserDefinedType<T> {
fun getUserTypeName(): String
...
}
interface UserDefinedObjectType<T> : UserDefinedType<T> {
fun getAttributes(): List<...> // STRUCT ์ ์ฉ
}
interface UserDefinedArrayType<T> : UserDefinedType<T> {
fun getElementType(): JdbcType
}
์ฆ, ์์ ํ์ (UserDefinedType)์ ๊ณตํต ์ธํฐํ์ด์ค, ํ์์ ObjectType (STRUCT), ArrayType(VARRAY ๋ฑ)์ด ๋ช ํํ๊ฒ ๋ถ๋ฆฌ ๋จ.
์ด๋ค ์ํฉ์ ์ฌ์ฉํ ๊น?
- ๊ณ ๊ธ Oracle UDT ๋งคํ์ ์ฌ์ฉ ๋จ
-- 1. Oracle STRUCT ํ์
์์
CREATE TYPE address_type AS OBJECT (
city VARCHAR2(100),
zipcode VARCHAR2(10)
);
-- 2. Oracle ARRAY of STRUCT
CREATE TYPE address_array AS TABLE OF address_type;
- Kotlin ๋งคํ ์์
// Address ๊ตฌ์กฐ์ฒด
data class Address(
val city: String,
val zipcode: String
)
// ์ฃผ์ ๋ชฉ๋ก (Oracle ARRAY of STRUCT)
@Entity
class Customer(
@Id @GeneratedValue
val id: Long? = null,
@JdbcTypeCode(SqlTypes.ARRAY)
@Column(name = "addresses")
var addresses: Array<Address>? = null
)
Hibernate๋ ๋ด๋ถ์ ์ผ๋ก UserDefinedArrayType<Address> โ UserDefinedObjectType<Address> ๊ตฌ์กฐ๋ก ์ธ์ํ๋ค.
UserDefinedObjectType<T>
Oracle์ STRUCT ํ์ ์ ์๋ฏธ
ex) address_type(city VARCHAR2, zipcode VARCHAR2)
๋จ์ผ ๊ตฌ์กฐ์ฒด ํ๋.
data class Address(โฆ)
์ ๋์๋๋ค.
UserDefinedArrayType<T>
Oracle์ TABLE OF STRUCT, ์ฆ STRUCT์ ๋ฐฐ์ด์ ์๋ฏธํจ.
ex) adress_array AS TABLE OF address_type
์ฌ๋ฌ ๊ฐ์ ๊ตฌ์กฐ์ฒด.
Array<Address>
์ ๋์ ๋๋ค.
Hibernate๊ฐ ์ดํดํ๋ ๋ด๋ถ ๋ชจ๋ธ
UserDefinedArrayType<Address> {
elementType = UserDefinedObjectType<Address> {
attributes = ["city" โ String, "zipcode" โ String]
}
}
- ์ด ๋ฐฐ์ด์ Address๋ผ๋ ๊ตฌ์กฐ์ฒด์ ๋ฐฐ์ด์ด๊ณ , ๊ทธ Address๋ผ๋ ๊ตฌ์กฐ์ฒด๋ ๋ ๊ฐ์ ํ๋๋ฅผ ๊ฐ์ง๊ณ ์๊ณ ๊ทธ๊ฑด ๊ฐ๊ฐ VARCHAR2 ๋ผ๋ ๊ฒ์ ์ธ์ํ๊ณ ๋งคํ ์ ๋ณด๋ฅผ ๊ตฌ์ฑํ๋ค.
์ด ๊ตฌ์กฐ์ ์ฅ์
์๋ SQL ์์ฑ โ Hibernate๊ฐ ์ ์ ํ Oracle SQL ํ์ ๊ณผ DDL์ ์ ํํ ์์ฑํจ
์ค์ฒฉ ํ์ ์ง์ โ STRUCT ์์ ๋ ๋ค๋ฅธ ARRAY๊ฐ ์์ ๊ฒฝ์ฐ๋ ๋ชจ๋ธ๋ง ๊ฐ๋ฅ
๋ช ํํ ํ์ ์์กด์ฑ ์ถ์ โ ์ด๋ค ๋ฐฐ์ด์ด ์ด๋ค ๊ตฌ์กฐ์ฒด๋ฅผ ์ฐ๋์ง Hibernate๊ฐ ๊ตฌ์กฐ์ ์ผ๋ก ์ถ์
๋คํ์ฑ ๋ฐ ํ์ ์์ ์ฑ ๊ฐํ โ ๋ฉํ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ฐํ์์ ํ์ ์ถฉ๋ ๋ฐฉ์ง ๊ฐ๋ฅ
๋งคํ ์์
data class Address(val city: String, val zipcode: String)
@Entity
class Customer(
...
@JdbcTypeCode(SqlTypes.ARRAY)
var addresses: Array<Address>? = null
)
- addresses ํ๋๋ ๋ด๋ถ์ ์ผ๋ก, UserDefinedArrayType<Address>๋ก ์ธ์๋๊ณ , ๊ทธ ์์ ๋ค์ด๊ฐ๋ Address๋ UserDefinedObjectType<Address>๋ก ์ธ์๋๋ค.
๊ทธ๋ ๋ค๋ฉด Oracle UDT์ ๋ํ DDL ์๋ ์์ฑ์ด ๊ฐ๋ฅํ ๊น?
Hibernate 6.6์์๋ Oracle์ UDT(STRUCT, VARRAY, NESTED TABLE)์ ๋ํ DDL์ ์ฌ์ ํ ์๋ ์์ฑ๋์ง ์๋๋ค. (ex hibernate.hbm2ddl.auto=create ์ค์ ๋ฑ)
- ํ์ง๋ง Hibernate 6.6์์๋ ๋ด๋ถ ๊ตฌ์กฐ ์ธ์ ๋ฐ ํ์ ์ด๋ฆ ์์ฑ์ด ๋ ์ ๊ตํด์ ธ์ ๋งคํ๊ณผ ๋ฐ์ธ๋ฉ์ ๋ ์ ๋์ํ๋ค.
Hibernate๊ฐ ์๋์ผ๋ก ์์ฑํ๋ ๊ฒ VS ์ํ๋ ๊ฒ
ํญ๋ชฉ | ์๋ ์์ฑ ์ฌ๋ถ | ์ค๋ช |
Oracle ํ
์ด๋ธ (CREATE TABLE ) | โ ๊ฐ๋ฅ | ์ํฐํฐ์ ๊ธฐ๋ฐํ ํ ์ด๋ธ ๋ฐ ์ปฌ๋ผ ์์ฑ ๊ฐ๋ฅ |
Oracle STRUCT ํ์
(CREATE TYPE ... AS OBJECT ) | โ ๋ถ๊ฐ๋ฅ | Oracle ์ ์ฉ ๋ฌธ๋ฒ, Hibernate๊ฐ ์์ฑํ์ง ์์ |
Oracle ๋ฐฐ์ด ํ์
(CREATE TYPE ... AS TABLE OF ) | โ ๋ถ๊ฐ๋ฅ | ๋ฐฐ์ด ํ์ ๋ ์๋ ์์ฑ ํ์ |
Hibernate ๋งคํ ๋ฉํ๋ชจ๋ธ | โ ๊ฐ๋ฅ | STRUCT/ARRAY ํ์ ์ ๋งคํ ๋ฉํ์ ๋ณด ์์ฑ |
Oracle ๋ฐฐ์ด ํ์ ์ด๋ฆ ์ถ๋ก | โ ๊ฐ๋ฅ | Hibernate 6.6๋ถํฐ ๋ ์ ๋ฐํ๊ณ ์ถฉ๋ ๋ฐฉ์ง๋จ |
Hibernate insert/update/delete SQL | โ ๊ฐ๋ฅ | ์์ฑ๋ UDT ํ์ ์ ์ฐธ์กฐํ์ฌ SQL ์คํ ๊ฐ๋ฅ |
Hibernate schema validator | โ ๊ฐ๋ฅ | ์๋ ์์ฑ๋ UDT์ ๋งคํ์ด ์ผ์นํ๋์ง ๊ฒ์ฆ ๊ฐ๋ฅ (validate ) |
Hibernate๊ฐ CREATE TYPE์ ์ง์ํ์ง ์๋ ์ด์
JPA ์ฌ์์ ์์
@Entity
,@Embeddable
๋ฑ์ ํ ์ด๋ธ๊ณผ ์ปฌ๋ผ์ ์ ์ํ์ง, Oracle์ TYPE์ ์ ์ํ๋ ์ฌ์์ด ์กด์ฌํ์ง ์์
DBMS์ ์ข ์์ ์ธ ๋ฌธ๋ฒ
- CREATE TYPE์ ๊ฐ DB๋ง๋ค ๋ฌธ๋ฒ์ด ์ ํ ๋ค๋ฆ
Hibernate์ ์คํค๋ง ์์ฑ๊ธฐ๋ ANSI SQL ๊ธฐ๋ฐ
- Oracle ์ ์ฉ UDT๋ HIbernate๊ฐ ์ธ์์ ํ์ง๋ง ์ง์ ์์ฑํ์ง๋ ์์
Hibernate์ Oracle UDT๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
- Oracle UDT ์๋ ์์ฑ
CREATE OR REPLACE TYPE address_type AS OBJECT (
city VARCHAR2(100),
zipcode VARCHAR2(10)
);
CREATE OR REPLACE TYPE address_array AS TABLE OF address_type;
- Hibernate๋ ํด๋น ํ์ ์ ์ด๋ฆ๋ง ์ฐธ์กฐ
@Column(columnDefinition = "address_array")
@JdbcTypeCode(SqlTypes.ARRAY)
var addresses: Array<Address>? = null
Hibernate 6.6์ ์ญํ
STRUCT/ARRAY๋ฅผ ๊ตฌ๋ถํ์ฌ ํ์ ๋ฉํ์ ๋ณด ์ถ์
UDT์ ์์กด์ฑ ๊ด๊ณ๊น์ง ์ถ์ ๊ฐ๋ฅ (์. STRUCT ์์ ARRAY๊ฐ ์์ ๊ฒฝ์ฐ)
์๋ ํ์ ์ด๋ฆ ์์ฑ์ด ๋ ๋ช ํํ๊ณ ์ถฉ๋ ์๋ ๋ฐฉ์์๋ก ๊ฐ์ ๋จ
3. ๋ฐฐ์ด ๋ถ๋ถ ํฌํจ ์ฌ๋ถ ๊ฒ์ฌ ํจ์ ๋ณ๊ฒฝ
Hibernate 6.5 ์ดํ ๋ฒ์ ์ ๋ฌธ์ ์
where array_contains(tags, ARRAY['urgent', 'vip'])
Hibernate 6.5 ์ดํ์์๋ ๋ค์๊ณผ ๊ฐ์ JPQL ๋ฌธ์ ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ฐ, ์ด ํจ์๋ ์๋ โ๋ฐฐ์ด์ ๋จ์ผ ๊ฐ์ด ํฌํจ๋๋๊ฐ?โ๋ฅผ ์๋ํ ๊ฒ
๋ฐฐ์ด ๋ ๋ฐฐ์ด ๋น๊ต์ ์ฌ์ฉ๋๋ ๊ฒ์ ๋นํ์ค ์ฌ์ฉ
Hibernate 6.6์์ ๋ณ๊ฒฝ๋ ์
ํจ์ ๋ฐฉ์
where array_includes(tags, ARRAY['urgent', 'vip'])
tags ๋ฐฐ์ด์ด [โurgentโ, โvipโ]๋ฅผ ํฌํจํ๋์ง ํ์ธ
๋ด๋ถ์ ์ผ๋ก PostgreSQL/Oracle์ @>, ANY, ALL ๋ฑ์ ๋งคํ ๋จ
์ ์ด ๋ฐฉ์
where tags INCLUDES ARRAY['urgent', 'vip']
์ฃผ์์ฌํญ
๋ฐฐ์ด ํ์ ์ปฌ๋ผ์ด์ด์ผ ์๋ํจ (
@JdbcTypeCode(SqlTypes.ARRAY)
)DB๊ฐ ๋ฐฐ์ด ์ฐ์ฐ์ ์ง์ํด์ผ ํ๋ค. (MySQL์ ๋ฐฐ์ด ํ์ ์ง์ X)
INCLUDES๋ Hibernate๊ฐ ๋ง๋ JPQL ์ ์ด๋ก JPA ํ์ค์๋ ์์
4. ์ญ์ ๋ Entity ๋ณํฉ ์ ์์ธ ์ฒ๋ฆฌ ๊ฐํ
JPA์
EntityManager.merge()
์ฌ์ฉ ์ ๋ฐ์ํ ์ ์๋ ๋น์ ์์ ์ธ insert ๋์์ ๋ฐฉ์งํ๊ธฐ ์ํด ๋์ ๋ ์์ ์ฑ ๊ฐ์ ๊ธฐ๋ฅHibernate 6.6๋ถํฐ๋
merge()
ํธ์ถ ์ DB์ ํด๋น ํ์ด ์กด์ฌํ์ง ์์ผ๋ฉด, ์กฐ๊ฑด์ด ์ถฉ์กฑ๋ ๊ฒฝ์ฐ OptimisticLockException ์์ธ๋ฅผ ๋ฐ์์ํจ๋ค. (์๋์น ์์ INSERT ๋ฐ์ํ์ง ์๋๋ก ํจ)
๊ธฐ์กด ๋ฌธ์ ์ (Hibernate 6.5 ์ดํ)
๋ฌธ์ ์ํฉ
์ด๋ค ์ํฐํฐ๊ฐ A ํธ๋์ญ์ ์์ ์ญ์ ๋จ
B ํธ๋์ญ์ ์์๋ ํด๋น ์ํฐํฐ์ ์ฐธ์กฐ๋ฅผ ์ฌ์ ํ ๋ค๊ณ ์์ (detached ์ํ)
B ํธ๋์ญ์ ์์ em.merge(entity) ํธ์ถ
๊ธฐ์กด ๋์ (๋ฌธ์ O)
Hibernate๋ ํด๋น ID์ ์ํฐํฐ๊ฐ DB์ ์์ผ๋ฏ๋ก ์๋ก ์ถ๊ฐํด์ผ ํ๋ค๊ณ ํ๋จํด INSERT ์คํ
์ค์ ๋ก๋ ์ฌ์ฉ์๊ฐ ์ํ๊ฑด ๋ณํฉ(Update) ์์ง๋ง, ์ด๋ฏธ ์ญ์ ๋ ์ํฐํฐ๊ฐ ๋ค์ ์์ฑ๋จ.
๋๊ด์ ๋ฝ(Optimistic Locking)์ ๊ท์น ์๋ฐ์ด๋ฉฐ ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ์ ํด์น๋ ์ํฉ
Hibernate 6.6์ ๊ฐ์ ๋ ๋์
DB์ ํด๋น row๊ฐ ์์ ๊ฒฝ์ฐ OptimisticLockException์ ๋์ง
์์ธ ๋ฐ์ ์กฐ๊ฑด
@Id
๊ฐ@GeneratedValue
๋ฑ์ผ๋ก ์๋ ์์ฑ๋ ๊ฐ์ผ ๊ฒฝ์ฐ@Version
ํ๋๊ฐ nullable ๋ํผ ํ์ ์ผ ๊ฒฝ์ฐ (ex. Long?, Integer?)
์ด ๋๊ฐ์ง ์ค ํ๋์ ์กฐ๊ฑด์ด๋ผ๋ ๋ง์กฑํ๋ฉด Hibernate๋ ์ด ๊ฐ์ฒด๊ฐ ์๋ ์กด์ฌํ ๊ฐ์ฒด๋ผ๊ณ ํ๋จํ ์ ์์. ๋ฐ๋ผ์ DB์ ์์ผ๋ฉด ์ญ์ ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผํจ.
์์
@Entity
class Product(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
var name: String,
@Version
var version: Long? = null
)
--
interface ProductRepository : JpaRepository<Product, Long>
--
@Service
class ProductService(
private val productRepository: ProductRepository
) {
@Transactional
fun simulateMergeAfterDelete() {
// 1. ์ ์ฅ
val saved = productRepository.save(Product(name = "Item"))
// 2. ์ญ์
productRepository.deleteById(saved.id!!)
// 3. ์ญ์ ๋ ๊ฐ์ฒด๋ฅผ ๋ค๊ณ ์๋ค๊ณ ๊ฐ์ ํ๊ณ , ์์ ํด์ ๋ค์ ์ ์ฅ ์๋
val detached = Product(id = saved.id, name = "Updated Item", version = saved.version)
// 4. ๋ค์ ์ ์ฅ (์ด ์์ ์ Hibernate ๋ด๋ถ์ ์ผ๋ก merge ๋ฐ์)
productRepository.save(detached) // Hibernate 6.6: OptimisticLockException ๋ฐ์ ๊ฐ๋ฅ
}
}
๋๊ด์ ๋ฝ(Optimistic Lock)์ ์๋ฐํ๋ ์ด์
๋๊ด์ ๋ฝ์ด๋?
๋์์ฑ ์ ์ด ๊ธฐ๋ฒ ์ค ํ๋
โ์ถฉ๋์ด ๊ฑฐ์ ์์ ๊ฒโ์ด๋ผ๊ณ ๋๊ด์ ์ผ๋ก ๊ฐ์ ํ๊ณ , ๋ฐ์ดํฐ ๋ณ๊ฒฝ ์์ ์๋ง ์ถฉ๋์ ๊ฐ์งํจ.
@Version
ํ๋๋ฅผ ํตํด ๋ฒ์ ์ ๋น๊ตํ์ฌ ์ถฉ๋ ์ฌ๋ถ๋ฅผ ํ๋จํจ.
๋์์ฑ ์ถฉ๋์ ์์
A ํธ๋์ญ์ ์์ Product๋ฅผ ์กฐํํ ํ ์ญ์ ํจ
B ํธ๋์ญ์ ์ ์กฐํ๋ ์ํฐํฐ๋ฅผ save()(=merge) ํ๋ ค ํจ
์ด ๋ DB์ ์ด๋ฏธ ์๋ ์ํ์ผ ๋ Hibernate์ ๋์์?
Hibernate 6.6์ ๊ฒฝ์ฐ
๊ฐ์ฒด๋ฅผ ์ฝ์ ๋ version = 1์ด๋ผ๋ฉด, ์ ๋ฐ์ดํธ ์์๋ ์ฌ์ ์ด version = 1์ด์ด์ผ ํจ
๋ง์ฝ ํด๋น row๊ฐ ์๊ฑฐ๋ version์ด ๋ค๋ฅด๋ฉด โ OptimisticLockException์ด ๋ฐ์ํจ
select version from product where id = ? -- id = 1
๊ฒฐ๊ณผ ์์ โ ์ญ์ ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผ
๊ทธ๋ฆฌ๊ณ
@Version
์ด nullable(Long?)์ด๊ธฐ์ โ์๋ ์กด์ฌํ๋ ๊ฐ์ฒดโ๋ก ํ๋จ ๊ฐ๋ฅ์ดํ ์ญ์ ๋ ์ํฐํฐ์ ๋ํ ๋ณํฉ ์๋ โ ์์ธ๊ฐ ๋ฐ์ํจ.
์ด์ Hibernate๋?
merge ๋์์ด DB์ ์์ผ๋ฉด ๊ทธ๋ฅ INSERT ์งํ
์ญ์ ๋ ์ค๋ ๋ชจ๋ฅด๊ณ ์๋ก ๋ง๋ค์ด ๋ฒ๋ฆฌ๋ ๋์์ ์ทจํจ
๋์์ฑ ์ถฉ๋์ ๊ฐ์งํ์ง ์๊ณ ๋ฐ์ดํฐ๊ฐ ์๋ก ์๊ธฐ๋ฏ๋ก ๋๊ด์ ๋ฝ์ ์ฒ ํ์ ์ด๊ธฐ๊ฒ ๋จ.
์์) ์ค๋ฌด์์ ๊ฐ์ ๋๋ ๊ฒ
์ด๋ฒคํธ ๊ธฐ๋ฐ ์์คํ ์์ ์ค๋๋ ์ํฐํฐ ์ด๋ฒคํธ๊ฐ ๋ณํฉ๋ ์ํ
๊ด๋ฆฌ์ ํ๋ฉด ๋ฑ์์ UI์ ๋ ธ์ถ๋ ์ญ์ ๋ ๋ฐ์ดํฐ๊ฐ ์ค์๋ก ์์ ๋ ๊ฐ๋ฅ์ฑ
๋ฉํฐ ์ ์ ํ๊ฒฝ์์ ๋์ ์ญ์ /์์ ์ด ์ถฉ๋๋ ๊ฒฝ์ฐ
๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ ๋ณดํธ์ ํฐ ํจ๊ณผ๊ฐ ์์
5. ํด๋์ค ์ด๋ ธํ ์ด์ ์ค๋ณต ์ฌ์ฉ ๊ธ์ง
ํ ํด๋์ค์ ์ฌ๋ฌ ๊ฐ์ JPA ๋ฉํ ์ด๋ ธํ ์ด์ ์ ํจ๊ป ์ฌ์ฉํ๋ ๊ฒ์ด ํ์ฉ๋์ง ์์.
Hibernate 6.5 ์ดํ
@MappedSuperclass
+@Entity
@Embeddable
+@Entity
@Embeddable
+@MappedSuperclass
์์ ๊ฐ์ ์ด๋ ธํ ์ด์ ์กฐํฉ์ด ๊ฐ๋ฅํ๋ค.
๊ฐ ์ด๋ ธํ ์ด์ ์ ์ญํ
@Entity
โ ๋ ๋ฆฝ์ ์ธ ํ ์ด๋ธ๋ก ๋งคํ๋๋ ์ค์ ์ํฐํฐ ํด๋์ค@MappedSuperclass
โ ํ ์ด๋ธ์ ์์ง๋ง ์์ ์ ํ๋๊ฐ ํ์ ์ํฐํฐ์ ํฌํจ๋จ.@Embeddable
โ ๋ณ๋ ํ ์ด๋ธ ์์ด, ๋ค๋ฅธ ์ํฐํฐ์ ํฌํจ๋๋ ๊ฐ ๊ฐ์ฒด ํ์
์์ด์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ๋ด๋ถ์ ์ผ๋ก ๋งค์ฐ ๋ชจํธํ ์๋ฏธ๋ฅผ ๊ฐ์ง
๋ชจํธํจ์ ์ ๊ฑฐํ๊ธฐ ์ํด Hibernate 6.6์์๋ ๋์ ์ฌ์ฉ ์์ฒด๋ฅผ ๊ธ์งํจ.
Hibernate 6.6์ ๋ด๋ถ ๋ณํ
๋ถํธ์คํธ๋ฉ ์ ์ํฐํฐ ์ค์บ ๊ณผ์ ์์ ํด๋์ค์ ์ญํ ์ ๋ช ํํ ์๋ณํด์ผ ํ๋ฉฐ,
org.hibernate.metamodel.model.domain.internal.ClassTypeDeterminer
๊ณ์ธต์์ ์ค๋ณต ์ ์๋ ์ด๋ ธํ ์ด์ ์ ๊ฒ์ฌํ๋ค.์ฆ, ์ ๋งคํ ํด๋์ค๊ฐ ํ๋ก์ ํธ์ ์์ผ๋ฉด ๋ถํ ์์ฒด๊ฐ ์คํจํ๋ค.
@MappedSuperclass
VS @Emabeddable
๋น๊ต
ํญ๋ชฉ | @MappedSuperclass | @Embeddable |
์ญํ | ์ํฐํฐ๊ฐ ์์๋ฐ๋ ์ถ์ ๋ถ๋ชจ ํด๋์ค | ๋ค๋ฅธ ์ํฐํฐ ๋ด๋ถ์ ํฌํจ๋๋ ๊ฐ ๊ฐ์ฒด |
ํ ์ด๋ธ ์กด์ฌ ์ฌ๋ถ | โ ๋ณ๋์ ํ ์ด๋ธ ์์ | โ ๋ณ๋์ ํ ์ด๋ธ ์์ |
์์ ์ฌ๋ถ | ๋ฐ๋์ extends ๋๋ : ์ผ๋ก ์์๋ฐ์์ผ ํจ | ํฌํจ(aggregation) ๊ด๊ณ, @Embedded ๋ก ์ฃผ์
|
๋ฐ์ดํฐ ํฌํจ ๋ฐฉ์ | ์์ ์ํฐํฐ๊ฐ ๋ถ๋ชจ ํ๋๋ค์ ์ง์ ํฌํจํจ | ํฌํจ๋ ํ๋ ์์ ๊ฐ์ ํ๋์ ๊ฐ์ฒด์ฒ๋ผ ํฌํจ |
์์๋ ํ๋ ์ฌ์ฉ | ์์ ์ํฐํฐ์ ์ปฌ๋ผ์ผ๋ก ์ง์ ํฌํจ๋จ | ๊ฐ์ฒด ํ๋ ์์ ์์ฑ์ผ๋ก ํฌํจ๋จ (audit.createdAt ) |
์ํฐํฐ๋ก ์ธ์๋จ? | โ ์ง์ @Entity ์๋ | โ ์ง์ @Entity ์๋ |
์ฌ์ฉ ์์ | ๊ณตํต ํ๋๋ฅผ ์ฌ๋ฌ ์ํฐํฐ์ ์์ํ๊ณ ์ถ์ ๋ | ํ๋ ๊ทธ๋ฃน์ ๊ตฌ์กฐํํ๊ณ ์ฌ์ฌ์ฉํ๊ณ ์ถ์ ๋ |
์ฟผ๋ฆฌ์์์ ํํ | SELECT o.createdAt FROM Order o | SELECT o.audit.createdAt FROM Order o |
์ค๋ฌด์์ ์ ํ ๊ธฐ์ค
์ฌ๋ฌ ์ํฐํฐ์ ๊ณตํต ํ๋๋ฅผ ์์ํด์ผ ํจ โ
@MappedSuperclass
๊ณตํต ํ๋๊ฐ ํ๋์ ๊ฐ๋ ๋จ์(๊ฐ์ฒด)๋ก ๋ฌถ์ฌ์ผ ํจ โ
@Embeddable
๋จ์ผ ๊ฐ์ฒด ์์์ ์๋ธ ์์ฑ์ผ๋ก ํํํ๊ณ ์ถ์ โ
@Embeddable
JPA์ ๋คํ์ฑ ์์ ์ ๋ต๊ณผ ํจ๊ป ์ฐ๊ณ ์ถ์ โ
@MappedSuperclass
๋๋@Entity
6. @Embeddable
๋คํ์ฑ ์ง์ (Discriminator ๊ธฐ๋ฐ)
Hibernate 6.6๋ถํฐ๋ ๊ฐ ํ์ (
@Embeddable
)์์๋ ๋คํ์ฑ์ ์ฌ์ฉํ ์ ์์
๊ฐ ํ์ (Embeddable)์ด๋?
- ์๋ณ์๊ฐ ์๊ณ ์๋ช ์ฃผ๊ธฐ๋ ์์ ํ๋ ์ํฐํฐ์ ์ข ์๋๋ฉฐ, ์ฌ๋ฌ ์ํฐํฐ์์ ๊ณต์ ๊ฐ๋ฅํ๊ณ , ๋ ๋ฆฝ ํ ์ด๋ธ์ด ์๋(์ปฌ๋ผ์ผ๋ก ๋งคํ๋จ) ํ์
๊ฐ ํ์ ์ ์ฐ๋ ์ํฉ
์ฃผ์, ๊ธฐ๊ฐ, ์ขํ, ๊ธ์ก, ํตํ ๋ฑ ์์ ์๋ฏธ ๋จ์๋ก ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฐ์ดํฐ ๊ตฌ์กฐ
๊ณตํต ํ๋ ๊ตฌ์กฐํ๋ฅผ ์ํด (createdAt, updatedAt, createdBy ๋ฑ)
ํ๋์ ๋ ผ๋ฆฌ์ ๋จ์๋ก ๋ฌถ์ด์ผ ํ ๋ (ex. Money(currency, amount))
์ฅ/๋จ์
์ฅ์
์ฌ์ฌ์ฉ์ฑ โ ์ฌ๋ฌ ์ํฐํฐ์์ ๊ณตํต ๊ตฌ์กฐ๋ก ์ฌ์ฉ ๊ฐ๋ฅ
์ฝ๋ ๊ฐ๊ฒฐ์ฑ โ ์ค๋ณต ์ค์ด๊ณ ์ ์ง๋ณด์ ์ฉ์ด
์๋ฏธ ์๋ ๋ชจ๋ธ๋ง โ ํ๋๋ฅผ ๊ฐ์ฒด ๋จ์๋ก ํํ
๋ณ๊ฒฝ ๊ฐ์ง โ ์์ ์ํฐํฐ์์ ๊ฐ ํ์ ํ๋ ๋ณ๊ฒฝ ๊ฐ์ง ๊ฐ๋ฅ (dirty checking)
๋จ์
์๋ณ์๊ฐ ์์ โ ๊ฐ๋ณ๋ก ์กฐํ/์ถ์ ๋ถ๊ฐ๋ฅ
๊ด๊ณ ์ค์ ๋ถ๊ฐ โ
@ManyToOne
,@OneToMany
๋ฑ ๊ด๊ณ ์ค์ ๋ถ๊ฐ๋ฅ๋ณ๊ฒฝ ๋ถ๋ฆฌ ์ด๋ ค์ โ ๊ฐ ํ์ ์์ฒด๋ก ๋ณ๊ฒฝํ๋ ค๋ฉด ์ ์ฒด ๊ฐ์ฒด๋ฅผ ๊ต์ฒดํด์ผ ํจ.
์์/๋คํ์ฑ ํ๊ณ โ Hibernate ์ด์ ๊น์ง๋ ๋คํ์ฑ ๋ถ๊ฐ (์ด์ ์ง์๋จ)
๊ฐ ํ์ ์ด ๋ณ๊ฒฝ ๋ถ๋ฆฌ๊ฐ ์ด๋ ค์ด ์ด์
๊ฐ ํ์ ์ ํน์ง์์ ๋ณผ ์ ์์
๋ถ๋ณ(immutable)ํ๊ฒ ์ค๊ณํ๋๊ฒ ์ด์์ ์
์ํฐํฐ์ ํ๋๋ก ํฌํจ๋๋ฉฐ, ์ฐธ์กฐ๊ฐ ์๋ ๊ฐ ์์ฒด๋ก ๋น๊ต๋๊ณ ๊ด๋ฆฌ๋๋ค.
JPA๋ ๊ฐ ํ์ ์ โ๊ทธ ๊ฐ ์์ฒด๊ฐ ๋ฐ๋์๋์งโ๋ฅผ ํ์ธํ๋ค.
๊ฐ ํ์ ์ ๊ฐ ์ผ๋ถ๋ง ์์ ํ๊ณ ์ถ์ ๊ฒฝ์ฐ
- ๊ฐ ํ์ ํ๋ ์ค ํ๋๋ง ๋ฐ๊พธ๋ ค๊ณ ํด๋, JPA๋ ๋ด๋ถ์ ์ผ๋ก ์ ์ฒด ๊ฐ์ด ๋ฌ๋ผ์ก๋์ง ๊ธฐ์ค์ผ๋ก ๋ณ๊ฒฝ ์ฌ๋ถ๋ฅผ ํ๋จํ๊ธฐ ๋๋ฌธ์, ๋ถ๋ถ ์์ ์ด ์๋๋ผ ์ ์ฒด ๊ฐ์ฒด ๊ต์ฒด๊ฐ ๋์ด์ผ ํ๋ค.
์์
@Embeddable
data class Address(
val city: String,
val street: String
)
@Entity
class Customer(
@Id @GeneratedValue
val id: Long? = null,
var name: String,
@Embedded
var address: Address
)
์์ ๊ฐ์ ์ํฉ์ผ ๋,
- ์๋ชป๋ ๋ฐฉ์ โ ํ๋ ์ง์ ์์
val customer = customerRepository.findById(id).get()
// address๋ data class๋ผ ๋ด๋ถ ํ๋ ์ง์ ์์ ๋ถ๊ฐ
customer.address.city = "NewCity" // ์ปดํ์ผ ์๋ฌ
- ์ฌ๋ฐ๋ฅธ ๋ฐฉ์ โ ์ ์ฒด ๊ต์ฒด
val customer = customerRepository.findById(id).get()
// ๊ธฐ์กด address๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์๋ก์ด ๊ฐ์ฒด ์์ฑ
val updatedAddress = customer.address.copy(city = "NewCity")
// ์ Address ๊ฐ์ฒด๋ก ๊ต์ฒด
customer.address = updatedAddress
JPA๋
@Embedded
ํ๋์ ์ฐธ์กฐ๊ฐ ๋ฐ๋์์์ ๊ฐ์งํ๊ณ ์ ์ฒด ๊ฐ ๋ณ๊ฒฝ์ผ๋ก ๊ฐ์ฃผํ๊ณ UPDATE๋ฅผ ์ํํ๋ค.
ํ๋๋ฅผ ์ง์ ์์ ํ๋ฉด ๋ํฐ ์ฒดํน์ด ์๋๊ฑฐ๋ ๋ฌด์๋ ์ ์๋ค.
@DynamicUpdate
๋ฑ์ ์จ๋ ์๋ฒฝํ ๋ณด์ฅ๋์ง ์์
๋ถ๋ณ ๊ฐ์ฒด(data class + copy)๋ก ์ฌ์ฉํ๋ ๊ฒ์ด ์ค๊ณ์ ๋ ์์ ํ๊ณ JPA ์ฒ ํ์๋ ๋ง๋ค.
Hibernate 6.5 ์ดํ์์์ ๊ฐ ํ์
@Embeddable
์ ๋จ์ผ ๊ตฌ์กฐ๋ก๋ง ์ทจ๊ธ์ด ๋จ (์์ํด๋ ํ์ ํด๋์ค ์ ๋ณด ์ ์ง ์๋จ)type() ๋๋ treat() JPQL์์ ์ฌ์ฉํ ์ ์์
Hibernate 6.6 ๋ถํฐ
@Embeddable
ํ์ ๋ ์์ ๊ตฌ์กฐ๋ฅผ ์ง์Hibernate๊ฐ ์๋์ผ๋ก Discriminator ์ปฌ๋ผ์ ์ถ๊ฐ ํจ
JPQL์์ type(), treat()์ผ๋ก ํ์ ํ์ ํํฐ๋ง ๊ฐ๋ฅ
์์
๋ถ๋ชจ ํด๋์ค (๊ฐ ํ์ ์์ ๊ตฌ์กฐ)
@Embeddable
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) // Hibernate 6.6์์ ์๋ ์ฒ๋ฆฌ๋จ
abstract class Address(
open val city: String
)
ํ์ ํด๋์ค (๋คํ์ฑ ๊ตฌํ)
@Embeddable
class KoreanAddress(
override val city: String,
val postalCode: String
) : Address(city)
--
@Embeddable
class ForeignAddress(
override val city: String,
val countryCode: String
) : Address(city)
์ํฐํฐ์์ ์ฌ์ฉ
@Entity
class Customer(
@Id @GeneratedValue
val id: Long? = null,
val name: String,
@Embedded
var address: Address
)
โ Hibernate๋ address_type์ด๋ผ๋ Discriminator ์ปฌ๋ผ์ ์๋์ผ๋ก ์์ฑํ๊ณ , KoreanAddress, ForeignAddress ์ค ์ด๋ค ํ์ ํ์ ์ธ์ง ๊ธฐ๋กํจ.
์์ฑ๋๋ ํ ์ด๋ธ ์์
id | name | address_city | address_postal_code | address_country_code | address_type |
1 | Kim | Seoul | 12345 | NULL | KoreanAddress |
2 | John | London | NULL | GB | ForeignAddress |
์ค๋ฌด ์ ์ฉ์
๊ณตํต ๊ตฌ์กฐ ์ ์ โ ๊ณตํต ํ๋๋ ๋ถ๋ชจ
@Embeddable
ํด๋์ค์ ์ ์ํ์ ๊ตฌ์กฐ ์ ์ โ ๊ณ ์ ํ๋๋ ์์ ํด๋์ค์ ์ ์
์กฐํ์ ์ ์ฐ์ฑ โ JPQL์์ ํ์ ํ์ ๊ตฌ๋ถ ํํฐ๋ง ๊ฐ๋ฅ (type())
์ฟผ๋ฆฌ ํํ๋ ฅ โ treat() ์ฌ์ฉ ๊ฐ๋ฅ โ ๊ฐ ํ์ ์์๋ ๋คํ์ฑ ์กฐ์ ๊ฐ๋ฅ
์ฟผ๋ฆฌ ์ต์ ํ โ ๊ฐ ํ์ ์ด์ง๋ง ์กฐ๊ฑด๋ณ ํํฐ๋ง์ด ๊ฐ๋ฅํด์ ธ ์ฑ๋ฅ ํฅ์ ๊ฐ๋ฅ
์ฟผ๋ฆฌ ์ต์ ํ
๊ธฐ์กด (Hibernate 6.5 ์ดํ)
@Embeddable
ํ์ ์ ์ผ๋ฐ ๊ฐ์ฒด์ฒ๋ผ๋ง ๋์ํ๊ณ , ํ์ ๊ตฌ๋ถ์ด ์ ๋จ.์๋ฅผ ๋ค์ด KoreanAddress, ForeignAddress๋ฅผ ๋ชจ๋ Address๋ก ์ ์ฅํ๋ฉด
์ฟผ๋ฆฌ์์๋ ๋์ ๊ตฌ๋ถํ ์ ์์
๋ถํ์ํ ์ ์ฒด ์ค์บ์ด ๋ฐ์ํ๊ฑฐ๋ ์ดํ๋ฆฌ์ผ์ด์ ๋จ์์ ํํฐ๋ง์ ํด์ผ ํจ.
Hibernate 6.6 ์ดํ
Discriminator ๊ธฐ๋ฐ์ผ๋ก ์ฟผ๋ฆฌ ๋จ์์ ํ์ ํ์ ์ ์ ํํ ํํฐ๋ง ๊ฐ๋ฅ
Hibernate๊ฐ
@Embeddable
ํ์ ์ ๋ํด ๋คํ์ฑ์ ์ง์ํ๊ณ Discriminator ์ปฌ๋ผ์ ์์ฑํจ๋ฐ๋ผ์ DB ๋ ๋ฒจ์์ ์ ํํ ์กฐ๊ฑด ํํฐ๋ง ๊ฐ๋ฅ
- address_type ์ปฌ๋ผ์ ์ธ๋ฑ์ค๋ฅผ ๊ฑธ๊ฑฐ๋ ์ ํํ where ์กฐ๊ฑด์ ์ค ์ ์์
์ ํ๋ฆฌ์ผ์ด์ ๋จ์์ ํ์ ํํฐ๋ง ๋ถํ์
๋ถํ์ํ ์กฐ์ธ ์์ด ๊ฐ ํ์ ์กฐ๊ฑด๋ง์ผ๋ก ํํฐ๋ง
7. H2 Bulk Mutation ์ ๋ต ๋ณ๊ฒฝ
๋๋ ์ญ์ (DELETE), ๋๋ ์์ (UPDATE)๋ฅผ ์คํํ ๋, H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๋ฐ์ํ ์ ์๋ ๋ฌด๊ฒฐ์ฑ ์ ์ฝ ์๋ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ ๋ชฉ์ ์ผ๋ก ๋ณ๊ฒฝ๋จ.
๋ณ๊ฒฝ ์ vs ํ
๋ณ๊ฒฝ ์ โ Hibernate๋ H2์์ inline ๋ฐฉ์์ผ๋ก bulk update/delete๋ฅผ ์คํ ํจ
๋ณ๊ฒฝ ํ โ H2์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก temporary table ๋ฐฉ์์ผ๋ก ์คํ๋๋๋ก ๋ณ๊ฒฝ ๋จ.
๋ชฉ์ โ FK ์ ์ฝ ์กฐ๊ฑด ์๋ฐ ์ค๋ฅ ๋ฐฉ์ง ๋ฐ ์ค์ RDBMS์ ์ ์ฌํ ๋์์ ๋ณด์ฅํจ.
Bulk Mutation ์ ๋ต์ด๋?
Hibernate์์ Bulk Mutation์ JPQL ๋๋ HQL์ ์ด์ฉํ ๋๋ ์์ /์ญ์ ๊ตฌ๋ฌธ์ ์คํํ ๋ ์ฌ์ฉํ๋ ๋ด๋ถ ์ ๋ต
INLINE โ SQL์ ์ง์ DELETE, UPDATE๋ก ์คํํจ
TEMP TABLE โ ๋จผ์ ์ญ์ ํ /์ ๋ฐ์ดํธํ ID๋ค์ ์์ ํ ์ด๋ธ์ ์ ์ฅ ํ ์ฒ๋ฆฌํจ.
CTE (WITH ๋ฌธ) โ ๊ณตํต ํ ์ด๋ธ ํํ์(Common Table Expression)์ ์ฌ์ฉํจ.
H2 DB์์์ ๊ธฐ์กด ๋ฌธ์ (INLINE ์ ๋ต)
- Hibernate์ INLINE ๋ฐฉ์์ ์์ ์์ด ์ญ์ ํจ โ FK ์ถฉ๋ ๋ฐ์
H2์ FK ์ ์ฝ ์กฐ๊ฑด
H2๋ ๋ค๋ฅธ RDBMS๋ณด๋ค ์ธ๋ ํค ์ ์ฝ์ ๋ ์๊ฒฉํ๊ฒ ์ฆ์ ํ๊ฐ(eager evaluation)ํจ.
๋๋ถ๋ถ์ ์ค์ RDBMS(ex. MySQL, Oracle ๋ฑ)๋
FK ์ ์ฝ์ ํธ๋์ญ์ ์ปค๋ฐ ์์ ๊น์ง defer(์ง์ฐ ํ๊ฐ)ํ ์๋ ์๊ณ
๋๋ ์ญ์ ์ ๋ด๋ถ์ ์ผ๋ก ์์๋ฅผ ์กฐ์ ํ๋ ค ์ถฉ๋ ์์ด ์ฒ๋ฆฌํ ์ ์์
H2์ ๊ฒฝ์ฐ
์ญ์ /์์ SQL ์คํ ์์ ์ ๋ฐ๋ก FK ๋ฌด๊ฒฐ์ฑ ์กฐ๊ฑด์ ๊ฒ์ฌํจ
ํธ๋์ญ์ ๋ด์์๋ผ๋ FK ์ถฉ๋์ด ์์ผ๋ฉด ๋ฐ๋ก ์์ธ๋ฅผ ๋์ง (ํ ์คํธ ์ค ๋ฌธ์ ๊ฐ ๋ ์ ์์)
H2์์ ์์ฃผ ๋ฌธ์ ๋๋ ์ํฉ
JPQL๋ก ๋๋ ์ญ์ ์: Hibernate๊ฐ DELETE FROM Parent๋ฅผ ๋จผ์ ์คํ โ FK ์ค๋ฅ ์ฆ์ ๋ฐ์
@Transactional
์์์ save โ delete ์๋: ์ ์ฅ์ด ๋๊ธฐ ์ ์ delete๊ฐ ํธ๋ฆฌ๊ฑฐ ๋๋ฉด FK ๊ฒ์ฌ ์คํจbulk ์ฐ์ฐ ์์๊ฐ ๋ถ์ ์ ํ ๊ฒฝ์ฐ: ์์ โ ๋ถ๋ชจ ์์ด ์๋๋ฉด ์ค๋ฅ
H2๋ ์ค์ RDBMS๋ณด๋ค ๋ ์ฆ์, ํธ๋์ญ์ ๊ณผ ๋ฌด๊ดํ๊ฒ ์ธ๋ํค ๋ฌด๊ฒฐ์ฑ ๊ฒ์ฌ๋ฅผ ์ํํ๊ธฐ ๋๋ฌธ์ ํ ์คํธ ์ค bulk ์ญ์ ๋ ์์ ์ด ์์๋ณด๋ค ๋ ์์ฃผ ์คํจํ ์ ์์.
Hibernate 6.6 ๋ณ๊ฒฝ ๋ด์ฉ (TEMP TABLE ์ ๋ต)
์ญ์ /์์ ๋์ ID๋ฅผ ์์ ํ ์ด๋ธ (hibernate_temp_ids)์ ์ ์ฅํจ
์ดํ ์ด ์์ ํ ์ด๋ธ์ ์ด์ฉํด DELETE, UPDATE ์ํ
FK๊ฐ ๊ฑธ๋ ค ์์ด๋ ์์ ํ๊ฒ ์์๋ฅผ ์ ์ดํ ์ ์์
์ค์ ๋ฐฉ๋ฒ
hibernate.hql.bulk_id_strategy=org.hibernate.hql.spi.id.inline.InlineIdsInClauseBulkIdStrategy
hibernate.hql.bulk_id_strategy=org.hibernate.hql.spi.id.table.LocalTemporaryTableBulkIdStrategy // ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋ณ๊ฒฝ๋จ
- Hibernate 6.6์์ H2 ๊ธฐ๋ณธ ์ ๋ต์ TEMP TABLE๋ก ๋ฐ๊ฟจ์ง๋ง, ์ํ๋ค๋ฉด ์ ๋ต์ ๋ช ์์ ์ผ๋ก ์ ํํ ์ ์๋ค.
8. Expression#as(Class)
โ case()
๋ช
ํํ
Criteria API ์์ ํ์ ๋ณํ ๋๋ ์กฐ๊ฑด ๋ถ๊ธฐ๋ฅผ ํํํ ๋ ํผ๋์ ์ค์ด๊ณ , ๋ช ํํ๊ณ ์ผ๊ด๋ API ์ฌ์ฉ์ ์ ๋ํ๊ธฐ ์ํด ๋ณ๊ฒฝํจ.
- ๊ฐ๋ฐ์๋ค์ด
as(class)
๋ฅผ ํ์ ๋ณํ / ์กฐ๊ฑด ๋ถ๊ธฐ / ๋คํ์ฑ ์ฒ๋ฆฌ๋ก ํผ๋ํด์ ์ฌ์ฉํจ.
Hibernate 6.6์ ๊ฐ์ ์ฌํญ
ํ์ ๋ณํ โ expression.as(Class) (๊ทธ๋๋ก ์ฌ์ฉ)
์กฐ๊ฑด ๋ถ๊ธฐ (CASE WHEN) โ criteriaBuilder.select(criteriaBuilder.<Type>selectCase()โฆ)
๋คํ์ฑ ์บ์คํ โ treat() ์ฌ์ฉ
Criteria API์์ ํผ๋ ๋๋ ์์
-- JPQL
SELECT CASE WHEN o.amount > 100 THEN 'HIGH' ELSE 'LOW' END FROM Order o
criteriaBuilder.select(orderRoot.get("amount").as(String.class))
// as() ์๋ฏธ ๋ถ๋ถ๋ช
// ์๋์ผ๋ก HIGH, LOW๋ก ์บ์คํ
๋๋ ๊ฒ์ด ์๋๋, ์คํดํด์ ๋ง์ด ์ฌ์ฉ ํจ.
- Hibernate 6.6์์๋ ๋ค์ ์ฒ๋ผ ๋ช ํํ๊ฒ ์์ฑํ ์ ์๋๋ก ์ ๋ํจ.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<String> cq = cb.createQuery(String.class);
Root<Order> root = cq.from(Order.class);
cq.select(
cb.selectCase()
.when(cb.gt(root.get("amount"), 100), "HIGH")
.otherwise("LOW")
);
Criteria ๋?
JPA์์ ๋งํ๋ Criteria API๋ SQL์ฒ๋ผ ๋ฌธ์์ด๋ก ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ์ง ์๊ณ , Java ๋๋ Kotlin ์ฝ๋๋ก ์ฟผ๋ฆฌ๋ฅผ ๋์ ์ผ๋ก ์กฐ๋ฆฝํ ์ ์๋๋ก ๋ง๋ ํ์ค API
์ธ์ , ์ ์ธ๊น?
๋์ ์ฟผ๋ฆฌ ์กฐ๋ฆฝ โ ์กฐ๊ฑด์ด ์ ํ์ ์ผ๋ก ๋ฐ๋ ๊ฒฝ์ฐ (if, for, switch๋ก ์กฐ๊ฑด ์ถ๊ฐ ๊ฐ๋ฅ)
ํ์ ์์ ์ฑ โ ์ปดํ์ผ ํ์์ ์ปฌ๋ผ ์ด๋ฆ ์คํ๋ ํ์ ์ค๋ฅ๋ฅผ ๋ฐฉ์งํ ์ ์์
IDE ์๋ ์์ฑ โ Kotlin/Java ๊ตฌ์กฐ ๊ธฐ๋ฐ์ผ๋ก IDE์์ ์ฝ๋ ์๋์์ฑ ๊ฐ๋ฅ
์ฅ์ โ JPA ํ์ค (๋ชจ๋ ๊ตฌํ์ฒด์์ ์ฌ์ฉ ๊ฐ๋ฅ)
๋จ์ โ ์ฝ๋๊ฐ ์ฅํฉํ๊ณ ๋ณต์กํด์ง โ ์ค๋ฌด์์๋ ๋ค๋ฅธ ์ฟผ๋ฆฌ ๊ธฐ์ ์ ๋ง์ด ์ ํธ
Criteria API ์ ๋ค๋ฅธ ์ฟผ๋ฆฌ ๊ธฐ์ ๋ค๊ณผ์ ๋น๊ต
ํญ๋ชฉ | Criteria API | QueryDSL | Spring Data Specification | Kotlin DSL (Spring 3.0+) |
๊ธฐ๋ณธ ์ฑ๊ฒฉ | JPA ํ์ค API | ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ | Spring Data ํ์ฅ | Spring ๊ณต์ Kotlin DSL |
๊ฐ๋ ์ฑ | โ ๋ฎ์ | โ ๋์ | โ ์ค๊ฐ | โ ๋์ |
IDE ์๋์์ฑ | โ ๋ณดํต | โ ๋งค์ฐ ์ข์ | โ ์์ (String ๊ธฐ๋ฐ) | โ Kotlin ์นํ์ |
์ค์ ๋์ด๋ | ์์ | โ ๋ณต์ก (APT, build ํ์) | ๊ฐ๋จ | ๊ฐ๋จ |
Kotlin ํธํ์ฑ | โ ์ ํ์ | โ Java ์ ์ฉ์ด ๋ง์ | โ Kotlin ๊ฐ๋ฅ | โ ์ต์ ํ๋จ |
์กฐ๊ฑด ๋์ ์กฐ๋ฆฝ | โ ๋ฐ์ด๋จ | โ ๋ฐ์ด๋จ | โ ๋ฐ์ด๋จ | โ ๋ฐ์ด๋จ |
์ปค๋ฎค๋ํฐ/๋ฌธ์ | ๐ ๋ง์ (JPA ํ์ค) | ๐ ๋ง์ | ๐ ๊ฐ๊ฒฐ | ๐ Spring 3.0 ์ด์ ๊ณต์ ์ง์ |
์ด๋ค ๊ธฐ์ค์ผ๋ก ์ ํํด์ผํ ๊น?
๊ฐ๋ฐ ํ๊ฒฝ | ์ถ์ฒ ๋ฐฉ์ |
์คํ๋ง + Kotlin | Spring Data Kotlin DSL, Specification |
์คํ๋ง + Java | QueryDSL ๋๋ Criteria |
๋์ ์ฟผ๋ฆฌ ์กฐ๋ฆฝ์ด ๋ง์ ๊ฒฝ์ฐ | Criteria API ๋๋ QueryDSL |
๋จ์ ์กฐ๊ฑด + ๋น ๋ฅธ ๊ฐ๋ฐ | Spring Data JPA ๋ฉ์๋ ์ฟผ๋ฆฌ or Specification |
๋ณต์กํ ๊ฒ์ API (๊ด๋ฆฌ์, ํํฐ ๋ฑ) | QueryDSL or Criteria API |
9. @Table
+ SINGLE_TABLE ์์ ์ ๋ต ์ถฉ๋ ๊ฒ์ถ
JPA์์ ์์ ์ ๋ต์ ์๋ชป ์ฌ์ฉํ๋ ์ค์๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํ ์ ์ฝ ์ถ๊ฐ
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
JPA์ ์์ ๋งคํ ์ ๋ต ์ค ํ๋
๋ถ๋ชจ ํด๋์ค์ ์์ ํด๋์ค์ ๋ฐ์ดํฐ๋ฅผ ํ ํ ์ด๋ธ์ ์ ์ฅํจ.
Hibernate๋ ์ด๋ฅผ ์ํด Discriminator ์ปฌ๋ผ์ ์๋ ์์ฑํด์ ์ด๋ค ์์ ํ์ ์ธ์ง ๊ตฌ๋ถํจ
@Table(name = โฆ )
์ํฐํฐ๊ฐ ๋งคํ๋ ์ค์ ํ ์ด๋ธ ์ด๋ฆ์ ์ง์ ํ๋ ์ด๋ ธํ ์ด์
๊ธฐ๋ณธ์ ์ผ๋ก ํด๋์ค ์ด๋ฆ์ ๋ฐ๋ฅด์ง๋ง, ์ํ๋ ํ ์ด๋ธ๋ช ์ ์ง์ ์ง์ ํ ์ ์์
๋ฌธ์ ์ํฉ (Hibernate 6.5 ์ดํ)
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(name = "animal") // ๋ถ๋ชจ ํ
์ด๋ธ ์ง์
open class Animal(
@Id @GeneratedValue
val id: Long? = null
)
@Entity
@Table(name = "cat") // SINGLE_TABLE ์ ๋ต์ธ๋ฐ ์์์๊ฒ ๋ค๋ฅธ ํ
์ด๋ธ ์ง์
class Cat(
val sound: String
) : Animal()
SINGLE_TABLE ์ ๋ต์ ๋ถ๋ชจ + ์์ ๋ชจ๋ ํ ํ ์ด๋ธ์ ์ ์ฅํด์ผ ํจ.
๊ทธ๋ฐ๋ฐ ์์ ํด๋์ค Cat์
@Table(name = โcatโ)
์ ์ง์ ํ๋ฉด ์ด ์์ ํด๋์ค๋ ๋ณ๋ ํ ์ด๋ธ์ ์ฐ๊ฒ ๋ค๋ ๋ปHibernate์ ๋งคํ ์ ๋ต๊ณผ ์ถฉ๋ ๋ฐ์
Hibernate 6.5 ์ดํ๋ ์ด๊ฑธ ๊ฐ์งํ์ง ์๊ณ ๋ฌต์ธํ๊ฑฐ๋ ๋ฐํ์์ ๋ชจํธํ ์ค๋ฅ๋ก ์ด์ด์ง.
Hibernate 6.6์์์ ๋ณ๊ฒฝ์
๋ถ๋ชจ๊ฐ SINGLE_TABLEl ์ ๋ต์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
์์ ํด๋์ค์
@Table
๋ช ์ํ๋ฉด ์ฆ์ ์์ธ ๋ฐ์MappingException: A @Table annotation on a subclass of a SINGLE_TABLE inheritance hierarchy is not allowed
์ค๋ฌด์์ ์ด๋ฐ ์ฝ๋๋ฅผ ์ฐ๋ ์ด์
์์ ์ํฐํฐ๋ง๋ค ํ ์ด๋ธ์ ๋ฐ๋ก ๋ง๋ค๊ณ ์ถ์ด์
- ์ ๋ต ์์ฒด๋ฅผ JOINED๋ TABLE_PER_CLASS๋ก ๋ฐ๊ฟจ์ด์ผ ํจ
SINGLE_TABLE ์ ๋ต์ ์ฐ๋ฉด์๋ ํ ์ด๋ธ ๋ช ์ ๋ช ํํ ํ๊ธฐ ์ํด ์๋ชป ๋ถ์ธ ๊ฒฝ์ฐ
JPA์ 3๊ฐ์ง ์์ ์ ๋ต
SINGLE_TABLE (๋จ์ผ ํ ์ด๋ธ ์ ๋ต)
๋ถ๋ชจ ํด๋์ค์ ์์ ํด๋์ค์ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ํ๋์ ํ ์ด๋ธ์ ์ ์ฅํ๋ค.
๊ตฌ๋ถ์ ์ํด Hibernate๊ฐ ์๋์ผ๋ก Discriminator ์ปฌ๋ผ์ ์์ฑํด ์ด๋ค ์์ ํ์ ์ธ์ง ๊ตฌ๋ถํ๋ค.
์ฅ์
์ฑ๋ฅ ์ข์ โ ์กฐ์ธ ์์ด ํ ํ ์ด๋ธ๋ง ์กฐํ
๊ตฌํ ๋จ์ โ ํ ์ด๋ธ์ด ํ๋๋ฟ์ด๋ผ ์ฟผ๋ฆฌ๊ฐ ๋น ๋ฅด๊ณ ๊ตฌ์กฐ ๋จ์
๊ธฐ๋ณธ ์ ๋ต โ JPA์ ๊ธฐ๋ณธ ์์ ์ ๋ต์ด๋ผ ์ค์ ์๋ต ์ ์ฌ์ฉ๋จ
๋จ์
NULL ์ปฌ๋ผ ๋ง์์ง โ ์์๋ง๋ค ํ๋๊ฐ ๋ค๋ฅด๋ฉด ๋๋ถ๋ถ ์ปฌ๋ผ์ด NULL์ด ๋จ
์คํค๋ง ์ ๊ทํ ๊นจ์ง โ ํ ์ด๋ธ์ด ์ปค์ง๊ณ ์ค๋ณต๋ ์๋ฏธ ์๋ ์ปฌ๋ผ ์ฆ๊ฐ
@Table
์ถฉ๋ ์ฃผ์ โ Hibernate 6.6๋ถํฐ ์์์@Table
์ฌ์ฉ ์ ์์ธ ๋ฐ์
์์
-- ๋จ์ผ ํ
์ด๋ธ์ ๋ชจ๋ ํด๋์ค ์ ๋ณด ์ ์ฅ
id | dtype | name | sound | breed
---|-----------|--------|---------|-------
1 | Cat | nabi | meow | NULL
2 | Dog | choco | NULL | shiba
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
open class Animal(@Id @GeneratedValue val id: Long? = null, val name: String)
@Entity
@DiscriminatorValue("Cat")
class Cat(val sound: String) : Animal()
@Entity
@DiscriminatorValue("Dog")
class Dog(val breed: String) : Animal()
JOINED (์กฐ์ธ ์ ๋ต)
๋ถ๋ชจ ํด๋์ค๋ ๊ณตํต ํ๋๋ง ํ ์ด๋ธ๋ก ์์ฑ
์์ ํด๋์ค๋ ์์ ๊ณ ์ ํ๋๋ง ๋ณ๋ ํ ์ด๋ธ๋ก ์์ฑ
์กฐํ ์ ๋ถ๋ชจ-์์ ํ ์ด๋ธ ๊ฐ JOIN ์ํ
์ฅ์
์ ๊ทํ ๊ตฌ์กฐ โ ํ ์ด๋ธ์ด ๋ ผ๋ฆฌ์ ์ผ๋ก ๊น๋ํ๊ณ ์ค๋ณต ์์
์์ ํ ์ด๋ธ์ ๊ณ ์ ์ ์ฝ ๊ฐ๋ฅ โ ์์ ํ ์ด๋ธ์ ๋ณ๋ ์ ์ฝ ์กฐ๊ฑด ์ค์ ๊ฐ๋ฅ
NULL ์ปฌ๋ผ ์์ โ ์์ ํ๋๋ ์์ ํ ์ด๋ธ์๋ง ์กด์ฌ
๋จ์
์ฑ๋ฅ ์ ํ โ JOIN์ด ํ์ํด ์กฐํ ์ฑ๋ฅ ๋๋ฆด ์ ์์
์ฟผ๋ฆฌ ๋ณต์ก โ ํ ์ด๋ธ ์ฌ๋ฌ๊ฐ๋ฅผ JOIN ํด์ผ ํจ.
์์
-- ๋ถ๋ชจ ํ
์ด๋ธ
id | name
---|-----
1 | nabi
2 | choco
-- ์์ ํ
์ด๋ธ (cat)
id | sound
---|-------
1 | meow
-- ์์ ํ
์ด๋ธ (dog)
id | breed
---|--------
2 | shiba
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
open class Animal(@Id @GeneratedValue val id: Long? = null, val name: String)
@Entity
@Table(name = "cat")
class Cat(val sound: String) : Animal()
@Entity
@Table(name = "dog")
class Dog(val breed: String) : Animal()
TABLE_PER_CLASS (์์๋ณ ํ ์ด๋ธ ์ ๋ต)
๋ถ๋ชจ ํด๋์ค๋ ์ค์ ํ ์ด๋ธ์ด ์์
์์ ํด๋์ค๋ง๋ค ๊ฐ์ ํ ์ด๋ธ ์์ฑ, ๋ถ๋ชจ์ ํ๋๋ ๋ชจ๋ ํฌํจ
์ฆ, ๊ฐ ์์ ํด๋์ค๋ ์๊ธฐ ํ๋๋ฅผ ๋ชจ๋ ๊ฐ๋ ๋ ๋ฆฝ ํ ์ด๋ธ
์ฅ์
JOIN ๋ถํ์ โ ์์ ํ ์ด๋ธ์ ๋ชจ๋ ํ๋๊ฐ ์กด์ฌ
์ฑ๋ฅ ๋น ๋ฆ (๋จ์ผ ์กฐํ ์) โ ์ฟผ๋ฆฌ๊ฐ ๋จ์ํจ
๊ตฌ์กฐ ๋ช ํ โ ํ ์ด๋ธ = ํด๋์ค ๊ตฌ์กฐ ๊ทธ๋๋ก
๋จ์
์ค๋ณต ๋ฐ์ดํฐ โ ๋ถ๋ชจ ํ๋๊ฐ ๊ฐ ์์ ํ ์ด๋ธ์ ๋ฐ๋ณต๋จ
์ ์ฒด ์กฐํ ์ด๋ ค์ โ ์์๋ง๋ค UNION์ด ํ์ํจ (
SELECT * FROM cat UNION ALL SELECT * FROM dog
)ID ์ค๋ณต ์ฃผ์ โ ๊ฐ ํ ์ด๋ธ์์ ID ์ถฉ๋ ๋ฐฉ์ง๋ฅผ ์ํด
@GeneratedValue(strategy = TABLE)
๊ถ์ฅ
์์
-- cat ํ
์ด๋ธ
id | name | sound
---|-------|-------
1 | nabi | meow
-- dog ํ
์ด๋ธ
id | name | breed
---|-------|--------
2 | choco | shiba
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
open class Animal(@Id @GeneratedValue(strategy = GenerationType.AUTO) val id: Long? = null, val name: String)
@Entity
class Cat(val sound: String) : Animal()
@Entity
class Dog(val breed: String) : Animal()
์ถ์ฒ : https://docs.jboss.org/hibernate/orm/6.6/migration-guide/migration-guide.html
Subscribe to my newsletter
Read articles from ๐ ๐๐๐ directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

๐ ๐๐๐
๐ ๐๐๐
Back-end Engineer