티스토리 뷰

반응형


| Web Application (Spring + JPA) 1.


--

1. 프로젝트 환경설정

2. 도메일 모델과 테이블 설게

3. 애플리케이션 기능 구현



|| 프로젝트 환경 설정


--


||| 프로젝트 구조 분석


--

project (프로젝트 루트)

|-- src (소스 폴더)

|    |-- main (실행 코드)

|    |    |-- java    (자바 소스 코드)

|    |    |-- resources    (리소스)

|    |    -- webapp    (웹 폴더)

|    | 

|    - test (테스트 코드)

|

|-- target   (빌드 결과)

- pom.xml (메이븐 설정 파일)



||| 메이븐과 라이브러리 설정


--

groupId + artifactId + version 만 적어주면

라이브러리(jar)파일을 메이븐 공식 저장소에서 자동으로 내려받아 라이브러리에 추가.


> 핵심 라이브러리

- Spring MVC (spring-webmvc) : Spring MVC Library

- Spring ORM (spring-orm) : Spring framework와 JPA 연동을 위한 Library

- JPA, Hibernate (hibernate-entitymanager) : JPA 표준과 Hibernate를 포함하는 Library


> 기타 라이브러리

- Connection Pool (tomcat-jdbc) : tomcat-jdbc connection pool 사용

- WEB : servlet, jsp 관련 Library

- Loggin SLF4J & LogBack : Log4j가 이전에 많이 사용되었지만 최근은 SLF4J & LogBack이 많이 사용


PAShop\pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
<?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">
 
    <!-- POM 모델 버전 -->
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>jpabook</groupId>    <!-- 프로젝트 그룹명 -->
    <artifactId>ch11-jpa-shop</artifactId>    <!-- 프로젝트를 식별하는 아이디 -->
    <version>1.0-SNAPSHOT</version>    <!-- 프로젝트 버전 -->
    <name>jpa-shop</name>    <!-- 프로젝트 이름 -->
    <packaging>war</packaging>    <!-- 빌드 패키징 방법 지정. Web Application : war, Java Library : jar -->
 
    <properties>
 
        <!-- 기본 설정 -->
        <java.version>1.6</java.version>
        <!-- 프로젝트 코드 인코딩 설정 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 
        <!-- 스프링 프레임워크 버전 -->
        <spring-framework.version>4.1.6.RELEASE</spring-framework.version>
        <!-- JPA, 하이버네이트 버전 -->
        <hibernate.version>4.3.10.Final</hibernate.version>
        <!-- 데이터베이스 버전 -->
        <tomcat-jdbc.version>7.0.57</tomcat-jdbc.version>
        <h2db.version>1.4.187</h2db.version>
        <!-- JSP, WEB 버전 -->
        <jsp.version>2.2</jsp.version>
        <jstl.version>1.2</jstl.version>
        <servlet.version>3.0.1</servlet.version>
        <!-- 로그 버전 -->
        <logback.version>1.1.1</logback.version>
        <slf4j.version>1.7.6</slf4j.version>
        <!-- 테스트 버전 -->
        <junit.version>4.12</junit.version>
 
    </properties>
 
    <!-- 사용할 라이브러리 지정 -->
    
    <dependencies>
 
        <!-- 스프링 MVC -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>
 
        <!-- 스프링 ORM -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>
 
        <!-- JPA, 하이버네이트 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
 
        <!-- H2 데이터베이스 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>${h2db.version}</version>
            <scope>runtime</scope>
        </dependency>
 
        <!-- 커넥션 풀 -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jdbc</artifactId>
            <version>${tomcat-jdbc.version}</version>
            <scope>compile</scope>
        </dependency>
 
        <!-- WEB -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>${jstl.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
            <scope>provided</scope>
        </dependency>
 
        <!-- 로깅 SLF4J & LogBack -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
            <scope>runtime</scope>
        </dependency>
 
        <!-- 테스트 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring-framework.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
 
    </dependencies>
 
    <!-- 빌드 관련 정보 설정 -->
    <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
              <source>${java.version}</source>
              <target>${java.version}</target>
            </configuration>
          </plugin>
          
          <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <path>/</path>
                   <uriEncoding>UTF-8</uriEncoding>
            </configuration>
          </plugin>
                               
        </plugins>
    </build>
 
</project>
 
cs



* 메이븐 <dependency>의 <scope> 설정

- compile (default) : 컴파일 시 라이브러리 사용

- runtime : 실행 시 라이브러리 사용

- provided : 외부에서 라이브러리 제공. 컴파일 시 사용하지만 빌드에 포함 X (JSP, Servlet ..)

- test : 테이스 코드에만 사용



||| 스프링 프레임워크 설정


--

project

-- src 

     -- main 

          |-- java    

          |-- resources   

          |   - appConfig.xml       (3. 스프링 애플리케이션 관련 설정)

          |   - webAppConfig.xml    (2. 스프링 웹 관련 설정)

          - webapp   

              - WEB-INF

                  - web.xml         (1. 웹 애플리케이션 환경설정 파일)

 


1. JPAShop\src\main\webapp\WEB-INF\web.xml

- 웹 애플리케이션 환경설정 파일

- 웹 애플리케이션에서 스프링 프레임워크를 구동하기 위한 설정

- appConfig.xml(비즈니스 도메인 계층), webAppConfig.xml(웹 계층) 을 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0" metadata-complete="true">
 
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
 
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
 
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <!-- 
        appConfig.xml
            비즈니스 로직, 도메인 계층, 서비스 계층, 데이터 저장 계층을 담당 
        -->
        <param-value>classpath:appConfig.xml</param-value
    </context-param>
 
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!-- 
            webAppConfig.xml
                스프링 MVC 설정을 포함해서 웹 계층 담당
            -->
            <param-value>classpath:webAppConfig.xml, classpath:appConfig.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
 
</web-app>
cs


2. JPAShop\src\main\resources\webAppConfig.xml

- 스프링 MVC 설정을 포함해서 웹 계층 담당

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                               http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
                               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
 
    <!-- 스프링 MVC 기능 활성화 -->
    <mvc:annotation-driven/>
    <!--<context:annotation-config />-->
    
    <!-- 
        basePackages를 포함한 하위 패키지를 검색해서
        @Component, @Service, @Repository, @Controller 어노테이션이 붙어 있는 클래스들을
        스프링 빈으로 자동 등록 
    -->
    <context:component-scan base-package="jpabook.jpashop.web"/>
 
    <!-- Spring Bean 등록 (JSP, JSTL 사용)-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
 
    <mvc:default-servlet-handler/>
 
    <mvc:interceptors>
        <bean class="org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor">
            <property name="entityManagerFactory" ref="entityManagerFactory" />
        </bean>
    </mvc:interceptors>
 
</beans>
cs


3. JPAShop\src\main\resources\appConfig.xml

- 비즈니스 로직, 도메인 계층, 서비스 계층, 데이터 저장 계층을 담당

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                               http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context
                               http://www.springframework.org/schema/context/spring-context.xsd
                               http://www.springframework.org/schema/tx
                               http://www.springframework.org/schema/tx/spring-tx.xsd">
 
    <!-- 
        스프링 프레임워크가 제공하는 어노테이션 기반의 트랜잭션 관리자 활성화
        @Transactional이 붙은 곳에 트랜잭션 적용
     -->
    <tx:annotation-driven/>
 
    <context:component-scan base-package="jpabook.jpashop.service, jpabook.jpashop.repository"/>
 
    <!-- dataSource 설정 -->
    <bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource">
        <property name="driverClassName" value="org.h2.Driver"/>
        <property name="url" value="jdbc:h2:mem:jpashop"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
 
    <!-- 트랜잭션 관리자 설정 -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
 
    <!-- 
        @Repository 어노테이션이 붙어 있는 spring bean에 예외 변환 AOP 적용
        이 AOP는 JPA 예외를 스프링 예외로 변환 
    -->
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/<!-- JPA를 스프링 컨테이너에서 사용할 수 있도록 제공 -->
 
    <!-- 엔티티 매니저 팩토리 등록 (Spring Framework에서 JPA 사용을 위함) -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/<!-- 사용할 DataSource 등록 -->
        <property name="packagesToScan" value="jpabook.jpashop.domain"/<!-- @Entity 가 붙은 클래스를 자동으로 검색하기 위한 시작점 지정-->
        <!-- <property name="persistenceUnitName" value="yourPersistenceUnitName"/> --> <!-- 영속성 유닛 이름 지정. 설정하지 않을 시 default 라는 이름의 영속성 유닛 생성 -->
        <property name="jpaVendorAdapter"<!-- 사용할 JPA 벤더를 지정 -->
            <!-- 하이버네이트 구현체 사용 -->
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
        </property>
        <property name="jpaProperties"<!-- 하이버네이트 구현체의 상세 설정 -->
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop<!-- 방언 -->
                <prop key="hibernate.show_sql">true</prop>                   <!-- SQL 보기 -->
                <prop key="hibernate.format_sql">true</prop>                 <!-- SQL 정렬해서 보기 -->
                <prop key="hibernate.use_sql_comments">true</prop>           <!-- SQL 코멘트 보기 -->
                <prop key="hibernate.id.new_generator_mappings">true</prop>  <!-- 새 버전의 ID 생성 옵션 (항상 true 권장) -->
                <prop key="hibernate.hbm2ddl.auto">create</prop>             <!-- DDL 자동 생성 -->
                <!-- 
                    hibernate.hbm2ddl.auto 4가지 옵션
                        - create : 기존 DDL을 제거하고 새로 생성
                        - create-drop : create와 동일, 애플리케이션 종료 시 생성한 DDL을 제거
                        - update : 현재 데이터베이스 DDL과 비교해서 변경사항만 수정
                        - validate : 현재 엔티티 매핑 정보와 데이터베이스 스키마가 같은지 비교 (다르면 경고 후 실행하지 않음)
                 -->
            </props>
        </property>
    </bean>
 
</beans>
cs





|| 도메인 모델과 테이블 설계


--


ㅇ 요구사항 분석

- 회원 기능

- 상품 기능

- 주문 기능

- 기타 요구사항

- ..


ㅇ 도메인 모델 설계

- UML (Unified Modeling Language)



ㅇ 테이블 설계

- ERD 작성



ㅇ 연관관계 정리



||| 엔티티 클래스


--

../domain


/Member.java (회원 엔티티)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Entity
public class Member {
 
    @Id
    @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;
 
    private String name;
 
    @Embedded
    private Address address;
 
    @OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<Order>();
 
    // Getter, Setter ..
}
 
cs

line 7) 회원 ID 기본 키 설정

 @Id : 기본키

 @GeneratedValue : 기본 키 생성 전략

 @Column() : 필드와 매핑할 테이블 컬럼 설정 (참고)

