아래는 엔티티를 JPA가 활용할 수 있게끔 설정한 엔티티이다. 애노테이션을 중점적으로 잘 봐보자!
@Data
@Entity
public class Item {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "item_name", length = 10)
    private String itemName;
    private Integer price;
    private Integer quantity;
    public Item() {
    }
    public Item(String itemName, Integer price, Integer quantity) {
        this.itemName = itemName;
        this.price = price;
        this.quantity = quantity;
    }
}
JPA는 public 혹은 protected의 기본생성자가 필수이다! JPA 규약이다! 이걸로 프록시나 여러가지 기능들을 소화하니 꼭 넣어주도록!
@Entity : JPA가 사용하는 객체라는 뜻. 이 애노테이션이 있어야 JPA가 인식할 수 있다. 엔티티.
@Id : 테이블의 PK와 해당 필드를 매핑한다.
@GeneratedValue(strategy = GenerationType.IDENTITY) : PK 생성 값을 데이터베이스에서 생성하는 IDENTITY 방식을 사용한다. 예) MySQL auto increment
@Column : 객체의 필드를 테이블의 컬럼과 매핑한다. 자바에서 낙타표기법을 많이 쓰지만, DB에선 언더바를 많이쓴다! 그래서 JPA가 쿼리문을 잘 작성할 수 있도록 해당 애노테이션을 통해서 컬럼명을 알려준 것이다!
스프링 부트가 객체필드의 낙타표기법을 테이블 컬럼의 언더스코어로 자동 변환해준다 itemName → item_name (하지만 명시적으로 표시해주는 것이 좋을듯)
length = 10 : JPA의 매핑정보로 DDL(create table)로 테이블 생성할 때, 그때 컬럼의 길이 값으로 활용된다. 위 같은 경우는 varchar(10) 으로!
아래는 JPA를 활용한 레포지토리 코드이다! EntityManager를 주입받아 다양하게 접근하고 있음을 볼 수 있다!
@Slf4j
@Repository
@Transactional
public class JpaItemRepository implements ItemRepository {
    private final EntityManager em;
    public JpaItemRepository(EntityManager em) {
        this.em = em;
    }
    @Override
    public Item save(Item item) {
        em.persist(item);
        return item;
    }
    @Override
    public void update(Long itemId, ItemUpdateDto updateParam) {
        Item findItem = em.find(Item.class, itemId);
        findItem.setItemName(updateParam.getItemName());
        findItem.setPrice(updateParam.getPrice());
        findItem.setQuantity(updateParam.getQuantity());
    }
    @Override
    public Optional<Item> findById(Long id) {
        Item item = em.find(Item.class, id);
        return Optional.ofNullable(item);
    }
    @Override
    public List<Item> findAll(ItemSearchCond cond) {
        String jpql = "selectxxx i from Item i";
        Integer maxPrice = cond.getMaxPrice();
        String itemName = cond.getItemName();
        if (StringUtils.hasText(itemName) || maxPrice != null) {
            jpql += " where";
        }
        boolean andFlag = false;
        List<Object> param = new ArrayList<>();
        if (StringUtils.hasText(itemName)) {
            jpql += " i.itemName like concat('%',:itemName,'%')";
            param.add(itemName);
            andFlag = true;
        }
        if (maxPrice != null) {
            if (andFlag) {
                jpql += " and";
            }
            jpql += " i.price <= :maxPrice";
            param.add(maxPrice);
        }
        log.info("jpql={}", jpql);
        TypedQuery<Item> query = em.createQuery(jpql, Item.class);
        if (StringUtils.hasText(itemName)) {
            query.setParameter("itemName", itemName);
        }
        if (maxPrice != null) {
            query.setParameter("maxPrice", maxPrice);
        }
        return query.getResultList();
    }
}
보면 마찬가지로 동적쿼리는 코드가 매우 길어지는 것을 확인할 수 있다….
EntityManager는 JPA 고유, 핵심 API 창구!
private final EntityManager em : 생성자를 보면 스프링을 통해 엔티티 매니저(EntityManager )
라는 것을 주입받은 것을 확인할 수 있다. JPA의 모든 동작은 엔티티 매니저를 통해서 이루어진다. 엔티티 매니저
는 내부에 데이터소스를 가지고 있고, 데이터베이스에 접근할 수 있다.