반응형

추상 팩토리 패턴(Abstract Factory Pattern)

  - 추상 팩토리 패턴은 생성패턴(Creational Pattern) 중 하나이다.
  - 생성 패턴은 인스턴스를 만드는 절차를 추상화하는 패턴이다.
  - 생성 패턴에 속하는 패턴들은 객체를 생성, 합성하는 방법이나 객체의 표현방법을 시스템과 분리해준다.

  - 생성 패턴을 이용하면 무엇이 생성되고, 누가 이것을 생성하며, 이것이 어떻게 생성되는지, 언제 생성할 것인지 결정하는 데 유연성을 화      보할 수 있습니다.

 

장점

  - 추상 팩토리 패턴은 클라이언트 코드로부터 서브 클래스의 인스턴스화를 제거하여 서로 간의 종속성을 낮추고, 결합도를 느슨하게 하며(Loosely Coupled), 확장을 쉽게 한다.

  - 추상 팩토리 패턴은 클라이언트와 구현 객체들 사이에 추상화를 제공한다.

  - 추상 팩토리 패턴에서는 팩토리 클래스에서 서브 클래스를 생성하는 데에 있어서 필요한 if-else, switch 문을 걷어냅니다.

 

팩토리 메소드 패턴 vs 추상 팩토리 패턴

- 둘 다 구체적인 객체 생성 과정을 추상화한 인터페이스를 제공한다.

 

팩토리 메소드 패턴 

추상 팩토리 패턴

관점 "팩토리를 구현하는 방법"에 초점 "팩토리를 사용하는 방법"에 초점
목적 구체적인 객체 생성 과정을 하위 또는 구체적인 클래스로 옮기는 것이 목적 관련있는 여러 객체를 구체적인 클래스에 의존하지 않고 만들 수 있게 해주는 것이 목적

 

코드

  * 부동산 중개 수수료 계산 프로그램 가정(매매/임대차)

 

  - 중개 정책 interface 생성 :

     interface를 구현하는 하위 클래스에서 수수료 계산 로직을 구현한다.

     java8 이후로 default 메소드를 사용하면 interface 내부에서 로직 작성이 가능하다. (default 명시)

public interface BrokeragePolicy {

    BrokerageRule createBrokerageRule(Long price);

     default Long calculate(Long price) {
        BrokerageRule rule = createBrokerageRule(price);
        return rule.calcMaxBrokerage(price);
    }
}

 

  - 매매 중개 수수료 class :

    java7 이후부터 _는 숫자 리너털 어디에도 사용할 수 있다. 가독성 향상

 

public class PurchaseBrokeragePolicy implements BrokeragePolicy{

    public BrokerageRule createBrokerageRule(Long price) {
        BrokerageRule rule;
        if(price < 50_000_000) {
            rule = new BrokerageRule(0.6, 250_000L);
        } else if(price < 200_000_000) {
            rule = new BrokerageRule(0.5, 800_000L);
        }else if(price < 600_000_000) {
            rule = new BrokerageRule(0.4, null);
        }else if(price < 900_000_000) {
            rule = new BrokerageRule(0.5, null);
        }else{
            rule = new BrokerageRule(0.9, null);
        }
        return rule;
    }


}

 

  - 임대차 중개 수수료 class

 

public class RentBrokeragePolicy implements BrokeragePolicy{

    public BrokerageRule createBrokerageRule(Long price) {
        BrokerageRule rule;
        if(price < 50_000_000) {
            rule = new BrokerageRule(0.5, 200_000L);
        } else if(price < 100_000_000) {
            rule = new BrokerageRule(0.4, 300_000L);
        }else if(price < 300_000_000) {
            rule = new BrokerageRule(0.3, null);
        }else if(price < 600_000_000) {
            rule = new BrokerageRule(0.4, null);
        }else{
            rule = new BrokerageRule(0.8, null);
        }
        return rule;
    }


}

 

  - 추상 팩토리 Interface Class :

     추상 팩토리 역할을 하는 인터페이스 생성

 

public interface BrokeragePolicyAbstractFactory {

    public BrokeragePolicy createBrokerageRule();

}

 

- 팩토리 Interface를 구현하는 클래스(매매 중개 수수료) :

  작성한 팩토리 인터페이스의 createBrokerageRule() 메소드의 리턴 타입이 super class인 BrokeragePolicy이다.

  이는 자바의 다형성을 잘 활용한 방식이다.

public class PurchaseBrokerageFactory implements BrokeragePolicyAbstractFactory{

    @Override
    public BrokeragePolicy createBrokerageRule() {
        return new PurchaseBrokeragePolicy();
    }
}

 

- 팩토리 Interface를 구현하는 클래스(임대차 중개 수수료)

 

public class RentBrokerageFactory implements BrokeragePolicyAbstractFactory{

    @Override
    public BrokeragePolicy createBrokerageRule() {
        return new RentBrokeragePolicy();
    }
}

 

- 서브 클래스들을 생성하기 위해서 if-else 없이 분개 처리해주는 클래스

 

public class BrokeragePolicyFactory {


    public static BrokeragePolicy getBrokeragePolicy(BrokeragePolicyAbstractFactory policyAbstractFactory) {
        return policyAbstractFactory.createBrokerageRule();
    }

}

 

