[JPA] @ManyToOne

2021. 10. 5. 22:27ใ†Back-End ์ž‘์—…์‹ค/Spring Framework

728x90
๋ฐ˜์‘ํ˜•

๐Ÿ“Œ @ManyToOne

์ด๊ฒƒ์€ ํ…Œ์ด๋ธ” ๊ฐ„์— ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ์žˆ์„ ๋•Œ, ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด์—์š”. RDBMS์—์„œ๋Š” FK๋ฅผ ํ†ตํ•ด ๋‹ค๋ฅธ Table์„ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด์—์š”. ์ด ๋•Œ, N:1, 1:N, N:M ๋“ฑ์˜ ๊ด€๊ณ„๊ฐ€ ํ˜•์„ฑ์ด ๋˜๋Š”๋ฐ, ์˜ˆ๋ฅผ ๋“ค์–ด Member (ํšŒ์›) Table๊ณผ Board (๊ฒŒ์‹œํŒ) Table์ด ์žˆ์„ ๋•Œ, ํ•œ ๋ช…์˜ ํšŒ์›์€ ์—ฌ๋Ÿฌ๊ฐœ์˜ ๊ฒŒ์‹œ๊ธ€์„ ๋‚จ๊ธธ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด์—์š”.

๊ทธ๋Ÿฐ๋ฐ, ์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์€ @ManyToOne๋„ ์žˆ๊ณ , @OneToMany๋„ ์žˆ๋Š” ๊ฒƒ์ธ๋ฐ, ์–ด๋–ป๊ฒŒ ๊ตฌ๋ถ„ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ์š”?

๋ฐ”๋กœ ์ฐธ์กฐ๋ฅผ ํ•  Table ๊ธฐ์ค€์œผ๋กœ ์ƒ๊ฐ์„ ํ•˜๋ฉด ๋œ๋‹ต๋‹ˆ๋‹ค! Board Table์ด Member Table์„ ์ฐธ์กฐํ•  ๊ฒƒ์ด๊ณ , Board Table์ด N์ด๊ธฐ ๋•Œ๋ฌธ์— Board Entity์—์„œ @ManyToOne์„ ์‚ฌ์šฉํ•˜๋ฉด ๋˜๋Š” ๊ฒƒ์ด์—์š”.

 

 

์œ„์˜ Code๋Š” SystemUseManual์ด๋ผ๋Š” ๊ฒŒ์‹œํŒ๊ณผ Member Table์„ ์—ฐ๊ด€ ๊ด€๊ณ„ ์ง€์ •์„ ํ•ด ์ค€ ๊ฒƒ์ด์—์š”.

์ž‘์„ฑ์ž๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ณ€์ˆ˜๋ช…์€ writer๋ผ๊ณ  ํ–ˆ๋‹ต๋‹ˆ๋‹ค!

 

์ฃผ๋‹ˆํ•˜๋ž‘์€ ํ˜„์žฌ ํ•ด๋‹น ๊ฒŒ์‹œํŒ์— ๋Œ“๊ธ€ ๊ธฐ๋Šฅ๋„ ๋„ฃ๊ธฐ ์œ„ํ•ด ๋”ฐ๋กœ Entity๋ฅผ ๋ถ„๋ฆฌํ•œ ๊ฒƒ์ด์—์š”.

 

 

๊ทผ๋ฐ ์—ฌ๊ธฐ์„œ ์ œ๊ฐ€ ์ž˜ ๋ชป ํŒ๋‹จํ•œ ๊ฒƒ์ด ์žˆ๋Š” ๊ฒƒ์ด์—์š”. ์œ„์— Code๋ฅผ ๋ณด๋ฉด ๊ฒŒ์‹œํŒ๊ณผ์˜ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ์œ„ํ•ด @ManyToOne์„ ์“ฐ๊ณ , ๋Œ“๊ธ€ ์ž‘์„ฑ์ž๊ฐ€ ํ•„์š”ํ•˜๋‹Œ๊นŒ ๋‹น์—ฐํžˆ Member Table๊ณผ ๋˜ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งบ์–ด์ค˜์•ผ ํ•˜๋Š”์ง€ ์•ˆ ๊ฒƒ์ด์—์š”.

 

 

