/api/v1/simple-orders에서 무한루프가 발생하지 않고 NullPointerException이 발생합니다.
List all = orderRepository.findAllByString(new OrderSearch());30번째 줄은 위와 같습니다.디버깅을 했더니 all이 null로 나오는 것 같습니다.강사님께서 올려주신 전체 코드와 몇 번을 비교해봐도 도저히 어느 부분에서 잘못된 것인지 찾지 못하였습니다.. 코드를 올리겠습니다.package jpabook.jpashop.api; import jpabook.jpashop.domain.Order; import jpabook.jpashop.repository.*; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** xToOne (ManyToOne, OneToOne) * Order * Order -> Member * Order -> Delivery */ @RestController @RequiredArgsConstructor public class OrderSimpleApiController { private OrderRepository orderRepository; @GetMapping("/api/v1/simple-orders") public List ordersV1() { List all = orderRepository.findAllByString(new OrderSearch()); return all; } }package jpabook.jpashop.domain.item; import jpabook.jpashop.domain.Category; import jpabook.jpashop.exception.NotEnoughStockException; import lombok.Getter; import lombok.Setter; import org.hibernate.annotations.BatchSize; import javax.persistence.*; import java.util.ArrayList; import java.util.List; @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "dtype") @Getter @Setter 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 categories = new ArrayList(); //==비즈니스 로직==// /** * stock 증가 */ public void addStock(int quantity) { this.stockQuantity += quantity; } /** * stock 감소 */ public void removeStock(int quantity) { int restStock = this.stockQuantity - quantity; if (restStock package jpabook.jpashop.domain; import lombok.Getter; import javax.persistence.Embeddable; @Embeddable @Getter public class Address { private String city; private String street; private String zipcode; protected Address() { } public Address(String city, String street, String zipcode) { this.city = city; this.street = street; this.zipcode = zipcode; } }package jpabook.jpashop.domain; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.Setter; import javax.persistence.*; import static javax.persistence.FetchType.*; @Entity @Getter @Setter public class Delivery { @Id @GeneratedValue @Column(name = "delivery_id") private Long id; //@JsonIgnore @OneToOne(mappedBy = "delivery", fetch = LAZY) private Order order; @Embedded private Address address; @Enumerated(EnumType.STRING) private DeliveryStatus status; //READY, COMP }package jpabook.jpashop.domain; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.Setter; import javax.persistence.*; import java.util.ArrayList; import java.util.List; @Entity @Getter @Setter public class Member { @Id @GeneratedValue @Column(name = "member_id") private Long id; private String name; @JsonIgnore @Embedded private Address address; //@JsonIgnore @OneToMany(mappedBy = "member") private List orders = new ArrayList(); }package jpabook.jpashop.domain; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import javax.persistence.*; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import static javax.persistence.FetchType.*; @Entity @Table(name = "orders") @Getter @Setter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Order { @Id @GeneratedValue @Column(name = "order_id") private Long id; @ManyToOne(fetch = LAZY) @JoinColumn(name = "member_id") private Member member; @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) private List orderItems = new ArrayList(); @OneToOne(fetch = LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "delivery_id") private Delivery delivery; private LocalDateTime orderDate; //주문시간 @Enumerated(EnumType.STRING) private OrderStatus status; //주문상태 [ORDER, CANCEL] //==연관관계 메서드==// 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); } //==생성 메서드==// 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(LocalDateTime.now()); return order; } //==비즈니스 로직==// /** * 주문 취소 */ public void cancel() { if (delivery.getStatus() == DeliveryStatus.COMP) { throw new IllegalStateException("이미 배송완료된 상품은 취소가 불가능합니다."); } 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; } }package jpabook.jpashop.domain; import com.fasterxml.jackson.annotation.JsonIgnore; import jpabook.jpashop.domain.item.Item; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import javax.persistence.*; import static javax.persistence.FetchType.*; @Entity @Getter @Setter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class OrderItem { @Id @GeneratedValue @Column(name = "order_item_id") private Long id; @ManyToOne(fetch = LAZY) @JoinColumn(name = "item_id") private Item item; //@JsonIgnore @ManyToOne(fetch = 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(); } }package jpabook.jpashop.repository; import jpabook.jpashop.domain.item.Item; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import javax.persistence.EntityManager; import java.util.List; @Repository @RequiredArgsConstructor public class ItemRepository { private final EntityManager em; public void save(Item item) { if (item.getId()==null){ // id가 없음 -> 새로 등록 em.persist(item); } else { em.merge(item); } } public Item findOne(Long id) { return em.find(Item.class, id); } public List findAll() { return em.createQuery("select i from Item i", Item.class) .getResultList(); } }package jpabook.jpashop.repository; import jpabook.jpashop.domain.Member; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import java.util.List; @Repository @RequiredArgsConstructor public class MemberRepository { private final EntityManager em; public void save(Member member) { em.persist(member); } public Member findOne(Long id) { return em.find(Member.class, id); } public List findAll() { return em.createQuery("select m from Member m", Member.class) .getResultList(); } public List findByName(String name) { return em.createQuery("select m from Member m where m.name=:name", Member.class) .setParameter("name", name) .getResultList(); } }package jpabook.jpashop.repository; import jpabook.jpashop.domain.Order; import org.springframework.stereotype.Repository; import org.springframework.util.StringUtils; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import javax.persistence.criteria.*; import java.util.ArrayList; import java.util.List; @Repository public class OrderRepository { private final EntityManager em; public OrderRepository(EntityManager em) { this.em = em; } public void save(Order order) { em.persist(order); } public Order findOne(Long id) { return em.find(Order.class, id); } public List findAll() { return em.createQuery("select o from Order o", Order.class) .getResultList(); } public List findAllByString(OrderSearch orderSearch) { String jpql = "select o from Order o join o.member m"; boolean isFirstCondition = true; //주문 상태 검색 if (orderSearch.getOrderStatus() != null) { if (isFirstCondition) { jpql += " where"; isFirstCondition = false; } else { jpql += " and"; } jpql += " o.status = :status"; } //회원 이름 검색 if (StringUtils.hasText(orderSearch.getMemberName())) { if (isFirstCondition) { jpql += " where"; isFirstCondition = false; } else { jpql += " and"; } jpql += " m.name like :name"; } TypedQuery query = em.createQuery(jpql, Order.class) .setMaxResults(1000); if (orderSearch.getOrderStatus() != null) { query = query.setParameter("status", orderSearch.getOrderStatus()); } if (StringUtils.hasText(orderSearch.getMemberName())) { query = query.setParameter("name", orderSearch.getMemberName()); } return query.getResultList(); } /** * JPA Criteria */ public List findAllByCriteria(OrderSearch orderSearch) { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery cq = cb.createQuery(Order.class); Root o = cq.from(Order.class); Join m = o.join("member", JoinType.INNER); List criteria = new ArrayList(); //주문 상태 검색 if (orderSearch.getOrderStatus() != null) { Predicate status = cb.equal(o.get("status"), orderSearch.getOrderStatus()); criteria.add(status); } //회원 이름 검색 if (StringUtils.hasText(orderSearch.getMemberName())) { Predicate name = cb.like(m.get("name"), "%" + orderSearch.getMemberName() + "%"); criteria.add(name); } cq.where(cb.and(criteria.toArray(new Predicate[criteria.size()]))); TypedQuery query = em.createQuery(cq).setMaxResults(1000); return query.getResultList(); } } package jpabook.jpashop.service; import jpabook.jpashop.domain.*; import jpabook.jpashop.domain.item.Item; import jpabook.jpashop.repository.ItemRepository; import jpabook.jpashop.repository.MemberRepository; import jpabook.jpashop.repository.OrderRepository; import jpabook.jpashop.repository.OrderSearch; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service @Transactional(readOnly = true) @RequiredArgsConstructor public class OrderService { private final OrderRepository orderRepository; private final MemberRepository memberRepository; private final ItemRepository itemRepository; /** * 주문 */ @Transactional public Long order(Long memberId, Long itemId, int count) { //엔티티 조회 Member member = memberRepository.findOne(memberId); Item item = itemRepository.findOne(itemId); //배송정보 생성 Delivery delivery = new Delivery(); delivery.setAddress(member.getAddress()); delivery.setStatus(DeliveryStatus.READY); //주문상품 생성 OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count); //주문 생성 Order order = Order.createOrder(member, delivery, orderItem); //주문 저장 orderRepository.save(order); return order.getId(); } /** * 주문 취소 */ @Transactional public void cancelOrder(Long orderId) { //주문 엔티티 조회 Order order = orderRepository.findOne(orderId); //주문 취소 order.cancel(); } //검색 public List findOrders(OrderSearch orderSearch) { return orderRepository.findAllByString(orderSearch); } }package jpabook.jpashop; import jpabook.jpashop.domain.*; import jpabook.jpashop.domain.item.Book; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import javax.annotation.PostConstruct; import javax.persistence.EntityManager; /** * 종 주문 2개 * * userA * * JPA1 BOOK * * JPA2 BOOK * * userB * * SPRING1 BOOK * * SPRING2 BOOK */ @Component @RequiredArgsConstructor public class InitDb { private final InitService initService; @PostConstruct public void init() { initService.dbInit1(); initService.dbInit2(); } @Component @Transactional @RequiredArgsConstructor static class InitService { private final EntityManager em; public void dbInit1() { System.out.println("Init1" + this.getClass()); Member member = createMember("userA", "서울", "1", "1111"); em.persist(member); Book book1 = createBook("JPA1 BOOK", 10000, 100); em.persist(book1); Book book2 = createBook("JPA2 BOOK", 20000, 100); em.persist(book2); OrderItem orderItem1 = OrderItem.createOrderItem(book1, 10000, 1); OrderItem orderItem2 = OrderItem.createOrderItem(book2, 20000, 2); Delivery delivery = createDelivery(member); Order order = Order.createOrder(member, delivery, orderItem1, orderItem2); em.persist(order); } public void dbInit2() { Member member = createMember("userB", "진주", "2", "2222"); em.persist(member); Book book1 = createBook("SPRING1 BOOK", 20000, 200); em.persist(book1); Book book2 = createBook("SPRING2 BOOK", 40000, 300); em.persist(book2); OrderItem orderItem1 = OrderItem.createOrderItem(book1, 20000, 3); OrderItem orderItem2 = OrderItem.createOrderItem(book2, 40000, 4); Delivery delivery = createDelivery(member); Order order = Order.createOrder(member, delivery, orderItem1, orderItem2); em.persist(order); } private Member createMember(String name, String city, String street, String zipcode) { Member member = new Member(); member.setName(name); member.setAddress(new Address(city, street, zipcode)); return member; } private Book createBook(String name, int price, int stockQuantity) { Book book1 = new Book(); book1.setName(name); book1.setPrice(price); book1.setStockQuantity(stockQuantity); return book1; } private Delivery createDelivery(Member member) { Delivery delivery = new Delivery(); delivery.setAddress(member.getAddress()); return delivery; } } } package jpabook.jpashop; import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class JpaShopApplication { public static void main(String[] args) { SpringApplication.run(JpaShopApplication.class, args); } }