- 중개 수수료 계산 api

 

  // 매매 중계수수료 계산 메소드
    @GetMapping("/api/calc/brokeragepur")
    public Long calcBrokeragePurchase(@RequestParam ActionType actionType,
                              @RequestParam Long price) {
        BrokeragePolicy policy = BrokeragePolicyFactory.getBrokeragePolicy(new PurchaseBrokerageFactory());
        return policy.calculate(price);
    }
  // 임대차 중계수수료 계산 메소드
    @GetMapping("/api/calc/brokeragerent")
    public Long calcBrokerageRent(@RequestParam ActionType actionType,
                              @RequestParam Long price) {
        BrokeragePolicy policy = BrokeragePolicyFactory.getBrokeragePolicy(new RentBrokerageFactory());
        return policy.calculate(price);
    }

 

 

실무 사용 사례

Spring에서 FactoryBean과 그 구현체

 

정리

 - 객체 생성을 담당 및 처리하는 팩토리 클래스를 생성하여서 객체 생성에 관한 확장도 쉽게 구성할 수 있다. 

    팩토리 패턴과 다르게 분개 처리하는 부분이 사라진다.

 

728x90
반응형

'JAVA > Design Pettern' 카테고리의 다른 글

[Design Pattern] 템플릿 메서드 패턴  (0) 2022.03.23
[Design Pattern] 프록시 패턴  (0) 2022.03.22
[Design Pattern] 어댑터 패턴  (0) 2022.03.16
[Design Pattern] 싱글톤 패턴  (0) 2022.03.15
[Design Pattern] 빌더 패턴  (0) 2022.03.14
반응형

어댑터 패턴(Adapter Pattern)

  - 어댑터 패턴은 구조패턴(Structural Pattern) 중 하나이다.
  - 구조 패턴은 클래스를 상속과 합성을 이용해서 더 큰 클래스를 생성하는 방법을 제공하는 패턴이다.

  - 구조 패턴을 사용하면 서로 독립적으로 개발한 클래스를 하나인 양 사용할 수 있다.

  - 기존 코드를 클라이언트가 사용하는 인터페이스의 구현체로 바꿔주는 패턴

 

장점

  - 어댑터 패턴은 연결할 수 없는 두 개의 호환되지 않는 인터페이스 사이의 커넥터 역할을 수행한다.

  - 클래스의 인터페이스를 사용자가 기대하는 인터페이스 형태로 변환시킨다.

  - 기존 코드를 변경하지 않고 원하는 인터페이스 구현체를 만들어 재사용할 수 있다.

  - 기존 코드가 하던 일과 특정 인터페이스 구현체로 변환하는 작업을 각기 다른 클래스로 분리하여 관리할 수 있다.

단점

  - 새 클래스가 생겨 복잡도가 증가할 수 있다. 경우에 따라서는 기존 코드가 해당 인터페이스를 구현하도록 수정하는 것이 좋은 선택이 될 수도 있다.

 

코드

  • 최종 변경해 줘야 하는 객체 타켓 (Target)
  • 변환의 대상이 되는 객체 어댑티 (Adaptee)
  • 변경을 도와주는 객체 어댑터 (Adapter)

 * 예시) BugattiVeyron의 속도를 시속 마일(MPH) 단위에서 킬로미터(km/h) 단위로 반환해야 하는 예제

 

BugattiVeyron.java

 

public class BugattiVeyron implements Movable{

    @Override
    public double getSpeed() {
        return 268;
    }
}

 

Movable.java

 

public interface Movable {

    double getSpeed();
}

 

MovableAdapter.java

  - Adapter interface

 

public interface MovableAdapter {

    double getSpeed();
}

 

MovableAdapterImpl.java

  - Adapter 실제 구현체 convertMPHtoKMPH 마일단위를 킬로미터로 변환한다.

 

public class MovableAdapterImpl implements MovableAdapter{

    private Movable luxuryCars;

    // standard constructors
    MovableAdapterImpl(Movable luxuryCars) {
        this.luxuryCars = luxuryCars;
    }

    @Override
    public double getSpeed() {
        return convertMPHtoKMPH(luxuryCars.getSpeed());
    }

    private double convertMPHtoKMPH(double mph) {
        return mph * 1.60934;
    }
}

 

AdapterPatternTest.java

 

@SpringBootTest
class AdapterPatternTest {

    @Test
    void 어댑터_테스트() {
        Movable bugattiVeyron = new BugattiVeyron();
        MovableAdapter bugattiVeyronAdapter = new MovableAdapterImpl(bugattiVeyron);

        assertEquals(bugattiVeyronAdapter.getSpeed(), 431.30312, 0.00001);

    }


}

 

정리

 - 어댑터 패턴은 호환되지 않는 두 개의 객체를 호환되게 하기 위해서 하나의 Adapter라는 인터페이스를 추가해서 변환하는 로직을 추가한다.

기존 클래스의 소스코드를 수정해서 인터페이스에 맞추는 작업보다 기존 클래스의 소스코드의 수정을 하지 않고 타겟 인터페이스에 맞춰서 동작을 가능하게 한다.

 

실무 사용 사례

- java에서 collections(Arrays.asList, Collections.enumeration(), Collections.list())

- java에서 IO code

- Spring에서 Spring Security에 UserDetails

- Spring에 HandlerAdapter

728x90
반응형
반응형

싱글톤 패턴(Singleton Pattern)

  - 싱글톤 패턴은 생성패턴(Creational Pattern) 중 하나이다.
  - 싱글톤 패턴은 인스턴스를 만드는 절차를 추상화하는 패턴이다.
  - 싱글톤 패턴에 속하는 패턴들은 객체를 생성, 합성하는 방법이나 객체의 표현방법을 시스템과 분리해준다.

  - 하나의 객체만을 생성해 이후에 호출된 곳에서는 생성된 객체를 반환하여 프로그램 전반에서 하나의 인스턴스만을 사용하게 하는 패턴이다.

 

장점

  - 한번의 객체 생성으로 재사용이 가능하여 메모리 낭비를 방지한다.

  - 시스템 런타임, 환경 세팅에 대한 정보 등, 인스턴스가 여러개 일 때 생기는 문제를 미연에 방지할 수 있다.

 

문제점

  - 멀티 스레드 환경에서 안전하지 않다. (대처가 가능하다.)

  - 객체간의 결함도가 높아지고 변경에 유연하게 대처할 수 없다. 싱글톤 객체가 변경되면 이를 참조하고 있는 모든 값들이 변경되어야 한다.

  - 객체 상태를 유지하면 큰 문제가 발생한다.(stateful)

코드

  * 여러가지 싱글턴 구현 방법이있다.

  1. 기본 Singleton
  2. 이른 초기화(Eager Initialization)
  3. synchronized 사용
  4. static inner SIngleton(권장하는 방법)
  5. enum SIngleton(권장하는 방법)

 

1. 기본 Singleton

 - 단점이 Thread Safe 하지 못하다.

 

public class SingletonStatic {

    private static SingletonStatic instance;

    private SingletonStatic() {}

    public static SingletonStatic getInstance() {
        if(instance == null) {
            instance = new SingletonStatic();
        }
        
        return instance;
    }

}

 

 

2. Eager Initialization

 - Thread Safe

 - 단점이 인스턴스를 미리 만들어 놓는다는 단점이 있다.

 

public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }

}

 

3. Thread Safe Singleton(synchronized 사용)

  - synchronized를 통해서 여러 쓰레드에서 동시에 접근하는 것을 막을 수 있다.(Thread Safe)

  - 동기화 처리하는 작업 때문에 성능에 불이익이 생길 수 있다.

 

public class ThreadSafeSingleton {

    private static ThreadSafeSingleton instance;
    
    private ThreadSafeSingleton() {}

    public static synchronized ThreadSafeSingleton getInstance() {
        if (instance == null) { instance = new ThreadSafeSingleton();}
        return instance;
    }
    
}

 

4. static inner SIngleton(권장하는 방법)

  - SingletonHelper 클래스는 SIngleton 클래스가 Load 될 때에도 Load 되지 않다가 getInstance()를 호출할 때 jvm 메모리에 로드되어 인스턴스를 생성한다. synchronized를 사용하지 않기 때문에 synchronized 키워드 자체에 대한 성능 저하 문제를 해결할 수 있다.

  - 단점으로 자바 리플렉션을 이용하면 싱글톤 패턴이 깨진다.

 

public class BillPughSIngleton {

    private BillPughSIngleton(){}

    private static class SingletonHelper{
        private static final BillPughSIngleton INSTANCE = new BillPughSIngleton();
    }

    public static BillPughSIngleton getInstance(){
        return SingletonHelper.INSTANCE;
    }
    
}

 

객체 상태를 유지했을 경우 주의 (stateful)

BillPughSIngleton.java

 

public class BillPughSIngleton {

    private String apartment;

    private BillPughSIngleton(){}

    private static class SingletonHelper{
        private static final BillPughSIngleton INSTANCE = new BillPughSIngleton();
    }

    public static BillPughSIngleton getInstance(){
        return SingletonHelper.INSTANCE;
    }

    public String getApartment() {
        return apartment;
    }

    public void setApartment(String apartment) {
        this.apartment = apartment;
    }

}

 

SIngletonTest.java

 

    @Test
    void singleTonStatefulTest() {

        BillPughSIngleton instance1 = BillPughSIngleton.getInstance();
        BillPughSIngleton instance2 = BillPughSIngleton.getInstance();

        instance1.setApartment("아크로빌 아파트");
        instance2.setApartment("한남더힐 아파트");

        System.out.println(instance1.getApartment());
        System.out.println(instance2.getApartment());


    }

  - stateful하게 코드를 작성하게 되면 위와 같이 객체의 상태가 바뀌는 문제가 발생하게 된다.

무결성하게(stateless) 코드를 작성하여야 한다.

 

BillPughSIngleton.java

  - setApartment 값을 바로 return 한다.

 

public class BillPughSIngleton {

    private String apartment;

    private BillPughSIngleton(){}

    private static class SingletonHelper{
        private static final BillPughSIngleton INSTANCE = new BillPughSIngleton();
    }

    public static BillPughSIngleton getInstance(){
        return SingletonHelper.INSTANCE;
    }

    public String getApartment() {
        return apartment;
    }

    public String setApartment(String apartment) {
        this.apartment = apartment;
        return apartment;
    }

}

 

SIngletonTest.java

 

   @Test
    void singleTonStatefulTest() {

        BillPughSIngleton instance1 = BillPughSIngleton.getInstance();
        BillPughSIngleton instance2 = BillPughSIngleton.getInstance();

        String apartment1 = instance1.setApartment("아크로빌 아파트");
        String apartment2 = instance2.setApartment("한남더힐 아파트");

        System.out.println(apartment1);
        System.out.println(apartment2);
        
    }

 

5. enum Singleton(권장하는 방법)

 - 4번(static inner Singleton)과 같이 권장하는 방법으로 enum 클래스로 사용하는 방법이다.

 - 자바 리플랙션을 이용해도 enum 클래스는 newInstance를 이용해서 새로운 인스턴스를 생성할 수 없어서 더 안전하다.

 - 단점으로는 상속을 사용하지 못하고, 사용하지 않아도 미리 만들어 놓는다는 단점이 있다.  

 

public enum ThreadSafeV5 {

    INSTANCE;

    private String name;

    public String getName() {
        return name;
    }

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

 

실무 사용 사례

1. 자바 java.lang.Runtime 클래스가 싱글톤 패턴으로 구현되어 있다.

2. java Application 개발에서 많이 사용되는 스프링 프레임워크는 스프링 빈 컨테이너의 도움을 받아 싱글톤 스코프로 관리된다.

스프링 프레임워크를 사용하면 싱글톤 패턴의 문제점들을 보완하면서 장점을 누릴수 있다.  

3. 다른 디자인 패턴(빌더, 퍼사드, 추상 팩토리 등) 구현체의 일부로 쓰이기도 한다.

 

728x90
반응형
반응형

빌더패턴(Builder Pattern)

  - 빌더 패턴은 생성패턴(Creational Pattern) 중 하나이다.
  - 생성 패턴은 인스턴스를 만드는 절차를 추상화하는 패턴이다.
  - 생성 패턴에 속하는 패턴들은 객체를 생성, 합성하는 방법이나 객체의 표현방법을 시스템과 분리해준다.

  - 별도의 Builder 클래스를 생성해서 필수 값에 대해서는 생성자를 통해, 선택적인 값들에 대해서는 메소드를 통해 값을 입력받은 후에 build() 메서드를 통해 최종적으로 하나의 인스턴스를 리턴하는 방식이다.

 

장점

  - 필요한 데이터만 설정할 수 있다.

  - 유연성을 확보할 수 있다.

  - 가독성을 높일 수 있다.

  - 변경 가능성을 최소화할 수 있다.

 

코드

House.java

 

public class House {

    private Long houseId;
    private String address;
    private String type;
    private Long price;
    private String phone;

    public Long getHouseId() {
        return houseId;
    }
    public String getAddress() {
        return address;
    }
    public String getType() {
        return type;
    }
    public Long getPrice() {
        return price;
    }
    public String getPhone() {
        return phone;
    }

    private House(HouseBuilder builder) {
        this.houseId = builder.houseId;
        this.address = builder.address;
        this.type = builder.type;
        this.price = builder.price;
        this.phone = builder.phone;
    }

    public static class HouseBuilder{

        private Long houseId;
        private String address;
        private String type;
        private Long price;
        private String phone;

	// 필수값 생성자
        public HouseBuilder(Long houseId, String address, String type) {
            this.houseId = houseId;
            this.address = address;
            this.type = type;
        }

        public HouseBuilder price(Long price) {
            this.price = price;
            return this;
        }

        public HouseBuilder phone(String phone) {
            this.phone = phone;
            return this;
        }

        public House build() {
            return new House(this);
        }

    }


}

HouseTest.java

@SpringBootTest
class HouseTest {

    @Test
    void HouseBuilderTest() {

        String houseAddress = "서울특별시 관악구";
        String houseType = "매매";
        Long housePrice = 10000000L;

        House house =  new House.HouseBuilder(1L, houseAddress, houseType)
                                .price(housePrice)
                                .build();

        assertThat(house.getAddress(), is(houseAddress));
        assertThat(house.getType(), is(houseType));
        assertThat(house.getPrice(), is(housePrice));

    }


}

 

실무 사용 사례

- java에 StringBuilder

- java에 Stream API

- Spring Lombok

- Spring UriComponents

 

정리

 - 인스턴스를 생성할 때 생성자만을 통해서 생성하면 칼럼이 변경될 때마다 많은 소스코드에 영향이 가는 고통스러운 상황이 발생할 것이다. 빌더 패턴을 이용해서 유연성을 확보할 수 있다.

 

참조

 - https://readystory.tistory.com/121

 

 

728x90
반응형
반응형

팩토리 메소드 패턴(Factory Method Pattern)

  - 팩토리 메소드 패턴은 생성패턴(Creational Pattern) 중 하나이다.
  - 생성 패턴은 인스턴스를 만드는 절차를 추상화하는 패턴이다.
  - 생성 패턴에 속하는 패턴들은 객체를 생성, 합성하는 방법이나 객체의 표현방법을 시스템과 분리해준다.

 

장점

  - 확장에 열려있고 변경에 닫혀있는 객체 지향 원칙을 적용해서 기존에 인스턴스를 만드는 과정의 로직을 건드리지 않고, 새로운 인스턴스를 다은 방법으로 확장이 가능하다.

  - 팩토리 메소드 패턴은 클라이언트 코드로부터 서브 클래스의 인스턴스화를 제거하여 서로 간의 종속성을 낮추고, 결합도를 느슨하게 하며(Loosely Coupled), 확장을 쉽게 한다.

  - 팩토리 메소드 패턴은 클라이언트와 구현 객체들 사이에 추상화를 제공한다.

 

단점

  - 각자의 역할을 나누어서 생성하다 보니 클래스 개수가 늘어난다.

코드

  * 부동산 중개 수수료 계산 프로그램 가정(매매/임대차)

 

  - 중개 정책 interface 생성 :

     interface를 구현하는 하위 클래스에서 수수료 계산 로직을 구현한다.

     java8 이후로 default 메소드를 사용하면 interface 내부에서 로직 작성이 가능하다. (default 명시)

     java9 이후로는 interface에 priave 메소드도 사용이 가능하다.

public interface BrokeragePolicy {

    BrokerageRule createBrokerageRule(Long price);

     default Long calculate(Long price) {
        BrokerageRule rule = createBrokerageRule(price);
        return rule.calcMaxBrokerage(price);
    }
}

 

  - 매매 중개 수수료 class :

    java7 이후부터 _는 숫자 리너털 어디에도 사용할 수 있다. 가독성 향상

 

public class PurchaseBrokeragePolicy implements BrokeragePolicy{

    public BrokerageRule createBrokerageRule(Long price) {
        BrokerageRule rule;
        if(price < 50_000_000) {
            rule = new BrokerageRule(0.6, 250_000L);
        } else if(price < 200_000_000) {
            rule = new BrokerageRule(0.5, 800_000L);
        }else if(price < 600_000_000) {
            rule = new BrokerageRule(0.4, null);
        }else if(price < 900_000_000) {
            rule = new BrokerageRule(0.5, null);
        }else{
            rule = new BrokerageRule(0.9, null);
        }
        return rule;
    }


}

 

  - 임대차 중개 수수료 class

 

public class RentBrokeragePolicy implements BrokeragePolicy{

    public BrokerageRule createBrokerageRule(Long price) {
        BrokerageRule rule;
        if(price < 50_000_000) {
            rule = new BrokerageRule(0.5, 200_000L);
        } else if(price < 100_000_000) {
            rule = new BrokerageRule(0.4, 300_000L);
        }else if(price < 300_000_000) {
            rule = new BrokerageRule(0.3, null);
        }else if(price < 600_000_000) {
            rule = new BrokerageRule(0.4, null);
        }else{
            rule = new BrokerageRule(0.8, null);
        }
        return rule;
    }


}

 

  - Factory Class (핵심 클래스)

 

public class BrokeragePolicyFactory {

    public static BrokeragePolicy of(ActionType actionType) {
        switch (actionType) {
            case RENT:
                return new RentBrokeragePolicy();
            case PURCHASE:
                return new PurchaseBrokeragePolicy();
            default:
                throw new IllegalArgumentException("해당 actionType에 대한 정책이 존재하지 않습니다.");
        }
    }

}

 

- 중개 수수료 계산 api

 

public Long calcBrokerage(@RequestParam ActionType actionType,
                          @RequestParam Long price) {
        // 타입 정의 - 매매 / 임대차
        BrokeragePolicy policy = BrokeragePolicyFactory.of(actionType);
        return policy.calculate(price);
}

 

실무 사용 사례

- Spring에서 BeanFactory

- Java Calendar 

728x90
반응형

'JAVA > Design Pettern' 카테고리의 다른 글

[Design Pattern] 프록시 패턴  (0) 2022.03.22
[Design Pattern] 추상 팩토리 패턴  (0) 2022.03.19
[Design Pattern] 어댑터 패턴  (0) 2022.03.16
[Design Pattern] 싱글톤 패턴  (0) 2022.03.15
[Design Pattern] 빌더 패턴  (0) 2022.03.14
반응형

목차

  1. 객체와 테이블 매핑
  2. 데이터베이스 스키마 자동 생성
  3. 필드와 칼럼 매핑
  4. 기본 키 매핑

엔티티 매핑 소개

1. 객체와 테이블 매핑 : @Entity, @Table

 - @Entity가 붙은 클래스는 JPA가 관리, 엔티티라 한다.

 - JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수

주의 

 - 기본 생성자 필수(파라미터가 없는 public 또는 protected 생성자)

 - final 클래스, enum, interface, inner 클래스 사용x

