프로젝트 설정
application.properties > apllcation.yaml 로 파일 변경
application.yaml
server:
port: 9090
spring:
datasource:
url: jdbc:oracle:thin:@localhost:1521:xe
username: springjpa
password: springjpa
driver-class-name: oracle.jdbc.driver.OracleDriver
jpa:
hibernate:
ddl-auto: create
properties:
hibernate:
show_sql: true
format_sql: true
logging.level:
org.hibernate.SQL: debug
org.hibernate.type: trace
서버 구동 확인
index.html
생성<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
Hello
<a href="/hello">hello</a>
</body>
</html>
Controller.java
생성@Controller
public class HelloController {
@GetMapping("hello")
public String hello(Model model) {
model.addAttribute("data", "hello!");
return "hello";
}
}
hello.html
생성<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<p th:text="'안녕하세요' + ${data}" ></p>
</body>
</html>
build.gradle
수정...
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-devtools'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.oracle.database.jdbc:ojdbc8'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
...
시작
domain 패키지 생성
Member.java
생성@Entity
@Getter @Setter
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>();
}
Address.java
생성@Embeddable
@Getter
public class Address {
private String city;
private String street;
private String zipcode;
public Address(String city, String street, String zipcode) {
super();
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
// JPA 스펙상 만들어 놓은 기본 생성자
// new를 통해서 생성하지 못하도록 한다.
protected Address() {}
}
Order.java
생성@Entity
@Getter @Setter
@Table(name = "orders")
public class Order {
@Id @GeneratedValue
@Column(name = "order_id")
private Long id;
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
private LocalDateTime orderDate;
@OneToMany(mappedBy = "order")
private List<OrderItem> orderItems = new ArrayList<OrderItem>();
// 주문상태 ( ORDRE , CANCEL ) -> Enum
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus;
// 연관관계 메서드
public void setMember(Member member) {
this.member = member;
member.getOrders().add(this);
}
public void addOrderItem(OrderItem orderItem) {
orderItems.add(orderItem);
orderItem.setOrder(this);
}
}
OrderStatus.java
enum 파일 생성public enum OrderStatus {
ORDER, CANCEL
}
OrderItem.java
생성@Entity
@Getter @Setter
public class OrderItem {
@Id @GeneratedValue
@Column(name = "order_item_id")
private Long id;
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
@ManyToOne
@JoinColumn(name = "item_id")
private Item item;
private int orderPrice;
private int count;
}
Item.java
생성@Entity
@Getter @Setter
public class Item {
@Id @GeneratedValue
@Column(name = "item_id")
private Long id;
private String name;
private int price;
private int stockQuantity;
}
코드는 하단에 링크 걸어 두었습니다.
작업 방향
- controller, web : 웹 계층
- service : 비즈니스 로직, 트랜잭션 처리
- repository : JPA를 직접 사용하는 계층, 엔티티 매니저 사용
- domain : 엔티티가 모여있는 계층, 모든 계층에서 사용 가능
HelloController.java
수정...
@GetMapping("/")
public String home() {
return "home";
}
}
-> 바로 Entity에 추가하지 않고 DTO를 추가해 데이터를 먼저 담아 준다.
MemberForm.java
생성@Getter @Setter
public class MemberForm {
private String name;
private String city;
private String street;
private String zipcode;
}
MemberController.java
생성@Controller
public class MemberController {
@GetMapping("/members/new")
public String createForm(Model model) {
model.addAttribute("memberForm", new MemberForm()); // html에서 thymeleaf를 사용하기 위해 빈 객체를 넘겨준다.
return "members/createMemberForm";
}
}
createMemberForm.html
생성 <form role="form" action="/members/new" th:object="${memberForm}" method="post">
<div class="form-group">
<label th:for="name">이름</label>
<input type="text" class="form-control" th:field="*{name}" placeholder="이름을 입력하세요" />
</div>
<div class="form-group">
<label th:for="city">도시</label>
<input type="text" class="form-control" th:field="*{city}" placeholder="도시를 입력하세요">
</div>
<div class="form-group">
<label th:for="street">거리</label>
<input type="text" class="form-control" th:field="*{street}" placeholder="거리를 입력하세요">
</div>
<div class="form-group">
<label th:for="zipcode">우편번호</label>
<input type="text" class="form-control" th:field="*{zipcode}" placeholder="우편번호를 입력하세요">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
...
implementation 'org.springframework.boot:spring-boot-devtools'
implementation 'org.springframework.boot:spring-boot-starter-validation'
compileOnly 'org.projectlombok:lombok'
...
MemberForm.java
수정...
@NotEmpty(message = "회원 이름은 필수입니다.")
private String name;
...
createMemberForm.html
수정...
<div class="form-group">
<label th:for="name">이름</label>
<input type="text" class="form-control" th:field="*{name}" placeholder="이름을 입력하세요"
th:class="${#fields.hasErrors('name')}? 'form-control fieldError' : 'form-control'"/>
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Incorrect data</p>
</div>
...
MemberController.java
수정 // BindingResult : @valid 다음에 파라미터로 Binding이 오면, error를 Binding에 담아준다.
@PostMapping("/members/new")
public String create(@Valid MemberForm form, BindingResult result) {
// error 발생시
if( result.hasErrors()) {
return "members/createMemberForm";
}
// 정상, service
return "redirect:/";
}
MemberRepository.java
생성@Repository
@RequiredArgsConstructor
public class MemberRepository {
// 기존에 JpaMain 등에서 사용했던 긴 문장들은 @RequiredArgsConstructor 와
// private final EntityManager em; 로 모두 대체 가능하다.
// @Autowired : spring boot lib 사용 시 @Autowired를 지원한다.
@Autowired
private final EntityManager em;
// 저장
public void save(Member member) {
em.persist(member);
}
// 1건 조회
public Member findOne( Long id) {
return em.find(Member.class, id);
}
// 여러건 조회
public List<Member> findAll(){
return em.createQuery("select m from Member m ", Member.class).getResultList();
}
// 이름으로 조회
public List<Member> findName(String name){
return em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name", name).getResultList();
}
}
Memberservice.java
생성@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
// 회원가입
@Transactional
public Long Join(Member member) {
memberRepository.save(member);
return member.getId();
}
}
MemberController.java
수정@Controller
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
...
// 정상, service
Address address = new Address(form.getCity(), form.getStreet(), form.getZipcode());
Member member = new Member();
member.setName(form.getName());
member.setAddress(address);
memberService.Join(member);
return "redirect:/";
}
...