line 9) 회원 이름

line 12) 회원 주소는 Embedded 타입

line 15) 회원은 주문을 여러번 할 수 있음

 @OneToMany(mappedBy="") : 연관관계 주인을 정해줌 (참고)


/Order.java (주문 엔티티)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
@Entity
@Table(name = "ORDERS")
public class Order {
 
    @Id
    @GeneratedValue
    @Column(name = "ORDER_ID")
    private Long id;
 
    // 외래키
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "MEMBER_ID")
    private Member member;      //주문 회원
 
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<OrderItem>();
    
    // 외래키
    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "DELIVERY_ID")
    private Delivery delivery;  //배송정보
 
    private Date orderDate;     //주문시간
 
    @Enumerated(EnumType.STRING)
    private OrderStatus status;//주문상태
 
    //==주문 생성 메서드==//
    public static Order createOrder(Member member, Delivery delivery, OrderItem... orderItems) {
 
        Order order = new Order();
        order.setMember(member);
        order.setDelivery(delivery);
        for (OrderItem orderItem : orderItems) {
            order.addOrderItem(orderItem);
        }
        order.setStatus(OrderStatus.ORDER);
        order.setOrderDate(new Date());
        return order;
    }
 
    //==비즈니스 로직==//
    /** 주문 취소 */
    public void cancel() {
        // 주문이 완료된 상태일 경우
        if (delivery.getStatus() == DeliveryStatus.COMP) {
            throw new RuntimeException("이미 배송완료된 상품은 취소가 불가능합니다.");
        }
 
        this.setStatus(OrderStatus.CANCEL);
        for (OrderItem orderItem : orderItems) {
            orderItem.cancel();
        }
    }
 
    //==조회 로직==//
    /** 전체 주문 가격 조회 */
    public int getTotalPrice() {
        int totalPrice = 0;
        for (OrderItem orderItem : orderItems) {
            totalPrice += orderItem.getTotalPrice();
        }
        return totalPrice;
    }
 
    //==연관관계 메서드==//
    public void setMember(Member member) {
        this.member = member;
        member.getOrders().add(this);
    }
 
    public void addOrderItem(OrderItem orderItem) {
        orderItems.add(orderItem);
        orderItem.setOrder(this);
    }
 
    public void setDelivery(Delivery delivery) {
        this.delivery = delivery;
        delivery.setOrder(this);
    }
 
    // ..
}
cs