 - 저장할 필드에 final 사용x 

 

@Entity

- JPA에서 사용할 엔티티 이름을 지정한다.

- 기본값 : 클래스 이름을 그대로 사용

- 가급적 기본값 사용 권장

 

@Table 

- 엔티티와 매핑할 데이블 지정

name - 매핑할 테이블 이름

@Table(name = "MBR")을 사용해서 insert 쿼리가 MBR 테이블로 생성된다.

 

2. 데이터베이스 스키마 자동 생성

- DDL을 애플리케이션 실행 시점에 자동 생성

- 테이블 중심 -> 객체 중심

- 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL 생성

- 이렇게 생성된 DDL은 개발 장비에서만 사용

 

옵션 설명
create 기존테이블 삭제 후 다시 생성(DROP + CREATE)
create-drop create와 같으나 종료시점에 테이블 DROP
update 변경분만 반영(운영 DB에는 사용하면 안됨)
validate 엔티티와 테이블이 정상 매핑되었는지만 확인
none 사용하지 않음

 

persistence.xml

DB 테이블을 DROP하고 CREATE 한다.

 

주의!

운영 장비에는 절대 create, create-drop, update 사용하면 안된다.

개발 초기 단계는 create 또는 update

테스트 서버는 update 또는 validate

스테이징과 운영 서버는 validate 또는 none

 

DDL 생성 기능

- DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.

크게 애플리케이션에 영향을 주지 않음 길이나 ALTER DDL 쿼리만 추가된다. 

3. 필드와 컬럼 매핑 : @Column

어노테이션 설명
@Column 컬럼 매핑
@Temporal 날짜 타입 매핑
@Enumerated enum 타입 매핑
@Lob BLOB, CLOB 매핑
@Transient 특정 필드를 칼럼에 매핑하지 않음(매핑 무시)

@Lob - 문자열 타입이면 기본으로 clob으로 생성된다.

@Enumerated(EnumType.STRING) - EnumType.STRING 사용O ORDINAL 사용X

 

참고 - LocalDate, LocalDateTime을 사용할 때는 @Temporal(TemporalType.TIMESTAMP) 생략 가능(최신 하이버네이트 지원)

4. 기본 키 매핑 : @Id

기본 키 매핑 방법

직접 할당 : @Id만 사용

자동 생성(@GeneratedValue)

 - IDENTITY : 데이터베이스에 위임, MYSQL

 - SEQUENCE : 데이터베이스 시쿼스 오브젝트 사용, ORACLE, @SequenceGenerator 필요

 - TABLE : 키 생성용 테이블 사용, 모든 DB에서 사용, @TableGenerator 필요

 - AUTO : 방언에 따라 자동 지정, 기본 값

 

IDENTITY 전략 - 특징

  • 기본 키 생성을 데이터베이스 위임
  • 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용
  • JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행
  • AUTO_INCREMENT는 데이터베이스에 INSERT SQL을 실행 한 후 이후에 ID 값을 알 수 있음
  • IDENTITY 전략은 entityManager.persist() 시점에 즉시 INSERT SQL 실행하고 DB에서 식별자를 조회

SEQUENCE 전략 - 특징

  • 데이터베이스 시쿼스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트
  • 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용
  • 속성 allocationSize - 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨, 데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 값을 반드시 1로 설정해야 한다.)

TABLE 전략 - 특징

  • 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
  • 장점 : 모든 데이터베이스에 적용 가능
  • 단점 : 성능

 

직접 할당

Member.java
JpaMain.java

자동 할당 - IDENTITY 전략

Member.java
JpaMain.java

자동 할당 - SEQUENCE 전략

시퀀스 DROP 이후 생성

 

자동 할당 - TABLE 전략

키 값을 채번하는 테이블 생성

 

참고 - 인프런 강의(자바 ORM 표준 JPA 프로그래밍 - 기본편, 김영한)

728x90
반응형

'JPA' 카테고리의 다른 글

JPA 순환 참조 원인 및 해결 방법  (0) 2021.08.26
JPA 연관관계 매핑 기초  (0) 2021.06.17
JPA 영속성 관리  (0) 2021.06.06
JPA 소개  (0) 2021.06.04
반응형

들어가기전에 JPA 환경 세팅

  • idea - Intellij 
  • maven 프로젝트

디렉토리 구조

 

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>jpa-basic</groupId>
<artifactId>ex1-hello-jpa</artifactId>
<version>1.0.0</version>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>

<dependencies>
<!-- JPA 하이버네이트 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.3.10.Final</version>
</dependency>
<!-- H2 데이터베이스 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>

<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
</project>

 

JPA 설정 파일 (/META-INF/persisatence.xml 위치)

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="hello">
<properties>
<!-- 필수 속성 -->
<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"/> <!--데이터베이스 방언-->

<!-- 옵션 -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
<!--<property name="hibernate.hbm2ddl.auto" value="create" />-->
</properties>
</persistence-unit>
</persistence>

 

JPA는 특정 데이터베이스에 종속X

각각의 데이터베이스가 제공하는 SQL 문법과 함수는 조금씩 다름
방언: SQL 표준을 지키지 않는 특정 데이터베이스만의 고유한 기능

<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <!--데이터베이스 방언-->

사용하는 데이터베이스에 따라서 dialect를 다르게 설정한다. 이번 실습은 H2 데이터베이스 사용

 

JpaMain.java

EntityManagerFactory - 하나만 생성해서 애플리케이션 전체에서 공유

EntityManager - 쓰레드간에 공유X (사용하고 버려야 한다.)

JPA의 모든 데이터 변경은 트랜잭션 안에서 실행

JPQL - 테이블이 아닌 객체를 대상으로 검색하는 객체 지향 쿼리

 

JPA에서 가장 중요한 2가지

  • 객체와 관계형 데이터베이스 매핑하기(Object Relational Mapping)
  • 영속성 컨텍스트

엔티티 매니저 팩토리와 엔티티 매니저

출처 - inflean 자바 ORM 표준 JPA 프로그래밍 - 기본편 수업자료, 김영한

영속성 컨텍스트

  • JPA를 이해하는데 가장 중요한 용어
  • "엔티티를 영구 저장하는 환경"이라는 뜻
  • EntityManager.persist(entity);

EntityManager가 영속성 컨테스트라고 생각하면 쉽다.

 

엔티티의 생명주기

비영속(new/transient) - 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태

영속(managed) - 영속성 컨텍스트 관리되는 상태

