//Member.java
package com.sparta.springjpa.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Entity //1. Entity를 사용하겠다는 표시를 먼저 달기
@Getter //2. 값을 가져올 때 사용
@NoArgsConstructor //3. 기본생성자를 만들어줌
public class Member { //user는 사용 불가. H2 DB가 2.0버전 되면서 user라고 하면 테이블 생성이 안됨 (대신에 Member 테이블을 사용)
@Id //4.
@GeneratedValue(strategy = GenerationType.IDENTITY) //5.
private Long id; //6.
@Column(nullable = false) //8. nullable = false 가 반드시 들어갈 수 있도록 추가
private String memberName; //7. Member에는 멤버이름이 필요하므로, memberName 추가
@OneToMany(mappedBy = "member", fetch = FetchType.EAGER) //9.
private List<Orders> orders = new ArrayList<>(); //10.
public Member(String memberName) { //11. 생성자 추가
this.memberName = memberName;
}
}
//Food.java
package com.sparta.springjpa.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Entity //1. Entity를 사용하겠다는 표시를 먼저 달기
@Getter //2. 값을 가져올 때 사용
@NoArgsConstructor //3. 기본생성자를 만들어줌
public class Food {
@Id //4.
@GeneratedValue(strategy = GenerationType.IDENTITY) //5.
private Long id; //6.
@Column(nullable = false) //8. nullable = false 가 반드시 들어갈 수 있도록 추가
private String foodName; //7. Member에는 멤버이름이 필요하므로, memberName 추가
@Column(nullable = false) //13. 음식 가격 설정을 위해 추가
private int price;
//9. Order.java에서 @ManyToMany 를 사용했으므로, 이걸 받아줄 부분이 필요 --> 음식 1개에 주문이 N개가 될 수 있으므로
//11. mappedBy를 사용해서, food와 연관관계를 만듦 (연관관계의 주인을 지정해주는 것) --> Order.java에서 @JoinColumn(name = "food_id")에서 food_id의 주인이 누구인지 알려주는 부분
//12. FetchType 은??
@OneToMany(mappedBy = "food", fetch = FetchType.EAGER)
private List<Orders> orders = new ArrayList<>(); //10. 여러가지의 order가 들어와야하므로 리스트 형식으로 받음. 초기값을 주기위해 new ArrayList<>() 로 설정
public Food(String foodName, int price) { //14. foodName과 price를 받아주는 생성자 추가
this.foodName = foodName;
this.price = price;
}
}
//Orders.java
package com.sparta.springjpa.entity;
//@Id에 따라 import org.springframework.data.annotation.Id; 로 했을 때, Error creating bean with name 'entityManagerFactory' defined in class path resource 오류 발생
//--> Entity Id 참조가 잘못돼서 발생하는 오류 --> import javax.persistence.Id; 로 변경 후, 정상 작동
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter //1. Entity를 사용하겠다는 표시를 먼저 달기
@Entity //2. 값을 가져올 때 사용
@NoArgsConstructor //3. 기본생성자를 만들어줌
public class Orders { //Order는 예약어이기때문에, 부득이하게 Orders로 클래스명 설정
@Id //4.
@GeneratedValue(strategy = GenerationType.IDENTITY) //5.
private Long id; //6.
@ManyToOne //7. Food의 ID, Member의 ID를 받아야하므로
@JoinColumn(name = "food_id") //8. 'Order 테이블의 음식 ID와 Food 테이블의 ID가 JOIN 된다.' 라는 의미 --> 주인은 Food 테이블이다!
private Food food; //7.
@ManyToOne //9. member 도 food와 똑같이 설정해줌
@JoinColumn(name = "member_id")
private Member member;
public Orders(Food food, Member member) { //10. order을 만들때 Food와 Member를 추가해줘야하므로, Food와 Member를 함께 넣어주는 생성자 추가
this.food = food;
this.member = member;
}
}
//MemberRepository.java
//entity 패키지의 Food, Member, Orders 엔티티 객체를 데이터베이스와 연결해주기 위해 만듦
package com.sparta.springjpa.repository;
import com.sparta.springjpa.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MemberRepository extends JpaRepository<Member, Long> { //1.
}
//FoodRepository.java
//entity 패키지의 Food, Member, Orders 엔티티 객체를 데이터베이스와 연결해주기 위해 만듦
package com.sparta.springjpa.repository;
import com.sparta.springjpa.entity.Food; //<Food, Long>에서 import 자동 추가 안되면 Alt + Enter 눌러서 수동으로 추가하기!
import org.springframework.data.jpa.repository.JpaRepository;
//Java Class - interface로 만듦
//1. JpaRepository의 상속을 받는 FoodRepository interface를 만듦(extends JpaRepository 를 추가)
//1. <Food, Long>: 어떤 테이블과 연결할지 명시 --> '이 JpaRepository는 FoodRepository와 연결하겠다.' 는 의미
//1. ID를 Long 타입으로 지정했으므로, 여기서도 Long 타입으로 주면 됨
public interface FoodRepository extends JpaRepository<Food, Long> {
}
//OrdersRepository.java
//entity 패키지의 Food, Member, Orders 엔티티 객체를 데이터베이스와 연결해주기 위해 만듦
package com.sparta.springjpa.repository;
import com.sparta.springjpa.entity.Orders;
import org.springframework.data.jpa.repository.JpaRepository;
public interface OrdersRepository extends JpaRepository<Orders, Long> { //1.
}
//Restaurant.java
//앞서 만든 것들이 잘 사용되는지 확인하기 위한 Java class
package com.sparta.springjpa;
import com.sparta.springjpa.entity.Food;
import com.sparta.springjpa.entity.Member;
import com.sparta.springjpa.entity.Orders;
import com.sparta.springjpa.repository.FoodRepository;
import com.sparta.springjpa.repository.MemberRepository;
import com.sparta.springjpa.repository.OrdersRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
@RequiredArgsConstructor
public class Restaurant implements ApplicationRunner {
private final FoodRepository foodRepository;
private final OrdersRepository ordersRepository;
private final MemberRepository memberRepository;
@Override
public void run(ApplicationArguments args) throws Exception { //1. 스프링부트가 시작 될때, run 메소드가 실행됨
List<Food> foods = new ArrayList<>();
Food food1 = new Food("후라이드", 10000);
foods.add(food1);
Food food2 = new Food("양념치킨", 12000);
foods.add(food2);
Food food3 = new Food("반반치킨", 13000);
foods.add(food3);
Food food4 = new Food("고구마피자", 9000);
foods.add(food4);
Food food5 = new Food("아보카도피자", 110000);
foods.add(food5);
foodRepository.saveAll(foods); //foodRepository에 saveAll를 사용해서, List 안에 있는 Food들을 한번에 저장
List<Member> members = new ArrayList<>();
Member member1 = new Member("삼식이");
members.add(member1);
Member member2 = new Member("먹깨비");
members.add(member2);
memberRepository.saveAll(members);
System.out.println("==================================================================");
System.out.println("Member 데이터");
List<Member> findMembers = memberRepository.findAll(); //memberRepository와 연결하고, findAll() 메소드를 사용해서 테이블에 있는 Member들을 List 형식으로 받아와서 사용
for (Member findMember : findMembers) {
System.out.println("findMember = " + findMember.getMemberName());
}
System.out.println("==================================================================");
System.out.println("Food 데이터");
List<Food> findFoods = foodRepository.findAll(); //foodRepository와 연결하고, findAll() 메소드를 사용해서 테이블에 있는 Food들을 List 형식으로 받아와서 사용
for (Food findFood : findFoods) {
System.out.println("findFood = " + findFood.getFoodName());
}
List<Orders> ordersList = new ArrayList<>();
Orders orders1 = new Orders(findFoods.get(0), findMembers.get(0)); //orders를 만들 때, Food 객체와 Member 객체를 넣어줌 --> Orders.java에서 public Orders(Food food, Member member) 를 보면 확인 가능
ordersList.add(orders1);
Orders orders2 = new Orders(findFoods.get(3), findMembers.get(1));
ordersList.add(orders2);
Orders orders3 = new Orders(findFoods.get(4), findMembers.get(1));
ordersList.add(orders3);
Orders orders4 = new Orders(findFoods.get(2), findMembers.get(0));
ordersList.add(orders4);
Orders orders5 = new Orders(findFoods.get(2), findMembers.get(0));
ordersList.add(orders5);
Orders orders6 = new Orders(findFoods.get(1), findMembers.get(1));
ordersList.add(orders6);
Orders orders7 = new Orders(findFoods.get(1), findMembers.get(0));
ordersList.add(orders7);
Orders orders8 = new Orders(findFoods.get(3), findMembers.get(1));
ordersList.add(orders8);
ordersRepository.saveAll(ordersList); //위에서 findAll() 메소드로 가져온 Member들과 Food들을 인덱스(리스트형식이므로. 임의로 지정한 순서대로 오더를 만든.)를 통해,
//saveAll() 메소드를 사용해서 데이터베이스에 Insert한다
System.out.println("==================================================================");
int num = 1;
System.out.println("Orders 데이터"); //Orders 테이블에 데이터들이 어떻게 들어가있는지 확인하는 코드
List<Orders> orderList = ordersRepository.findAll();
for (Orders orders : orderList) {
System.out.println(num);
System.out.println("주문한 사람 = " + orders.getMember().getMemberName());
System.out.println("주문한 음식 = " + orders.getFood().getFoodName());
num++;
}
System.out.println("==================================================================");
System.out.println("삼식이 주문한 음식"); //삼식이라는 member가 어떤 음식을 주문했는지 확인하는 코드
Member samsik = memberRepository.findById(1L).orElseThrow(
()->new RuntimeException("없음") //예외 처리
);
num = 1;
for (Orders orders : samsik.getOrders()) {
System.out.println(num);
System.out.println("주문한 음식 = " + orders.getFood().getFoodName());
System.out.println("주문한 음식 가격 = " + orders.getFood().getPrice());
num++;
}
System.out.println("==================================================================");
System.out.println("아보카도피자 주문한 사람"); //음식 中 아보카도피자를 주문한 사람이 누구인지 찾는 코드
Food abocado = foodRepository.findById(5L).orElseThrow(
()->new RuntimeException("없음") //예외 처리
);
for (Orders order : abocado.getOrders()) {
System.out.println("주문한 사람 = " + order.getMember().getMemberName());
}
}
}
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class Timestamped { //1. Timestamped라는 클래스를 만들고
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt; //2. Timestamped 클래스 안에 createdAt 컬럼, modifiedAt 컬럼을 만들어서
@LastModifiedDate
@Column
private LocalDateTime modifiedAt;
}
@Entity // 게시글
public class Post extends Timestamped { //3. Timestamped 클래스를 Post 클래스라는 Entity에 상속함(extends Timestamped)
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false, unique = true)
private String content;
}
1 ~ 3
Post 클래스는 실제로는 reatedAt, modifiedAt이 없지만, Timestamped를 상속하는 것만으로도 데이터베이스에 추가될 때 자동으로 추가되는 시간이 설정됨
그 외에 @ 어노테이션들
위의 기능들을 사용하기 위해 달아주는 것들
@SpringBootApplication이 있는 class에 @EnableJpaAuditing 추가!
- @EnableJpaAuditing 어노테이션
- Spring Audit 기능을 활용하기 위한 것
- Audit: Spring Data JPA에서 시간에 대해서 자동으로 값을 넣어주는 기능
상품 Enitity 선언
@Entity
public class Product extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Long userId;
private String title;
private String image;
private String link;
private int lprice;
private int myprice;
}
Spring Data JPA) 상품 Repository 생성
public interface ProductRepository extends JpaRepository<Product, Long> {
}
Spring Data JPA) 기본 제공해 주는 기능
// 1. 상품 생성
Product product = new Product(...);
productRepository.save(product); //save()는 하나의 객체만 저장할 때 사용
// 2. 상품 전체 조회
List<Product> products = productRepository.findAll();
// 3. 상품 전체 개수 조회
long count = productRepository.count();
// 4. 상품 삭제
productRepository.delete(product);
ID 외의 필드에 대한 추가 기능은 interface 만 선언해 주면, 구현은 Spring Data JPA 가 대신!!
public interface ProductRepository extends JpaRepository<Product, Long> {
// (1) 회원 ID 로 등록된 상품들 조회
List<Product> findAllByUserId(Long userId);
// (2) 상품명이 title 인 관심상품 1개 조회
Product findByTitle(String title);
// (3) 상품명에 word 가 포함된 모든 상품들 조회
List<Product> findAllByTitleContaining(String word);
// (4) 최저가가 fromPrice ~ toPrice 인 모든 상품들을 조회
List<Product> findAllByLpriceBetween(int fromPrice, int toPrice);
}
위에서 했던 걸 이용해보는데, 내가 어떤 멤버를 찾고 싶다면?
ApplicationRunner(Restaurant.java)에서 삼식이 같은 이름을 통해서 조회해보려고 하는데, findById() 만 있고 별로 적당한 게 안 보였음
MemberRepository에서 아래 코드 추가
Optional<Member> findByMemberName(String memberName);
다시 ApplicationRunner에서 아래 코드 추가
Member member = memberRepository.findByMemberName("삼식이").orElseThrow(
() -> new RuntimeException("삼식이 없음")
);
System.out.println("member.getMemberName() = " + member.getMemberName());
System.out.println("member.getId() = " + member.getId());
RUN 돌려보면, 추가돼있음!
Spring Data JPA 의 Query Methods(Spring Data JPA 추가기능 구현방법): https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods