๐Ÿ’พ Database

[JPA] Java Persistence API ๋“ฑ์žฅ๋ฐฐ๊ฒฝ, ์‚ฌ์šฉ๋ฐฉ๋ฒ•

์—ฐ_์šฐ๋ฆฌ 2022. 1. 28. 23:42
๋ฐ˜์‘ํ˜•

๋ชฉ์ฐจ

     

     

    JPA ๋“ฑ์žฅ๋ฐฐ๊ฒฝ

     

    [MyBatis] ๋™์ž‘์›๋ฆฌ, ์‚ฌ์šฉ๋ฐฉ๋ฒ• ์ •๋ฆฌ

    ๋ชฉ์ฐจ MyBatis ๋“ฑ์žฅ๋ฐฐ๊ฒฝ [JDBC] ์‚ฌ์šฉ๋ฐฉ๋ฒ• JDBC : JAVA DataBase Connectivity ๊ธฐ์กด ์ž๋ฐ”์—์„œ๋Š” DB๋ฅผ ์กฐ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด์„œ JDBC API๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค. JDBC๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ข…๋ฅ˜์— ์ƒ๊ด€์—†์ด JDBC๋งŒ ์•Œ๋ฉด ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ..

    lotuus.tistory.com

     

    JDBC์—์„œ MyBatis๋กœ ๋„˜์–ด์˜ค๋ฉด์„œ ์ผ์ •๋ถ€๋ถ„ ํŽธ๋ฆฌํ•ด์กŒ์ง€๋งŒ ์•„์ง๋„ ๋ถˆํŽธํ•จ์€ ์กด์žฌํ–ˆ๋‹ค

     

     

    1. ๊ฐ์ฒด๋งˆ๋‹ค ๋ฐ˜๋ณต๋˜๋Š” CRUD, ๋งตํ•‘์ฝ”๋“œ ์ž‘์„ฑ

    @Mapper
    @Repository
    public interface MemberRepository {
    
        @Insert("insert into member(name, age) values(#{name}, #{age})")
        @Options(useGeneratedKeys = true, keyProperty = "id")
        void insertAnno(Member member);
    
        @Select("select * from member where id=#{id}")
        Member selectAnno(Long id);
    }

    ๊ฐ์ฒด๋งˆ๋‹ค insert, select, update, delete.... ์ง€๋ฃจํ•˜๊ฒŒ ๋ฐ˜๋ณต๋˜๋Š” CRUD์ฝ”๋“œ๋“ค์„ ์ž‘์„ฑํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค

    DB์— ์ €์žฅํ• ๋•Œ๋„ ๊ฐ์ฒด์—์„œ SQL๋ฌธ์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๊ณ , DB์— ๊ฐ’์„ ๋ฐ›์•„์˜ฌ๋•Œ๋„ SQL๋ฌธ์—์„œ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๊ณ ,,

    ๊ทธ๋‚˜๋งˆ MyBatis์—์„œ๋Š” ๊ฐ„ํŽธํ•ด์ง€๊ธดํ–ˆ์ง€๋งŒ ๊ฐ์ฒด๋งˆ๋‹ค ๋น„์Šทํ•œ SQL๋ฌธ์„ "์ง์ ‘" ๋ฐ˜๋ณตํ•ด์„œ ์ž‘์„ฑํ•ด์ฃผ๊ณ  ๋งตํ•‘ํ•ด์•ผํ•œ๋‹ค.

     

     

    2. ์—”ํ‹ฐํ‹ฐ ์‹ ๋ขฐ ๋ฌธ์ œ

    public void process(String id){
        Member member = memberRepository.find(id);
        member.getTeam();			//Team์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์„๊นŒ?
        member.getOrder().getDelivery();	//Delivery๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์„๊นŒ?
    }

    member.getTeam();
     > ์ด๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋ ค๋ฉด find๋ฉ”์†Œ๋“œ๊ฐ€ member์™€ team์„ joinํ•ด์„œ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•ด์•ผํ•œ๋‹ค.
        
    member.getOrder().getDelivery();
     > ์ด๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋ ค๋ฉด find๋ฉ”์†Œ๋“œ๊ฐ€ member, order, delivery๋ฅผ ๋ชจ๋‘ joinํ•ด์„œ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•ด์•ผํ•œ๋‹ค.
     > select๋ฌธ์˜ join์— ๋”ฐ๋ผ ํƒ์ƒ‰๋ฒ”์œ„๊ฐ€ ๊ฒฐ์ •๋˜์–ด๋ฒ„๋ฆฐ๋‹ค. 

     

     

    3. ๊ฐ์ฒด์™€ RDB์˜ ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ถˆ์ผ์น˜

    public class Member{
    	private Long id;
    	private Long teamId;
    	private String username;
    }
    
    private class Team{
    	private Long id;
    	private String name;
    }

     

    create table member(
    	member_id bigint primary key,
    	team_id bigint,
    	username varchar,
        foreign key (team_id) references team(id)
    );
    
    create table team(
    	team_id bigint primary key,
    	name varchar
    );

    ๊ฐ์ฒด๋ฅผ SQL๋กœ ๋ณ€ํ™˜ ์‹œ ํŽธ๋ฆฌํ•จ์„ ์œ„ํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ๋ฅผ ๊ฐ์ฒด์— ๊ทธ๋Œ€๋กœ ๋ฐ˜์˜ํ–ˆ๋‹ค.

    ๊ฐ์ฒด์— ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ์žˆ๋Š”๊ฐ€? NO

    ํ…Œ์ด๋ธ”์— ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ์žˆ๋Š”๊ฐ€? YES

    ๊ฐ์ฒด๋ฅผ ํ…Œ์ด๋ธ”์— ๋งž์ถ”์–ด ๋ชจ๋ธ๋งํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ์ฒด ๊ฐ„์˜ ์—ฐ๊ด€๊ด€๊ณ„๋Š” ์žˆ์„์ˆ˜๊ฐ€ ์—†๋‹ค!

     

    ๊ฐ์ฒด์ง€ํ–ฅ ๊ด€์ ์—์„œ teamId๋Œ€์‹  Team์„ ๋„ฃ์œผ๋ฉด ์–ด๋–จ๊นŒ? SQL์—์„œ Team์„ joinํ•ด์„œ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ด ๋ณด์žฅ๋˜์–ด์•ผํ•œ๋‹ค.

    joinํ•ด์„œ ๊ฐ€์ ธ์™”๋Š”๋ฐ Team๋‚ด์šฉ์ด ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด?? Member๋งŒ ๊ฐ€์ ธ์˜ค๋Š” ์ฟผ๋ฆฌ๋ฌธ์„ ์จ์„œ Team์€ null๋กœ ๋†”์•ผํ•˜๋‚˜??? ๐Ÿค”๐Ÿค”

     

    ์ด๋Ÿฐ ๊ณ ๋ฏผ์—์„œ ๋‚˜์˜จ ๊ฒƒ์ด ORM ๊ธฐ์ˆ ์ด๋‹ค

     

     

     

    [ORM] Object-Relational Mapping : ๊ฐ์ฒด-๊ด€๊ณ„ ๋งตํ•‘

    ๊ฐ์ฒด๋Š” ๊ฐ์ฒด๋Œ€๋กœ ์„ค๊ณ„ํ•˜๊ณ , DB๋Š” DB๋Œ€๋กœ ์„ค๊ณ„ํ•˜์ž 

    ๋„ˆ๋„ค ์ค‘๊ฐ„์— ๋‚ด๊ฐ€(=ORM) SQL๋ฌธ ์•Œ์•„์„œ ์ž‘์„ฑํ•˜๊ณ  ๊ฐ์ฒด๋กœ ๋งตํ•‘ํ•ด์ค„๊ฒŒ!!!

    ๋ฐ์ดํ„ฐ ์‚ฝ์ž… ์‹œ
     > Entity ๋ถ„์„
     > Insert SQL ์ƒ์„ฑ
     > JDBC API ์‚ฌ์šฉ
     > ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ถˆ์ผ์น˜ ํ•ด๊ฒฐ
    ๋ฐ์ดํ„ฐ ์กฐํšŒ ์‹œ
     > Select SQL ์ƒ์„ฑ
     > JDBC API ์‚ฌ์šฉ
     > ResultSet ๋งคํ•‘
     > ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ถˆ์ผ์น˜ ํ•ด๊ฒฐ

     

     

     

    [JPA] Java Persistence API : ์ž๋ฐ”์˜ ORM ๊ธฐ์ˆ  ํ‘œ์ค€

    ์ž๋ฐ” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ↔ [JPA] ↔ JDBC API ↔ DB

    JPA๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ JDBC ์‚ฌ์ด์—์„œ ๋™์ž‘ํ•œ๋‹ค.

     

    Member ํ…Œ์ด๋ธ”์— ์ปฌ๋Ÿผ์ด ์ถ”๊ฐ€๋˜๋ฉด??

     - ๊ธฐ์กด : ๋ชจ๋“  SQL๋ฌธ์„ ์ฐพ์•„์„œ ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค.

     - JPA : JPA๊ฐ€ SQL๋ฌธ์„ ์•Œ์•„์„œ ์ž‘์„ฑํ•ด์ฃผ๊ธฐ๋•Œ๋ฌธ์—, Member ํด๋ž˜์Šค์— ํ•„๋“œ๋งŒ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด๋œ๋‹ค.

     => ์œ ์ง€๋ณด์ˆ˜์„ฑ ์ฆ๊ฐ€

     

    ๊ฐ™์€ Member๋ฅผ ๊ฐ€์ง€๊ณ  ์˜จ๋‹ค๋ฉด??

    Long memberId = 100L;
    Member member1 = repository.find(Member.class, memberId);
    Member member2 = repository.find(Member.class, memberId);

     - ๊ธฐ์กด : member1 != member2

     - JPA : member1 == member2

    => ๋™์ผํ•œ ํŠธ๋žœ์žญ์…˜์—์„œ ์กฐํšŒํ•œ ์—”ํ‹ฐํ‹ฐ๋Š” ๊ฐ™์Œ์„ ๋ณด์žฅํ•ด์ค€๋‹ค.

     

    JPA์˜ ์„ฑ๋Šฅ ์ตœ์ ํ™”

    1์ฐจ ์บ์‹œ์™€ ๋™์ผ์„ฑ(identity) ๋ณด์žฅ ์œ„ ์˜ˆ์‹œ์ฒ˜๋Ÿผ ๊ฐ™์€ 100๋ฒˆ์˜ Member๋ฅผ ์กฐํšŒํ•  ๋•Œ,
    1๋ฒˆ์งธ find๋Š” DB์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ณ ,
    2๋ฒˆ์งธ find๋Š” ์บ์‹œ์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์˜จ๋‹ค. => ์กฐํšŒ ์„ฑ๋Šฅ ํ–ฅ์ƒ
    ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•˜๋Š” ์“ฐ๊ธฐ ์ง€์—ฐ
    (transactional write-behind)
    ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ• ๋•Œ๊นŒ์ง€๋Š” insert SQL๋ฌธ๋“ค์„ ๋ชจ์•„๋†“๋Š”๋‹ค
    JDBC BATCH SQL ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด์„œ ํ•œ๋ฒˆ์— SQL๋ฌธ์„ ์ „์†กํ•œ๋‹ค
    ์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading) ์•„๋ž˜ ์ฐธ๊ณ 
    //์ง€์—ฐ๋กœ๋”ฉ : ๊ฐ์ฒด๊ฐ€ ์‹ค์ œ ์‚ฌ์šฉ๋  ๋•Œ ๋กœ๋”ฉ
    Member member = repository.find(memberId);		// select * from member;
    Team team = member.getTeam();
    String teamname = team.getName();			// select * from team;
    
    //์ฆ‰์‹œ๋กœ๋”ฉ : JOIN์„ ์‚ฌ์šฉํ•˜์—ฌ ์—ฐ๊ด€๋œ ๊ฐ์ฒด๊นŒ์ง€ ๋ฏธ๋ฆฌ ์กฐํšŒ
    Member member = repository.find(memberId);		// select member.*, team.* from member join team..
    Team team = member.getTeam();
    String teamname = team.getName();

     

     

     

    JPA ํ™˜๊ฒฝ์„ค์ •

    maven ์‚ฌ์šฉ ์‹œ

    pom.xml์— ๋””ํŽœ๋˜์‹œ ์ถ”๊ฐ€

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>5.6.4.Final</version>
    </dependency>

     

    resources/META-INF/persistence.xml ์ž‘์„ฑ

    <?xml version="1.0" encoding="UTF-8"?>
    <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.2">
        <persistence-unit name="persistenceConfig">
            <properties>
            
                <!-- datasource ์†์„ฑ -->
                <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
                <property name="javax.persistence.jdbc.user" value="sa"/>
                <property name="javax.persistence.jdbc.password" value=""/>
                <property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
                <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
                
                <!-- ์˜ต์…˜ -->
                <!-- SQL๋ฌธ ๋กœ๊ทธ ์ถœ๋ ฅ -->
                <property name="hibernate.show_sql" value="true"/>
                <property name="hibernate.format_sql" value="true"/>
                <property name="hibernate.use_sql_comments" value="true"/>
                
                <!-- JPA ํ‘œ์ค€์— ๋งž์ถ˜ ์ƒˆ๋กœ์šด ํ‚ค ์ƒ์„ฑ ์ „๋žต ์‚ฌ์šฉ -->
                <property name="hibernate.id.new_generator_mappings" value="true"/>
                
                <!-- ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์‹œ์ ์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ” ์ž๋™ ์ƒ์„ฑ -->
                <!-- <property name="hibernate.hbm2ddl.auto" value="create"/> -->
                
            </properties>
        </persistence-unit>
    </persistence>

     

     

     

    gradle ์‚ฌ์šฉ ์‹œ

    build.gradle์— ๋””ํŽœ๋˜์‹œ ์ถ”๊ฐ€

    implementation 'org.hibernate:hibernate-entitymanager:5.6.4.Final'

     

    resources/application.properties์— ๋‚ด์šฉ ์ž‘์„ฑ

    spring.datasource.driverClassName=org.h2.Driver
    spring.datasource.username=va
    spring.datasource.password=
    spring.datasource.url=jdbc:h2:tcp://localhost/~/test
    
    spring.jpa.database-platform=org.hibernate.dialect.H2Dialect  
    spring.jpa.show-sql=true
    spring.jpa.properties.hibernate.format_sql=true
    spring.jpa.hibernate.ddl-auto=create

     

     * hibernate.hbm2ddl.auto : DDL์„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์‹œ์ ์— ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ด์ค€๋‹ค.

     - create : ๊ธฐ์กด ํ…Œ์ด๋ธ” ์‚ญ์ œ ํ›„ ๋‹ค์‹œ ์ƒ์„ฑ

     - create-drop : create์™€ ๊ฐ™์œผ๋‚˜ ์ข…๋ฃŒ์‹œ์ ์— ํ…Œ์ด๋ธ” drop

     - update : ๋ณ€๊ฒฝ๋ถ„๋งŒ ๋ฐ˜์˜ํ•œ๋‹ค

     - validate : ์—”ํ‹ฐํ‹ฐ์™€ ํ…Œ์ด๋ธ”์ด ์ •์ƒ ๋งคํ•‘๋˜์—ˆ๋Š”์ง€๋งŒ ํ™•์ธํ•œ๋‹ค

     - none : ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

     

    ์‹ค์ œ ์„œ๋ฒ„์—์„œ๋Š” ๋ฐ˜๋“œ์‹œ validate, none๋งŒ ์‚ฌ์šฉํ•˜๋„๋ก ํ•œ๋‹ค.

     

     

     

    EntityManager

    JPA์˜ persistenceํด๋ž˜์Šค๋Š” persistence.xml ๋˜๋Š” application.properties ์„ค์ •์ •๋ณด๋ฅผ ์กฐํšŒํ•œ ๋‹ค์Œ,

    EntityManagerFactory๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

     

    EntityManagerFactory๋Š” EntityManager๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๊ฐ์ฒด์ด๋‹ค.

    EntityManager๋Š” ํŠธ๋žœ์žญ์…˜์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ํ•ญ์ƒ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๋Š” ๊ฐ์ฒด์ด๋‹ค

     

    ์ฃผ์˜!

    EntityManagerFactory๋Š” DB์™€ ์—ฐ๊ฒฐํ•˜๊ธฐ๋•Œ๋ฌธ์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์— ํ•˜๋‚˜๋งŒ ์ƒ์„ฑํ•ด์„œ ๊ณต์œ ํ•ด์•ผํ•œ๋‹ค.

    EntityManager๋Š” ์“ฐ๋ ˆ๋“œ๊ฐ„์— ๊ณต์œ ํ•˜์ง€ ์•Š๊ณ  ์‚ฌ์šฉ ํ›„ ๋ฒ„๋ ค์•ผํ•œ๋‹ค.

    jpa์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์€ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ์‹คํ–‰๋˜์–ด์•ผํ•œ๋‹ค.

    public static void main(String args[]){
    
        //EntityFactory ์ƒ์„ฑ
        //์„ค์ •์ •๋ณด persistence-unit ํƒœ๊ทธ์˜ name๋ช…์ž…๋ ฅ
        EntityManagerFactory emf = 
            Persistence.createEntityManagerFactory("persistenceConfig")
            
        //EntityManager ์ƒ์„ฑ
        EntityManager em = emf.createEntityManager();
    
        //EntityTransaction ์ƒ์„ฑ
        EntityTransaction tx = em.getTransaction();
    
        //ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘
        tx.begin();
    
        try{
            Member member = new Member(1, "hello");
            em.persist(member);
        } catch(Exception e){
            tx.rollback();
        }
    
        em.close();
        emf.close();
    
    }

     

    JPA์˜ CRUD ๋ฉ”์„œ๋“œ

      ๊ฐœ๋ฐœ์ž ์ž‘์„ฑ JPA ์ฒ˜๋ฆฌ
    ์ €์žฅ member.setTeam(team);
    jpa.persistent(member);
    insert into team ...
    insert into member ...
    ์กฐํšŒ Member member = jpa.find(Member.class, memberId);
    member.getTeam();    //๊ฐ€๋Šฅ
    select member.*, team.*
    from member join team ...
    ์ˆ˜์ • member.setName("๋ณ€๊ฒฝํ•  ์ด๋ฆ„");  
    ์‚ญ์ œ jpa.remove(member);  

     

     

     

    ํ•„๋“œ์™€ ์ปฌ๋Ÿผ ๋งคํ•‘

    @Entity								//jpa๊ฐ€ ๊ด€๋ฆฌํ•  ๊ฐ์ฒด
    public class Member{
        @Id								//DB์˜ PK์™€ ๋งคํ•‘ํ•  ํ•„๋“œ
        @GeneratedValue(strategy = GenerationType.AUTO)		//์ž๋™ ์ฆ๊ฐ€ํ•˜๋Š” ํ•„๋“œ์˜ ๊ฐ’์„ ์ฑ„์›Œ์ค€๋‹ค.
        private Long id;
        
        @Column(name = "USERNAME")		//DB์˜ ์ปฌ๋Ÿผ๋ช…๊ณผ ๊ฐ์ฒด์˜ ํ•„๋“œ๋ช…์ด ๋‹ค๋ฅผ๋•Œ ๋งคํ•‘ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.
        private String name;
        
        private int age;				
        
        @Temporal(TemporalType.TIMESTAMP)	//์‹œ๊ฐ„ ํƒ€์ž…์„ ๋งคํ•‘ํ•œ๋‹ค
        private Date regDate;
        
        @Enumerated(EnumType.STRING)	//Enumํƒ€์ž…์„ ๋งคํ•‘ํ•œ๋‹ค
        private MemberType memberType;
        
        @Lob				//CLOB, BLOB์„ ๋งคํ•‘ํ•œ๋‹ค
        private String blob;
    }

    ์–ด๋…ธํ…Œ์ด์…˜์€ javax.persistence์— ์žˆ๋Š” ๊ฒƒ์„ ์‚ฌ์šฉํ•˜์ž

     

    @GeneratedValue

     - GenerationType.IDENTITY : MySQL์ผ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

     - GenerationType.SEQUENCE: ORACLE์ผ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

     - GenerationType.TABLE : ๋ชจ๋“  DB์— ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค. ํ‚ค ์ƒ์„ฑ์šฉ ํ…Œ์ด๋ธ”์ผ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

     - GenerationType.AUTO: ๋ฐฉ์–ธ์— ๋”ฐ๋ผ ์ž๋™ ์ง€์ •ํ•œ๋‹ค (๊ธฐ๋ณธ๊ฐ’)

     

    @Enumerated ์˜ ๊ธฐ๋ณธ๊ฐ’์€ EnumType.ORDINAL์ด๋‹ค.

    ์ด๊ฒƒ์€ Enum์˜ index๊ฐ’์œผ๋กœ ๋งคํ•‘ํ•˜๊ธฐ๋•Œ๋ฌธ์— ์ค‘๊ฐ„์— enum์ด ์ถ”๊ฐ€๋˜๋ฉด ์ˆซ์ž๊ฐ€ ๋‹ค ๊ผฌ์—ฌ๋ฒ„๋ฆฐ๋‹ค.

    ๋ฐ˜๋“œ์‹œ String์œผ๋กœ ์„ค์ •ํ•ด์„œ enum์˜ ๊ธ€์ž๋ฅผ ๋„ฃ์–ด์ฃผ์ž.

     

     

     

    ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘

    ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘ A → B

    @Entity
    public class Member{
    	@Id @GeneratedValue
    	private Long id;
        
    	@ManyToOne
    	@JoinColumn(name = "TEAM_ID")
    	private Team team;
        
    	@Column(name = "USERNAME")
    	private String username;
    }
    
    @Entity
    private class Team{
    	@Id @GeneratedValue
    	private Long id;
        
    	@Column(name = "NAME")
    	private String name;
    }

    Member์™€ Team์˜ ๊ด€๊ณ„๊ฐ€ N : 1 ์ด๋ฏ€๋กœ Member์˜ team์— @ManyToOne์„ ์ž‘์„ฑํ•œ๋‹ค.

    Team ํ…Œ์ด๋ธ”์˜ TEAM_ID ์ปฌ๋Ÿผ๊ณผ ์—ฐ๊ฒฐ๋  ๊ฒƒ์ด๋‹ˆ @JoinColumn์— TEAM_ID๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.

     

    ์‹ค์ œ๋กœ Member๋ฅผ ์กฐํšŒํ•˜๋ฉด JPA๊ฐ€ DB์— "select ~ from member left outer join team ~" SQL๋ฌธ์žฅ์„ ์ „์†กํ•œ๋‹ค.

     

    @ManyToOne(fetch = FetchType.LAZY) ๋กœ ์„ค์ •ํ•˜๋ฉด

    Member ์กฐํšŒ ์‹œ JPA๊ฐ€ Member์— ๋Œ€ํ•œ ๋‚ด์šฉ๋งŒ selectํ•ด์„œ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

    ์ดํ›„ Team๊ฐ์ฒด๋ฅผ ์กฐํšŒํ•˜๋ฉด ๊ทธ ์ˆœ๊ฐ„์— JPA๊ฐ€ Team์— ๋Œ€ํ•ด์„œ๋งŒ select๋ฌธ์„ ์ „์†กํ•œ๋‹ค.

    //์ง€์—ฐ๋กœ๋”ฉ : ๊ฐ์ฒด๊ฐ€ ์‹ค์ œ ์‚ฌ์šฉ๋  ๋•Œ ๋กœ๋”ฉ
    Member member = repository.find(memberId);		// select * from member;
    Team team = member.getTeam();
    String teamname = team.getName();			// select * from team;

     

     

     

    ์–‘๋ฐฉํ–ฅ ๋งคํ•‘ A ↔ B

    @Entity
    public class Member{
    	@Id @GeneratedValue
    	private Long id;
        
    	@ManyToOne
    	@JoinColumn(name = "TEAM_ID")
    	private Team team;
        
    	@Column(name = "USERNAME")
    	private String username;
    }
    
    @Entity
    private class Team{
    	@Id @GeneratedValue
    	private Long id;
        
    	@Column(name = "NAME")
    	private String name;
        
    	@OneToMany(mappedBy = "team")	//โ˜…โ˜…โ˜…โ˜…โ˜…
    	private List<Member> members = new ArrayList<Member>();
    }

    ํ…Œ์ด๋ธ”์€ ์—ฐ๊ฒฐ๊ด€๊ณ„์— ๋ฐฉํ–ฅ์„ฑ์ด ์—†๊ธฐ๋•Œ๋ฌธ์— ๊ทธ ์ž์ฒด๋กœ ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๊ฐ€ ์„ฑ๋ฆฝ๋œ๋‹ค. Member ↔ Team

    ๊ฐ์ฒด์˜ ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋Š” Member → Team 1๊ฐœ, Member ← Team 1๊ฐœ = ๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ฐ„๊ด€๊ณ„ 2๊ฐœ๊ฐ€ ์—ฐ๊ฒฐ๋œ ๊ฒƒ์ด๋‹ค.

     

    ๊ทธ๋ ‡๋‹ค๋ฉด Member์˜ team ๊ฐ’์„ ์ˆ˜์ •ํ•œ ํ›„์— Team์˜ members๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด..?

    ๋‘˜๋‹ค ์—…๋ฐ์ดํŠธ๊ฐ€ ๋˜์–ด์•ผํ•˜๋Š”๋ฐ ๊ทธ๋Ÿผ ๋ฐ์ดํ„ฐ์˜ ์‹ ๋ขฐ์„ฑ์ด ๋–จ์–ด์ง€๊ฒŒ๋œ๋‹ค

     

    ๊ทธ๋ž˜์„œ ๋‘˜ ์ค‘ ํ•˜๋‚˜๋กœ ์™ธ๋ž˜ํ‚ค๋ฅผ ๊ด€๋ฆฌํ•˜์ž! ํ•˜๊ณ  ๊ทœ์น™์ด ์ƒ๊ธฐ๊ฒŒ ๋œ๋‹ค

    ์–‘๋ฐฉํ–ฅ ๋งคํ•‘ ๊ทœ์น™
     - ๊ฐ์ฒด์˜ ๋‘ ๊ด€๊ณ„ ์ค‘ ํ•˜๋‚˜๋ฅผ ์—ฐ๊ฐ„๊ด€๊ณ„์˜ ์ฃผ์ธ์œผ๋กœ ์ง€์ •
     - ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ๋งŒ์ด ์™ธ๋ž˜ํ‚ค๋ฅผ ๊ด€๋ฆฌ(๋“ฑ๋ก, ์ˆ˜์ •)
     - ์ฃผ์ธ์ด ์•„๋‹Œ ์ชฝ์€ ์ฝ๊ธฐ๋งŒ ๊ฐ€๋Šฅ
     - ๊ฐ์ฒด ๋‘˜ ๋‹ค ์ˆ˜์ •ํ•˜๋ฉด ์ฃผ์ธ์ชฝ ๋ฐ์ดํ„ฐ๋งŒ ์ˆ˜์ •๋จ.
     - ์ฃผ์ธ์€ @JoinColumn์„ ์ž‘์„ฑํ•˜๊ณ , ์ฃผ์ธ์ด ์•„๋‹Œ ์ชฝ์€ mappedBy ์†์„ฑ์„ ์ž‘์„ฑํ•œ๋‹ค.

    ๋ˆ„๊ตฌ๋ฅผ ์ฃผ์ธ์œผ๋กœ?
     - ์™ธ๋ž˜ํ‚ค๊ฐ€ ์žˆ๋Š” ๊ณณ์„ ์ฃผ์ธ์œผ๋กœ! 
       (๊ธฐ์กด ์ฝ”๋“œ์—์„œ๋Š” Member์•ˆ์— Long teamId ๊ฐ€ ์กด์žฌํ–ˆ์—ˆ๋‹ค.)

    ์ฃผ์˜!!!

     

     

    JPA์˜ ๋‚ด๋ถ€๊ตฌ์กฐ

    ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ : ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜๊ตฌ ์ €์žฅํ•˜๋Š” ํ™˜๊ฒฝ

    ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” EntityManager๋ฅผ ํ†ตํ•ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

     

    ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด

     - 1์ฐจ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ 

     - ๋™์ผ์„ฑ์„ ๋ณด์žฅ๋ฐ›๊ณ 

     - ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•˜๋Š” ์“ฐ๊ธฐ ์ง€์—ฐ์ด ๊ฐ€๋Šฅํ•˜๊ณ 

     - ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๊ณ (Dirty Checking)

     - ์ง€์—ฐ์„ ๋กœ๋”ฉํ•  ์ˆ˜ ์žˆ๋‹ค(Lazy Loading)

     

    ์—”ํ‹ฐํ‹ฐ์˜ ์ƒ๋ช…์ฃผ๊ธฐ

     - ๋น„์˜์†(new/transient)

       : ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ์ „ํ˜€ ๊ด€๊ณ„๊ฐ€ ์—†๋Š” ์ƒํƒœ

       : Member member = new Member(); 

        

     - ์˜์†(managed)

       : ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ๋˜์–ด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์ƒํƒœ

       : em.persist(member); 

     

     - ์ค€์˜์†(detached)

       : ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ๋˜์—ˆ๋‹ค๊ฐ€ ๋ถ„๋ฆฌ๋œ ์ƒํƒœ

       : em.detach(member); 

     

     - ์‚ญ์ œ(removed)

       : ์‚ญ์ œ๋œ ์ƒํƒœ

       : em.remove(member);  ๊ฐ์ฒด๋ฅผ DB์—์„œ ์‚ญ์ œํ•œ ์ƒํƒœ

     

     

     

    JPA ๋™์ž‘๋ฐฉ์‹

    ์กฐํšŒ

    Member member = new Member();
    member.setId("member1");
    member.setUsername("ํšŒ์›1");
    
    em.persist(member); 
    //์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ 1์ฐจ ์บ์‹œ์— key๋Š” member1๋กœ, value๋Š” Entity์ž์ฒด๊ฐ€ ์ €์žฅ๋œ๋‹ค
    //(key๋Š” @Id๊ฐ€ ๋ถ™์€ ๊ฐ’์ด ๋œ๋‹ค)
    
    Member findM1 = em.find(Member.class, "member1")
    //1์ฐจ ์บ์‹œ์—์„œ ์กฐํšŒ
    
    Member findM2 = em.find(Member.class, "member2");
    //1์ฐจ ์บ์‹œ์— ์—†์œผ๋ฉด DB์—์„œ ์กฐํšŒ
    //DB์กฐํšŒ ๊ฒฐ๊ณผ๊ฐ’์„ 1์ฐจ ์บ์‹œ์— ์ €์žฅํ•œ ํ›„, findM2์— ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์ค€๋‹ค

     

    ์ž‘์„ฑ

    Member member1 = new Member("Lisa");
    Member member2 = new Member("Happy");
    
    em.persist(member1);
    em.persist(member2);
    // member1, member2 1์ฐจ ์บ์‹œ์— ์ €์žฅ
    // ํ•ด๋‹น insert๋ฌธ 2๊ฐœ๋ฅผ ์“ฐ๊ธฐ์ง€์—ฐSQL์ €์žฅ์†Œ์— ์ €์žฅํ•ด๋‘”๋‹ค
    
    tx.commit();
    // insert๋ฌธ 2๊ฐœ๋ฅผ DB์— ์ „์†กํ•œ๋‹ค (flush)
    // DB์— commit์„ ์‹คํ–‰ํ•œ๋‹ค

     

    ์ˆ˜์ •

    Member insertMember = new Member();
    insertMember.setUsername("stark");
    member.setAge(20);
    
    em.persist(insertMember);
    // ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ 1์ฐจ ์บ์‹œ์— ์ €์žฅ๋œ๋‹ค.
    // ์ด๋•Œ, insertMember์˜ ์Šค๋ƒ…์ƒท์„ ์ฐ์–ด์„œ ๋ณด๊ด€ํ•œ๋‹ค.
    
    Member member = em.find(Member.class, "stark");
    // insertMember == member
    
    member.setUsername("tony");
    member.setAge(10);
    
    tx.commit();
    // ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ insertMember์˜ ์Šค๋ƒ…์ƒท๊ณผ ๋ณ€๊ฒฝ๋œ member๋ฅผ ๋น„๊ตํ•ด์„œ
    // ์ž๋™์œผ๋กœ update๋ฌธ์„ DB์— ์ „์†กํ•œ๋‹ค.

    ์™œ ์ด๋ ‡๊ฒŒ ๋™์ž‘ํ• ๊นŒ? ์ž๋ฐ”์˜ List๋ฅผ ์ƒ๊ฐํ•ด๋ณด์ž.

    List<Member> ์—์„œ Member๋ฅผ ํ•˜๋‚˜ ๊บผ๋‚ธ ํ›„ ๊ฐ’์„ ์ˆ˜์ •ํ•˜๊ณ  ๋‹ค์‹œ List์— ๋‹ด๋Š”๊ฐ€?? NO

     => ๊ฐ์ฒด์ง€ํ–ฅ์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๊ฒŒ ํ•˜๊ธฐ์œ„ํ•ด์„œ em.update() ์ด๋Ÿฐ ๋ฉ”์†Œ๋“œ๊ฐ€ ์—†๋Š” ๊ฒƒ์ด๋‹ค!

     

    ์‚ญ์ œ

    Member member = em.find(Member.class, "memberA");
    
    em.remove(member);
    
    tx.commit();
    //์ปค๋ฐ‹ ์‹œ์ ์— delete๋ฌธ DB์— ์ „๋‹ฌ๋œ๋‹ค

     

    ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ”Œ๋Ÿฌ์‹œ ํ•˜๋Š” ๋ฐฉ๋ฒ•

    flush : SQL๋ฌธ์„ DB์— ์ „์†กํ•ด์„œ ์‹ค์ œ๋กœ ์ ์šฉํ•˜๋Š” ๊ฒƒ = ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋™๊ธฐํ™”ํ•˜๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ด๋‹ค.

     - em.flush() : ์ง์ ‘ ํ˜ธ์ถœ

     - transaction.commit() : flush ์ž๋™ํ˜ธ์ถœ

     - JPQL ์ฟผ๋ฆฌ ์‹คํ–‰ : flush ์ž๋™ํ˜ธ์ถœ

     

     

    ์ง€์—ฐ๋กœ๋”ฉ LAZY๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ”„๋ก์‹œ๋กœ ์กฐํšŒ

    ์ฃผ์˜!! ์ง€์—ฐ๋กœ๋”ฉํ•˜๋ ค๋ฉด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ํ™œ์„ฑํ™”๋˜์–ด์žˆ์–ด์•ผํ•œ๋‹ค!!

    Member member = em.find(Member.class, 1L);
    //์ด๋•Œ Member์˜ Team์—๋Š” ํ”„๋ก์‹œ๊ฐ์ฒด(๊ฐ€์งœ๊ฐ์ฒด)๊ฐ€ ์ฑ„์›Œ์ ธ์žˆ๋‹ค.
    
    Team team = member.getTeam();
    //์•„์ง Team์€ ํ”„๋ก์‹œ๊ฐ์ฒด
    
    team.getName();
    //์‹ค์ œ team์„ ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์— DB์กฐํšŒํ•˜์—ฌ Team์ด ์ฑ„์›Œ์ง„๋‹ค

     

     

    JPA์™€ ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ

    JPQL : SQL์„ ์ถ”์ƒํ™”ํ•˜์—ฌ ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœํ•˜๋Š” ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ ์–ธ์–ด

    SQL๊ณผ ๋ฌธ๋ฒ• ์œ ์‚ฌํ•˜๋‹ค. 

    JPQL์€ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๊ณ , SQL์€ ํ…Œ์ด๋ธ”์„ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

    from ๋’ค์—๋Š” ํ…Œ์ด๋ธ” ์ด๋ฆ„์ด ์•„๋‹Œ ์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•œ๋‹ค

    ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•œ๋‹ค

    ๋ณ„์นญ์ด ํ•„์ˆ˜์ด๋‹ค(m)

    String jpql = "select m from Member m where m.name like '%hello%'";
    // String jpql = "select m from Member m where m.age > 18";
    
    
    List<Member> result = em.createQuery(jpql, Member.class).getResultList();
    // getResultList() : ๊ฒฐ๊ณผ๊ฐ€ ๋ฆฌ์ŠคํŠธ๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค.
    
    Member result = em.createQuery(jpql, Member.class).getSingleResult();
    // getSingleResult() : ๊ฒฐ๊ณผ๊ฐ€ ์ •ํ™•ํžˆ ํ•˜๋‚˜ ๋ฐ˜ํ™˜๋œ๋‹ค. (ํ•˜๋‚˜๊ฐ€ ์•„๋‹ˆ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒ๋œ๋‹ค)
    
    
    String jpql = "select m from Member m where m.username =: username";
    query.setParameter("username", usernameParam);
    // ์ฟผ๋ฆฌ์— ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์ž…๋ ฅ๋œ๋‹ค
    
    
    String jpql = "select m.username from Member m";
    String username = em.createQuery(jpql, String.class);
    // member์˜ ํ•„๋“œ์— ํ•ด๋‹น๋˜๋Š” ๊ฐ’๋งŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
    
    
    String jpql = "select new com.example.dto.UserDTO(m.username, m.age) from Member m";
    UserDTO user = em.createQuery(jpql, UserDTO.class);
    // ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ new ์—ฐ์‚ฐ์ž๋ฅผ ํ†ตํ•ด DTO๋กœ ๋ฐ”๋กœ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.
    
    
    String paging = "select m from Member m order by m.name desc";
    List<Member> result = em.createQuery(jpql, Member.class)
                            .setFirstResult(int startPosition)	//์กฐํšŒ ์‹œ์ž‘ ์œ„์น˜(0๋ถ€ํ„ฐ)
                            .setMaxResults(int maxResult)		//์กฐํšŒํ•  ๋ฐ์ดํ„ฐ ์ˆ˜ (N๊ฐœ ๊ฐ€์ ธ์™€)
                            .getResultList();
    // ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
    
    
    String innerJoin = "select m from Member m join m.team t";
    String outerJoin = "select m from Member m left [outer] join m.team t";
    // join
    
    
    String fetchJoin = "select m from Member m join fetch m.team";
    // fetch join : ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๋ฅผ ํ•œ๋ฒˆ์— ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.
    // ๋ณ„์นญ ์‚ฌ์šฉ ๋ถˆ๊ฐ€
    // ๋ชจ๋‘ Lazy ๋กœ๋”ฉ์ผ๋•Œ ์ด๋•Œ๋งŒํผ์€ ๋ฌด์กฐ๊ฑด joinํ•ด์„œ ๊ฐ™์ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์•ผํ•œ๋‹ค! ํ• ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
    // SQL๋ฌธ์œผ๋กœ๋Š” select m.*, t.* from Member m inner join team t on m.team_id = t.id

     

     

     

     

     

     

     

     

     

     

     

    ์ฐธ๊ณ 

     

    ํ† ํฌON 41์ฐจ. JPA ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ๋ณธ๊ธฐ ๋‹ค์ง€๊ธฐ | T์•„์นด๋ฐ๋ฏธ

    T์•„์นด๋ฐ๋ฏธ ์˜จ๋ผ์ธ ๊ฐ•์˜- [ํ† ํฌON์„ธ๋ฏธ๋‚˜] JPA ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ๋ณธ๊ธฐ ๋‹ค์ง€๊ธฐ (์ด8๊ฐ•) https://tacademy.skplanet.com/live/player/onlineLectureDetail.action?seq=149 [๊ณผ์ • ์†Œ๊ฐœ] ๋งˆํ” ํ•œ๋ฒˆ์งธ ์„ธ๋ฏธ๋‚˜ ์ฃผ์ œ๋Š” ‘JPA(Java...

    www.youtube.com

     

    ๋ฐ˜์‘ํ˜•
    • ๋„ค์ด๋ฒ„ ๋ธ”๋Ÿฌ๊ทธ ๊ณต์œ ํ•˜๊ธฐ
    • ํŽ˜์ด์Šค๋ถ ๊ณต์œ ํ•˜๊ธฐ
    • ํŠธ์œ„ํ„ฐ ๊ณต์œ ํ•˜๊ธฐ
    • ๊ตฌ๊ธ€ ํ”Œ๋Ÿฌ์Šค ๊ณต์œ ํ•˜๊ธฐ
    • ์นด์นด์˜คํ†ก ๊ณต์œ ํ•˜๊ธฐ