line 8) 주문 번호는 기본 키

line 13) 주문을 한 회원의 정보를 외래 키로 가짐

 @ManyToOne(fetch="") : 다대일 관계 설정 및 fetch 전략 설정 (참고)

 @JoinColumn : 외래 키 매핑

line 16) 한 번 주문 시 여러 상품을 주문할 수 있음

line 21) 배송 정보도 외래 키

line 23) 주문 날짜

line 26) 주문 상태는 열거형을 사용(주문 or 취소)

 @Enumerated : 자바의 enum 타입을 매핑

line 29~40) 주문이 들어올 경우 주문 엔티티 생성

회원 정보, 배송 정보, 주문 상품 정보, 주문 상태, 주문 날짜를 받아 엔티티 생성

line 44~54) 주문 취소 기능

주문 취소가 가능할 경우 주문한 상품들을 cancel()


/OrderItem.java (주문상품 엔티티)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@Entity
@Table(name = "ORDER_ITEM")
public class OrderItem {
 
    @Id
    @GeneratedValue
    @Column(name = "ORDER_ITEM_ID")
    private Long id;
 
    // 외래키
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ITEM_ID")
    private Item item;      //주문 상품
 
    // 외래키
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ORDER_ID")
    private Order order;    //주문
 
    private int orderPrice; //주문 가격
    private int count;      //주문 수량
 
    //==생성 메서드==//
    public static OrderItem createOrderItem(Item item, int orderPrice, int count) {
 
        OrderItem orderItem = new OrderItem();
        orderItem.setItem(item);
        orderItem.setOrderPrice(orderPrice);
        orderItem.setCount(count);
 
        item.removeStock(count);
        return orderItem;
    }
 
    //==비즈니스 로직==//
    /** 주문 취소 */
    public void cancel() {
        getItem().addStock(count);
    }
 
    //==조회 로직==//
    /** 주문상품 전체 가격 조회 */
    public int getTotalPrice() {
        return getOrderPrice() * getCount();
    }
 
    // ...
}
 
cs

line 8) 주문 상품 번호는 기본 키

line 13) 주문 상품 정보는 외래키로 

line 18) 주문 정보도 외래키로

line 20) 주문 금액

line 21) 주문 수량

line 24~33) 주문 상품이 생기면 상품 정보, 가격, 수량 정보를 사용하여 주문 상품 엔티티 생성 후 재고에서 차감

line 37~39) 주문 취소 시 취소한 주문량만큼 재고를 증가


item/Item.java (상품 엔티티)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
 
    @Id
    @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;
 
    private String name;        //이름
    private int price;          //가격
    private int stockQuantity;  //재고수량
 
    @ManyToMany(mappedBy = "items")
    private List<Category> categories = new ArrayList<Category>();
 
    //==Biz Method==//
    public void addStock(int quantity) {
        this.stockQuantity += quantity;
    }
 
    public void removeStock(int quantity) {
        int restStock = this.stockQuantity - quantity;
        if (restStock < 0) {
            throw new NotEnoughStockException("need more stock");
        }
        this.stockQuantity = restStock;
    }
 
 
    // ..
}
 
cs

line 2) 상속 매핑을 부모 클래스에 선언 (단일 테이블 전략)

lien 3) 부모 클래스에 구분 컬럼 지정

line 9) 상품 ID는 기본 키

line 11~13) 상품 이름, 가격, 재고

line 16) 상품의 카테고리 정보

line 19~21) 재고가 증가하거나 상품 주문을 취소했을 경우

line 23~29) 상품 주문 시 재고 차감 (재고가 부족하면 예외 발생)


item/Book.java (상품-도서 엔티티)

1
2
3
4
5
6
7
8
9
@Entity
@DiscriminatorValue("B")
public class Book extends Item {
 
    private String author;
    private String isbn;
 
    // ..
}
cs

line 2) 엔티티 이름 저장


item/Album.java (상품-음반 엔티티)

1
2
3
4
5
6
7
8
9
@Entity
@DiscriminatorValue("A")
public class Album extends Item {
 
    private String artist;
    private String etc;
 
    // ..
}
cs


item/Movie.java (상품-영화 엔티티)