ํ•˜์ง€๋งŒ ์ด๊ฑด ์—„์ฒญ๋‚œ ์ฐฉ๊ฐ์ด๊ณ , ์‹ค์ˆ˜์˜€๋‹ต๋‹ˆ๋‹ค!

 

 

์œ„์— left outer join์œผ๋กœ ์—ฌ๋Ÿฌ Entity์™€ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ๋งบ์–ด์„œ Data๋ฅผ ๊ฐ–๊ณ  ์˜ค๋Š”๋ฐ, ๋ณด์‹œ๋‹ค ์‹œํ”ผ Member Entity๋ฅผ ๋‘ ๋ฒˆ์ด๋‚˜ Joinํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด์—์š”!

 

 

 

์œ„์™€ ๊ฐ™์ด Member์— ๋Œ€ํ•œ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ์—†์•ค ๋’ค ๋‹ค์‹œ Test๋ฅผ ์ง„ํ–‰ ํ•ด ๋ณผ ๊ฒƒ์ด์—์š”.

 

 

์ด๋ฒˆ์—๋Š” ์ •์ƒ์ ์œผ๋กœ ๋”ฑ! ํ•œ๋ฒˆ๋งŒ Member Entity๋กœ join์„ ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด์—์š”.

 

ํ•˜.์ง€.๋งŒ ์œ„์—์„œ ๋ณด๋Š” ๋ฐ”์™€ ๊ฐ™์ด SQL ๋ฌธ์„ ๋ณด๋ฉด Comment Table, UseManual Table, Member Table๊นŒ์ง€ ๋ชจ๋‘ Join์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด์—์š”. Comment๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ, ๋งค๋ฒˆ ๊ฒŒ์‹œํŒ(UseManual)๊ณผ Member๊นŒ์ง€ Joinํ•ด์„œ ๊ฐ€์ ธ์˜ฌ ํ•„์š”๊ฐ€ ๋งŽ์ง€๋Š” ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์œ„์™€ ๊ฐ™์ด ์—ฌ๋Ÿฌ Table์„ Join์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๋Š” ์ƒํ™ฉ์€ ์ข‹์ง€ ์•Š์€ ๊ฒƒ์ด์—์š”. 

 

 

    ๐Ÿ“ fetch๋Š” Lazy Loading์„ ์‚ฌ์šฉํ•˜์ž!

์œ„์˜ Query ์‹คํ–‰ ๊ฒฐ๊ณผ์™€ ๊ฐ™์ด ํŠน์ • Entity๋ฅผ ์กฐํšŒํ•  ๋•Œ, ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๊ฐ€์ง„ ๋ชจ๋“  Entity๋ฅผ ๊ฐ™์ด Lodingํ•˜๋Š” ๊ฒƒ์„ 'Eager Loding'์ด๋ผ๊ณ  ํ•˜๋Š” ๊ฒƒ์ด์—์š”. 'Eager'๋Š” '์—ด๋ ฌํ•œ, ์ ๊ทน์ ์ธ'์ด๋ผ๋Š” ๋œป์„ ๊ฐ–๊ณ  ์žˆ๊ณ , ์ผ๋ฐ˜์ ์œผ๋กœ '์ฆ‰์‹œ ๋กœ๋”ฉ'์ด๋ผ๊ณ  ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ์ด์—์š”.

 

'์ฆ‰์‹œ ๋กœ๋”ฉ'์€ ํ•œ ๋ฒˆ์— ์—ฐ๊ด€ ๊ด€๊ณ„๊ฐ€ ์žˆ๋Š” ๋ชจ๋“  Entity๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์œผ๋‚˜, ์—ฌ๋Ÿฌ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ๋งบ๊ณ  ์žˆ๊ฑฐ๋‚˜, ์—ฐ๊ด€ ๊ด€๊ณ„๊ฐ€ ๋ณต์žกํ• ์ˆ˜๋ก Join์œผ๋กœ ์ธํ•œ ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ํ”ผํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์ด์—์š”.JPA์—์„œ ์—ฐ๊ด€ ๊ด€๊ณ„์˜ Data๋ฅผ ์–ด๋–ป๊ฒŒ ๊ฐ€์ ธ์˜ฌ ๊ฒƒ์ธ๊ฐ€๋ฅผ fetch(ํŒจ์น˜)๋ผ๊ณ  ํ•˜๋Š”๋ฐ, ์—ฐ๊ด€ ๊ด€๊ณ„ Annotation์˜ ์†์„ฑ์œผ๋กœ 'fetch' Mode๋ฅผ ์ง€์ •ํ•œ๋‹ต๋‹ˆ๋‹ค!

์ฆ‰์‹œ ๋กœ๋”ฉ์€ ๋ถˆ ํ•„์š”ํ•œ Join๊นŒ์ง€ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , ๊ทธ์™€ ๋ฐ˜๋Œ€๋˜๋Š” 'Lazy Loding'์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹์€ ๊ฒƒ์ด์—์š”. 'Lazy Loding'์€ '์ง€์—ฐ ๋กœ๋”ฉ, ๊ฒŒ์ด๋ฅธ ๋กœ๋”ฉ'์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ต๋‹ˆ๋‹ค!

 

๊ทธ๋Ÿฌ๋ฉด ๊ฒŒ์‹œํŒ(UseManul)์„ ํ•œ๋ฒˆ ๊ณ ์ณ ๋ณด๋„๋ก ํ•  ๊ฒƒ์ด์—์š”.

 

 

UseManual Entity Class์—์„œ ์ˆ˜์ •๋œ ๋ถ€๋ถ„์€ @ManyToOne์— fetch ์†์„ฑ์„ ๋ช…์‹œํ•˜๊ณ , FetchType.LAZY๋ผ๋Š” ์ง€์—ฐ ๋กœ๋”ฉ์„ ์ ์šฉํ•œ ๋ถ€๋ถ„์ธ ๊ฒƒ์ด์—์š”. ๊ทธ๋Ÿผ ๋‹ค์‹œ Test๋ฅผ ๋Œ๋ ค ๋ณด๋„๋ก ํ•  ๊ฒƒ์ด์—์š”!

 

 

์•—! ๋ญ”๊ฐ€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ด์—์š”! ๋ฐœ์ƒํ•œ Exception Message๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒƒ์ด์—์š”.

 

์ด ๋ง์€ Data Base์™€ ์ถ”๊ฐ€์ ์ธ ์—ฐ๊ฒฐ์ด ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์ด์—์š”.

 

์ง€์—ฐ ๋กœ๋”ฉ ๋ฐฉ์‹์œผ๋กœ ๋กœ๋”ฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— UseManual Table๋งŒ ๊ฐ€์ ธ์™€์„œ System.out.println()์„ ํ•˜๋Š” ๊ฒƒ์€ ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ, 

๋ฐ˜์‘ํ˜•

์—ฌ๊ธฐ์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด์—์š”. manual.getWriter()๋Š” member Table์„ ๋กœ๋”ฉํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ด๋ฏธ Data Base์™€ ์—ฐ๊ฒฐ์ด ๋๋‚œ ์ƒํƒœ์ด๊ธฐ ๋•Œ๋ฌธ์— Exception์ด ํ„ฐ์ง€๋Š” ๊ฒƒ์ด์—์š”.

'no Session'์ด๋ผ๋Š” ๋ฉ”์‹œ์ง€๋Š” ์ด๋Ÿฐ ๊ฒฝ์šฐ ๋ฐœ์ƒํ•œ๋‹ต๋‹ˆ๋‹ค! ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์‹œ ํ•œ๋ฒˆ Data Base์™€ ์—ฐ๊ฒฐ์ด ํ•„์š”ํ•œ๋ฐ, @Transactional์ด ๋ฐ”๋กœ ์ด๋Ÿฌํ•œ ์ฒ˜๋ฆฌ์— ๋„์›€์„ ์ฃผ๋Š” ๊ฒƒ์ด์—์š”. Method ์„ ์–ธ๋ถ€์— ์ด Annotation์„ ๋„ฃ์–ด ๋ณด๋„๋ก ํ• ๊ฒŒ์š”!

 

 

@Transactional ์ด ์นœ๊ตฌ๋Š” ํ•ด๋‹น Method๋ฅผ ํ•˜๋‚˜์˜ 'ํŠธ๋žœ์žญ์…˜'์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ผ๋Š” ์˜๋ฏธ์ธ ๊ฒƒ์ด์—์š”. ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉด ์†์„ฑ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•˜์ง€๋งŒ, ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ํ•„์š”ํ•  ๋•Œ ๋‹ค์‹œ Data Base์™€ ์—ฐ๊ฒฐ์ด ์ƒ์„ฑ๋œ๋‹ต๋‹ˆ๋‹ค!

 

๐Ÿ’ก ์ฐธ๊ณ  :

Transaction์ด๋ž€?

 

Hibernate: 
    select
        systemusem0_.no as no1_1_0_,
        systemusem0_.moddate as moddate2_1_0_,
        systemusem0_.regdate as regdate3_1_0_,
        systemusem0_.content as content4_1_0_,
        systemusem0_.title as title5_1_0_,
        systemusem0_.writer_id as writer_i6_1_0_ 
    from
        system_use_manual systemusem0_ 
    where
        systemusem0_.no=?
com.hongga.junyharang.domain.entity.manual.SystemUseManual@202b5293
Hibernate: 
    select
        member0_.id as id1_0_0_,
        member0_.moddate as moddate2_0_0_,
        member0_.regdate as regdate3_0_0_,
        member0_.email as email4_0_0_,
        member0_.grade as grade5_0_0_,
        member0_.name as name6_0_0_,
        member0_.password as password7_0_0_,
        member0_.phone as phone8_0_0_ 
    from
        member member0_ 
    where
        member0_.id=?
com.hongga.junyharang.domain.entity.member.Member@7137cd14
2021-10-05 18:41:40.137  INFO 17188 --- [    Test worker] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test: [DefaultTestContext@2ce86164 testClass = SystemUseManualRepositoryTest, testInstance = com.hongga.junyharang.repository.manual.SystemUseManualRepositoryTest@9ba167e, testMethod = ๊ฒŒ์‹œ๊ธ€_์กฐํšŒ@SystemUseManualRepositoryTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@5e8f9e2d testClass = SystemUseManualRepositoryTest, locations = '{}', classes = '{class com.hongga.junyharang.JunyharangApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@4c51cf28, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@66ac5762, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@517d4a0d, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@1941a8ff, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@6aa3a905, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@1e683a3e], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true, 'org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]

 

Test ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ์ฒ˜์Œ์—๋Š” SystemUseManual Table๋งŒ ๋กœ๋”ฉํ•ด์„œ ์ฒ˜๋ฆฌํ•˜์˜€์œผ๋‚˜, SystemUseManual.getWriter()๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด member Table์„ ๋กœ๋”ฉํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด์—์š”. ์ง€์—ฐ ๋กœ๋”ฉ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ ์ž๋™์„ SystemUseManual Table๊ณผ Member Table์ด ์กฐ์ธ์œผ๋กœ ์ฒ˜๋ฆฌ๋œ ๊ฒƒ๊ณผ๋Š” ์ฐจ์ด๊ฐ€ ์žˆ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด์—์š”.

 

 

 

    ๐Ÿ“ ์ง€์—ฐ ๋กœ๋”ฉ ( lazy loding )์˜ ์žฅ / ๋‹จ์ 

์ง€์—ฐ ๋กœ๋”ฉ์€ ์กฐ์ธ์„ ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋‹จ์ˆœํ•˜๊ฒŒ ํ•˜๋‚˜์˜ ํ…Œ์ด๋ธ”์„ ์ด์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ๋น ๋ฅธ ์†๋„์˜ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ด์—์š”. ํ•˜์ง€๋งŒ, ์œ„์™€ ๊ฐ™์ด ํ•„์š”ํ•œ ์ˆœ๊ฐ„์— ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์—ฐ๊ด€ ๊ด€๊ณ„๊ฐ€ ๋ณต์žกํ•œ ๊ฒฝ์šฐ ์—ฌ๋Ÿฌ ๋ฒˆ์˜ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋Š” ๊ฒƒ์ด์—์š”.

๋”ฐ๋ผ์„œ ๋ณดํŽธ์ ์ธ ์ฝ”๋”ฉ ๊ฐ€์ด๋“œ๋Š” '์ง€์—ฐ ๋กœ๋”ฉ์„ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ , ์ƒํ™ฉ์— ๋งž๊ฒŒ ํ•„์š”ํ•œ ๋ฐฉ๋ฒ•์„ ์ฐพ๋Š”๋‹ค' ์ธ ๊ฒƒ์ด์—์š”.

 

 

 

   

728x90
๋ฐ˜์‘ํ˜•