준영속(detached) - 영속성 컨텍스트에 저장되었다가 분리된 상태

삭제(removed) - 삭제된 상태

출처 - inflean 자바 ORM 표준 JPA 프로그래밍 - 기본편 수업자료, 김영한

비영속 상태 - 객체를 생성한 상태

영속 상태 - 객체를 저장한 상태

준영속, 삭제 상태

영속성 컨텍스트의 이점

  • 1차 캐시
  • 동일성(identity) 보장
  • 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
  • 변경 감지(Dirty Checking)
  • 지연 로딩(Lazy Loading)

1차 캐시에서 조회

entityManager.persist(member) - 영속성 컨텍스트에서 관리되기 때문에 db select 쿼리 필요 없이 1차 캐시에서 바로 해당 member name 출력 가능

id가 2인 member를 찾기위해서 db select 쿼리 후 member name 출력

 

영속 엔티티의 동일성 보장

1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공

 

트랜잭션을 지원하는 쓰기 지연

EntityTransaction transaction = entityManager.getTransaction();
transaction.begin(); - 엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.

entityManager.persist(member2); - 여기까지 INSERT 쿼리를 데이터베이스에 보내지 않는다.

커밋하는 순간 데이터 베이스에 INSERT 쿼리를 보낸다.

쓰기 지연 SQL 저장소에 쿼리를 저장해놓다가 commit 하는 순간 데이터베이스에 쿼리를 날린다.

 

변경 감지(Dirty Checking)

영속성 컨텍스트에서 관리하고 있다가 엔티티와 스냅샷을 비교하여 변경이 감지되면 commit 할 때 update 쿼리를 데이터베이스에 날린다.

 

엔티티 삭제

entityManager.remove(member1) - 엔티티 삭제

플러시

영속성 컨텍스트의 변경내용을 데이터베이스에 반영

 

플러시 발생

  • 변경 감지
  • 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
  • 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송(등록, 수정,  삭제 쿼리)

영속성 컨텍스트를 플러시하는 방법

  • entityManager.flush() - 직접 호출
  • 트랜잭션 커밋 - 플러시 자동 호출
  • JPQL 쿼리 실행 - 플러시 자동 호출

JPQL 쿼리 실행 시 플러시가 자동으로  호출되는 이유 - 영속성 컨텍스트에서만 관리되면 해당 테이블 모두 조회하는 쿼리가 실행 시 데이터베이스가 동기화되지 않아서 문제 발생 소지가 있기 때문에

EX)

플러시는 

  • 영속성 컨텍스트를 비우지 않음
  • 연속성 컨텍스트의 변경내용을 데이터베이스에 동기화
  • 트랜잭션이라는 작업 단위가 중요 -> 커밋 직전에만 동기화하면 됨

준영속 상태

  • 영속 -> 준영속
  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)
  • 영속성 컨텍스트가 제공하는 기능을 사용 못함

준영속 상태로 만드는 방법

entityManager.detach(entyty) - 특정 엔티티만 준영속 상태로 전환

entityManager.clear() - 영속성 컨텍스트를 완전히 초기화

entityManager.close() - 영속성 컨텍스트를 종료

 

 

참고 - 인프런 강의(자바 ORM 표준 JPA 프로그래밍 - 기본편, 김영한)

728x90
반응형

'JPA' 카테고리의 다른 글

JPA 순환 참조 원인 및 해결 방법  (0) 2021.08.26
JPA 연관관계 매핑 기초  (0) 2021.06.17
JPA 엔티티 매핑  (0) 2021.06.08
JPA 소개  (0) 2021.06.04
반응형
  • SQL 중심적인 개발의 문제점
  • JPA 소개

SQL 중심적인 개발의 문제점

-  무한 반복

- 지루한 코드

 

반복되는 CRUD 작업

INSERT INTO...

UPDATE...

SELECT...

DELETE...

자바 객체를 SQL로 ...

SQL을 자바 객체로 ...

 

public class Member {

   private String memberId;

   pirvate String name;

   ...

}

 

INSERT INTO MEMBER(MEMBER_ID, NAME) VALUES

SELECT MEMBER_ID, NAME FROM MEMBER M

UPDATE MEMBER SET ...

 

어느날 갑자기 요청사항으로 전화번호 칼럼 추가 요청이 오면 관련 테이블에 모두 수정 필요 

 

public class Member {

   private String memberId;

   pirvate String name;

   private String tel;  // 추가

   ...

}

 

INSERT INTO MEMBER(MEMBER_ID, NAME, TEL) VALUES // TEL 추가

SELECT MEMBER_ID, NAME, TEL FROM MEMBER M // TEL 추가

UPDATE MEMBER SET ... TEL = ? // TEL 추가

 

SQL에 의존적인 개발을 피하기 어렵다.

 

패러다임의 불일치! (객체 VS 관계형 데이터베이스)

객체와 관계형 데이터베이스의 차이

1. 상속

2. 연관관계

3. 데이터 타입

4. 데이터 식별 방법

 

1) 객체다운 모델링으로 개발

 

class Member {

   String id;

   Team team;  // 참조로 연관관계 맺는다.

   String username;



   Team getTeam() {

          return team;

   }

}



class Team {

   Long id;

   String name;

}

 

2) 객체 모델링 저장

member.getTeam().getId(); 를 이용해서 TEAM_ID에 넣는다.

 

INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME) VALUES ...

 

3) 객체 모델링 조회  

  객체다운 설계를 하여 조회를 하는데 오히려 복잡하고 맵핑 작업 시간이 더 걸려 결국엔 슈퍼 DTO 객체를 만들게 된다.

 

SELECT M.*, T.*

 FROM MEMBER M

  JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
public Member find(String memberId) {

   // SQL 실행 ... 

   Member member = new Member();

   // 데이터베이스에서 조회한 회원 관련 정보를 모두 입력

   Team team = new Team();

   // 데이터베이스에서 조회한 팀 관련 정보를 모두 입력



   // 회원과 팀 관계 설정

   member.setTeam(team);

   return member;

}

 

4) 처음 실행하는 SQL에 따라 탐색 범위 결정되는 경우

 

SELECT M.*, T.*

 FROM MEMEBER M

  JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID

 

member.getTeam(); // ok

member.getOrder(); // null 처음에 맴버과 팀만 가져왔기 때문에..  마음대로 사용자가 주문한 order 호출할 수가 없다..

 

만약에 memberDAO.find()를 다른 개발자가 개발했다면

엔티티 신뢰 문제 발생.. 개발자가 직접 눈으로 확인하지 않은 이상은 신뢰하고 사용하기 힘들다.

 

class MemberService {

  ...

 public void provess() {

    Mmber member = memberDAO.find(memberId);

    member.getTeam();  // ???

    member.getOrder().getDelivery();  // ???

}

 

모든 객체를 미리 로딩할 수는 없다.

상황에 따라 동일한 회원 조회 메서드를 여러개 생성한다.

 

memberDAO.getMember(); // Member만 조회

memberDAO.getMemberWithTeam(); // Member와 Team 조회

// Memberm, Order, Delivery

memberDAO.getMemberWithOrderWithDelivery();

 

그래서 자바 컬렉션에 저장하듯이 DB에 저장할 수는 없을까? 고민 끝에 JPA 등장!

JPA

- Java Persistence API

- 자바 진영의 ORM 기술 표준

 

ORM

- Object-relational mapping(객체 관계 매핑)

- 객체는 객체대로 설계

- 관계형 데이터베이스는 관계형 데이터베이스대로 설계

- ORM 프레임워크가 중간에서 매핑

- 대중적인 언어에는 대부분 ORM 기술이 존재

JPA는 애플리케이션과 JDBC 사이에서 동작

JPA 동작 - 저장

JPA 동작 - 조회

JPA는 표준 명세

- JPA는 인터페이스의 모음

- JPA 2.1 표준 명세를 구현한 3가지 구현체

- 하이버네이트, EclipseLink, DataNucleus

 

JPA를 사용하는 이유

- SQL 중심적인 개발에서 객체 중심으로 개발

- 생산성

- 유지보수

- 패러다임의 불일치 해결

- 데이터 접근 추상화와 벤더 독립성

- 표준

 

생산성 - JPA와 CRUD

저장 : jpa.persist(member)

조회 : Member member = jpa.find(memberId)

수정 : member.setName("변경할 이름")

삭제 : jpa.remove(member)

 

JPA의 성능 최적화 기능

1. 1차 캐시와 동일성(identity) 보장

2. 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)

3. 지연 로딩(Lazy Loading)

 

1차 캐시와 동일성 보장

1. 같은 트랜잭션 안에서는 같은 엔티티를 반환 - 약간의 조회 성능 향상

2. DB Isolation Level이 Read Commit이어도 애플리케이션에서 Repeatable Read 보장

 

String memberId = "100";

Member m1 = jpa.find(Member.class,  memberId); //SQL

Member m2 = jpa.find(Member.class,  memberId); //캐시

println(m1 == m2) // true

 

SQL문이 1번만 실행된다.

 

트랜잭션을 지원하는 쓰기 지연(transactional write-behind)

1. 트랜잭션을 커밋할 때까지 INSERT SQL을 모음

2. JDBC BATCH SQL 기능을 사용해서 한번에 SQL 전송

 

지연 로딩(Lazy Loading)

지연 로딩 : 객체가 실제 사용될 때 로딩

 

Member member = MemberDAO.find(memberId); => SELECT * FROM MEMBER

Team team = member.getTeam();

String teamName = team.getName(); => SELECT * FROM TEAM

 

즉시 로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회

 

Member member = MemberDAO.find(memberId); => SELECT M.*, T.* FROM MEMBER JOIN TEAM ...

Team team = member.getTeam();

String teamName = team.getName();

 

 

 

** ORM은 객체와 RDB 두 기둥위에 있는 기술

** JPA 사용하기 전 과거로 돌아가고 싶지않다. (JPA 사용하는 어느 개발자가..)

    - 개발 인생에서 많은 시간을 SQL 생성하는데 투자하는데 그 시간을 아껴 더 좋은 코드를 생산할 수 있다.

 

 

참고 - 인프런 강의(자바 ORM 표준 JPA 프로그래밍 - 기본편, 김영한)

 

728x90
반응형

'JPA' 카테고리의 다른 글

JPA 순환 참조 원인 및 해결 방법  (0) 2021.08.26
JPA 연관관계 매핑 기초  (0) 2021.06.17
JPA 엔티티 매핑  (0) 2021.06.08
JPA 영속성 관리  (0) 2021.06.06

+ Recent posts