1
2
3
4
5
6
7
8
9
@Entity
@DiscriminatorValue("M")
public class Movie extends Item {
 
    private String director;
    private String actor;
 
    // ..
}
cs


/Delivery.java (배송 엔티티)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Entity
public class Delivery {
 
    @Id
    @GeneratedValue
    @Column(name = "DELIVERY_ID")
    private Long id;
 
    @OneToOne(mappedBy = "delivery")
    private Order order;
 
    @Embedded
    private Address address;
 
    @Enumerated(EnumType.STRING)
    private DeliveryStatus status; //ENUM [READY(준비), COMP(배송)]
 
    public Delivery() {}
 
    public Delivery(Address address) {
        this.address = address;
        this.status = DeliveryStatus.READY;
    }
 
    // ..
}
 
cs

line 10) 주문과 배송은 일대일 관계

line 16) 주문 상태는 열거형을 사용(준비 or 배송)

line 20~22) 주문 시 하나의 배송 정보를 생성


/Category.java (카테고리 엔티티)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Entity
public class Category {
 
    @Id @GeneratedValue
    @Column(name = "CATEGORY_ID")
    private Long id;
 
    private String name;
 
    @ManyToMany
    @JoinTable(name = "CATEGORY_ITEM",
            joinColumns = @JoinColumn(name = "CATEGORY_ID"),
            inverseJoinColumns = @JoinColumn(name = "ITEM_ID"))
    private List<Item> items = new ArrayList<Item>();
 
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "PARENT_ID")
    private Category parent;
 
    @OneToMany(mappedBy = "parent")
    private List<Category> child = new ArrayList<Category>();
 
    //==연관관계 메서드==//
    public void addChildCategory(Category child) {
        this.child.add(child);
        child.setParent(this);
    }
 
    public void addItem(Item item) {
        items.add(item);
    }
 
    // ..
}
cs

line 14) 상품과 다대다 관계

name : 연결 테이블

joinColumns : 현재 방향인 카테고리와 매핑할 조인 컬럼 정보

inverseJoinColumns : 반대 방향인 아이템과 매핑할 조인 컬럼 정보 저장


/Address.java (주소 값 타입)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@Embeddable
public class Address {
 
    private String city;
    private String street;
    private String zipcode;
 
    public Address() {
    }
 
    public Address(String city, String street, String zipcode) {
        this.city = city;
        this.street = street;
        this.zipcode = zipcode;
    }
 
    // Getter, Setter ..
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Address)) return false;
 
        Address address = (Address) o;
 
        if (city != null ? !city.equals(address.city) : address.city != nullreturn false;
        if (street != null ? !street.equals(address.street) : address.street != nullreturn false;
        if (zipcode != null ? !zipcode.equals(address.zipcode) : address.zipcode != nullreturn false;
 
        return true;
    }
 
    @Override
    public int hashCode() {
        int result = city != null ? city.hashCode() : 0;
        result = 31 * result + (street != null ? street.hashCode() : 0);
        result = 31 * result + (zipcode != null ? zipcode.hashCode() : 0);
        return result;
    }
}
cs

line 1) 임베디드 타입 구현


etc.

/DeliveryStatus.java

1
2
3
public enum DeliveryStatus {
    READY, COMP
}
cs


/OrderSearch.java

1
2
3
4
5
6
7
public class OrderSearch {
 
    private String memberName;      //회원 이름
    private OrderStatus orderStatus;//주문 상태
 
    // Getter, Setter ..
}
cs


/OrderStatus.java

1
2
3
4
5
public enum OrderStatus {
 
    ORDER, CANCEL
 
}
cs



|| 애플리케이션 기능 구현


--

Next Post : [Spring + JPA] Make Web Application (2)






출처 : 자바 ORM 표준 JPA 프로그래밍

https://github.com/holyeye/jpabook/tree/master/ch11-jpa-shop

반응형

'Books' 카테고리의 다른 글

[Spring + JPA] Spring Data JPA 란? (1)  (2) 2020.12.24
[Spring + JPA] Make Web Application (2)  (0) 2020.12.24
[JPA] 벌크 연산이란?  (3) 2020.12.23
[JPA] 네이티브 SQL 정리  (0) 2020.12.23
[JPA] QueryDSL 정리  (0) 2020.12.22
댓글
최근에 올라온 글
최근에 달린 댓글
링크
Total
Today
Yesterday