Spring BootとJPA 1-TIL(3)を使用


[参考講座]実戦!Spring BootとJPAによる1-Webアプリケーションの開発

💡 メンバードメインの開発


実装機能

  • 会員登録
  • 照会
  • 会員目録
  • オーダー

  • メンバーエンティティコード再試行
  • 会員庫開発
  • 会員サービス開発
  • 会員機能テスト
  • ▼▼会員repository開発


    会員ライブラリコード

    @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<Member> findAll() {
            List<Member> result = em.createQuery("select m from Member m", Member.class)
                    .getResultList();
    
            return result;
        }
    
        public List<Member> findByName(String name) {
            return em.createQuery("select m from Member m where m.name = :name", Member.class)
                    .setParameter("name", name)
                    .getResultList();
        }
    }

    技術説明

  • @Repository:スプリングライブラリに登録し、JPA異常をスプリングベース異常
  • に変換する.
  • @PersistenceContext:エンティティーマネージャインジェクション
  • @PersistenceUnit:エンティティーマネージャファクトリ注入
  • ▼▼▼会員サービス開発


    会員サービスコード

    @Service
    @Transactional(readOnly = true)
    @RequiredArgsConstructor
    public class MemberService {
    
        private final MemberRepository memberRepository;
    
        //회원 가입
        @Transactional
        public Long join(Member member) {
            validateDuplicateMember(member); //중복 회원 검
            memberRepository.save(member);
            return member.getId();
        }
    
        private void validateDuplicateMember(Member member) {
            //EXCEPTION
            List<Member> findMembers = memberRepository.findByName(member.getName());
            if (!findMembers.isEmpty()) {
                throw new IllegalStateException("이미 존재하는 회원입니다.");
            }
        }
        //회원 전체 조회
        public List<Member> findMembers() {
            return memberRepository.findAll();
        }
    
        public Member findOne(Long memberId){
            return memberRepository.findOne(memberId);
        }
    }

    技術説明

  • @Service
  • @Transactional:トランザクション、永続性コンテキスト
  • 読取り専用=true:データを変更しない読取り専用メソッドです.永続性コンテキストをリフレッシュしないため、パフォーマンスはわずかに向上します(読取り専用)
  • .
    データベースドライバが
  • をサポートする場合、データベースのパフォーマンスは
  • 向上します.
  • @Autowired
  • コンストラクション関数Injectionを多く使用し、1つのコンストラクション関数は
  • を省略することができる.

    lombok

    @RequiredArgsConstructor
        public class MemberService {
              private final MemberRepository memberRepository;
    ... }
    finalキーワードをつかみ,生成者に注入させる.

    会員機能のテスト

  • 会員に加入することに成功します.
  • 会員に加入する場合、同じ名前があれば例外が発生するはずです.
  • @RunWith(SpringRunner.class)
    @SpringBootTest
    @Transactional
    public class MemberServiceTest {
    
        @Autowired MemberService memberService;
        @Autowired MemberRepository memberRepository;
        @Autowired EntityManager em;
    
        @Test
        public void 회원가입() throws Exception {
            //given
            Member member = new Member();
            member.setName("kim");
    
            //when
            Long savedId = memberService.join(member);
    
            //then
            assertEquals(member, memberRepository.findOne(savedId));
        }
    
        @Test(expected = IllegalStateException.class)
        public void 중복_회원_에외() throws Exception {
            //given
            Member member1 = new Member();
            member1.setName("kim");
    
            Member member2 = new Member();
            member2.setName("Kim");
    
            //when
            memberService.join(member1);
            memberService.join(member2); //예외가 발생해야 한다!!!
    
            //then
            fail("예외가 발생해야 한다.");
        }
    }
  • @SpringRunner.class:統合スプリングおよびテスト
  • @SpringBootTest:起動してテスト(ない場合は@Autowiredに失敗)
  • @Transactional:繰り返し可能なテストサポートで、テストを実行するたびにトランザクションが開始され、テスト終了時に
  • が強制的にロールバックされます.

    💡 商品ドメイン開発


    実装機能

  • 商品登録
  • 問い合わせ
  • カタログ
  • 商品修正
  • ▼商品実体開発(業務ロジック追加)


    商品実体コード
    @Entity
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(name = "dtype")
    @Getter
    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>();
    
        //==비즈니스 로직==//
        //stock 증가
        public void addStock(int quantity) {
            this.stockQuantity += quantity;
        }
    
        //stock 감소
        public void removeStock(int quantity) {
            int restStock = this.stockQuantity - quantity;
            if (restStock < 0) {
                throw new NotEnoughStockException("need more stock");
            }
            this.stockQuantity = restStock;
        }
    }
    

    ▼商品庫開発


    商品ライブラリコード
    @Repository
    @RequiredArgsConstructor
    public class ItemRepository {
    
        private final EntityManager em;
    
        public void save(Item item) {
            if(item.getId() == null) {
                em.persist(item);
            } else {
                em.merge(item);
            }
        }
    
        public Item findOne(Long id) {
            return em.find(Item.class, id);
        }
    
        public List<Item> findAll() {
            return em.createQuery("select i from Item i", Item.class)
                    .getResultList();
        }
    }
    idがない場合は、それを新しいものと見なしてpersist()を実行します.
    idがある場合、レポートはデータベースに格納されているエンティティを変更し、merge()を実行します.

    ▼▼▼商品サービス開発


    商品サービスコード
    @Service
    @Transactional(readOnly = true)
    @RequiredArgsConstructor
    public class ItemService {
    
        private final ItemRepository itemRepository;
    
        @Transactional
        public void saveItem(Item item) {
            itemRepository.save(item);
        }
    
        public List<Item> findItems() {
            return itemRepository.findAll();
        }
    
        public Item findOne(Long itemId) {
            return itemRepository.findOne(itemId);
        }
    }
    

    コアオーダー機能のコードを整理してみましょう~