본문 바로가기

개발/Backend

[Spring Boot] 예제로 배우는 스프링부트 입문 | Spring Data JPA

Spring Data JPA 기초

application.properties에 다음 설정 추가 (xml에 들어있던 오라클 접속에 대한 내용)

spring.jpa.database-platform=org.hibernate.dialect.OracleDialect
spring.jpa.open-in-view=false
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
# none, create, create-drop, update, validate
spring.jpa.properties.hibernate.hbm2ddl.auto=create //none으로 해야 데이터 안지워짐

 

엔티티를 만들자

Member.java

package com.study.springboot.jpa;

import java.time.LocalDate;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@Entity(name="JPAMEMBER01")
public class Member 
{
    @Id
    @GeneratedValue
    private Long id;
    private String username;
	@Column(name="create_date")
	private LocalDate createDate;
}
  • lombok => 엔티티 만드는 코드를 간결하게 
    • @Getter
    • @AllArgsConstructor : 모든 컬럼을 이용해서 파라미터를 받는 생성자 만듬 
    • @NoArgsConstructor : default 생성자 생성
    • @Builder : 필요한 값만 이용해서 멤버 개체 생성 

MemberRepository.java

package com.study.springboot.jpa;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> 
{
    // 제네릭타입 : long 이 아니라 Long으로 작성.
    // 기본적인 Create, Read, Update, Delete 자동으로 생성
}
  • jparepository를 상속한 memberRepository를 만들어 Repository로 등록 
  • 파라미터로 엔티티인 Member와 Member id의 데이터 타입(Long)을 두번째 파라미터로 넣음 
  • create, persist, read, find, update, delete, remove 다 자동으로 만들어짐

이 reposritory를 사용하는 service를 추가해보자

package com.study.springboot.jpa;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MemberService
{
    @Autowired
    private MemberRepository memberRepository;

    public Member insert(Member member) 
    {
    	Member returnMember = memberRepository.save(member);
    	return returnMember;
    }

    public Optional<Member> select(Long id) 
    {
        Optional<Member> member = memberRepository.findById(id);
        return member;
    }

    public List<Member> selectAll() 
    {
        return memberRepository.findAll();
    }

    public void delete(Long id) 
    {
        memberRepository.deleteById(id);
    }

    public Member update(Member member) 
    {
    	// insert 메서드에서 사용한 방법과 똑같다.
    	// 키 값과 같은 값이 있으면 업데이트, 없으면 인서트
    	Member returnMember = memberRepository.save(member);
    	return returnMember;
    }
}
  • save: persist와 동일한 기능
  • findById(id)에서 Id는 밑의 Id임 

 

서비스를 url과 맵핑 시켜주는 컨트롤러를 추가해보자

MyController.java

package com.study.springboot.jpa;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class MyController 
{
    @Autowired
    MemberService memberService;

    @RequestMapping("/")
    public String root() throws Exception {
        return "menu";
    }

    @RequestMapping("/insert")
    public String insert(@RequestParam("username") String username, Model model) 
    {
        // 데이터 3개 정도 추가하고 테스트
        Member member = Member.builder()
              .username(username)
              .createDate(LocalDate.now())
              .build();
       Member result = memberService.insert(member);
       model.addAttribute("member", result);

        return "insert";
    }

    @RequestMapping("/select")
    public String select(@RequestParam("id") Long id, Model model) 
    {
        Optional<Member> result = memberService.select(id);
        if (result.isPresent())
        {
           model.addAttribute("member", result.get());
        }
        else
        {
           model.addAttribute("member", null);
        }

       return "select";
    }

    @RequestMapping("/selectAll")
    public String selectAll(Model model) 
    {
        List<Member> result = memberService.selectAll();
       model.addAttribute("members", result);

       return "selectAll";
    }

    @RequestMapping("/delete")
    public String delete(@RequestParam("id") Long id) 
    {
        memberService.delete(id);
        
        return "delete";
    }

    @RequestMapping("/update")
    public String update(Member member, Model model) 
    {
        Member result = memberService.update(member);
       model.addAttribute("member", result);

        return "update";
    }

}
  • insert(), selclet(), selelctAll, delete(), update()호출 
  • builder() 이용해서 username과 LocalDate만 가지고 객체 만듬 
  • select로 member를 받았지만 값이 존재하지 않을 수도 있기 때문에 Optional 씀
    • 이때 데이터가 있느냐 없느냐를 체크하는 것이 isPresent() 

 

JpaRepository 활용 

SQL문에 바인딩 되는 값들 출력해보려면? => 디버깅 편리 

# show sql data binding
logging.level.org.hibernate.SQL=debug
logging.level.org.hibernate.orm.jdbc.bind=trace

위의 코드 추가하면 됨 

 

이제 Member엔티티를 수정해보자 

package com.study.springboot.jpa;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@Entity(name="JPAMEMBER02")
public class Member 
{
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private String email;
}

 

 MemberRepository.java

id 외에 name과 email로도 검색할 수 있도록 코드 추가하자

package com.study.springboot.jpa;

import java.util.List;
import java.util.Optional;

import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> 
{
    // 제네릭타입 : long 이 아니라 Long으로 작성.
    // 기본적인 Create, Read, Update, Delete 자동으로 생성


    // findBy 뒤에 컬럼명을 붙여주면 이를 이용한 검색이 가능하다
    Optional<Member> findByName(String keyword);
    Optional<Member> findByEmail(String keyword);

    // 다양한 확장이 가능하다.
    List<Member> findByNameLike(String keyword);
    List<Member> findByNameLikeOrderByNameDesc(String keyword);
    List<Member> findByNameLikeOrderByNameAscEmailDesc(String keyword);
    
    List<Member> findByNameLike(String keyword, Sort sort);
}

 

MemberService.java

package com.study.springboot.jpa;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

@Service
public class MemberService
{
    @Autowired
    private MemberRepository memberRepository;

    public void insert() 
    {
    	Member member;
    	
    	member = Member.builder().name("이순신").email("test1@test.com").build();
        memberRepository.save(member);

        member = Member.builder().name("강감찬").email("test2@test.com").build();
        memberRepository.save(member);

        member = Member.builder().name("을지문덕").email("test3@test.com").build();
        memberRepository.save(member);

        member = Member.builder().name("계백").email("test4@test.com").build();
        memberRepository.save(member);

        member = Member.builder().name("김유신").email("test5@test.com").build();
        memberRepository.save(member);

        member = Member.builder().name("연개소문").email("test6@test.com").build();
        memberRepository.save(member);

        member = Member.builder().name("양만춘").email("test7@test.com").build();
        memberRepository.save(member);

        member = Member.builder().name("김종서").email("test8@test.com").build();
        memberRepository.save(member);

        member = Member.builder().name("최영").email("test9@test.com").build();
        memberRepository.save(member);
    }

    public List<Member> selectAll() 
    {
        return memberRepository.findAll();
    }

    public Optional<Member> selectId(Long search) 
    {
        Optional<Member> member = memberRepository.findById(search);
        return member;
    }

    public Optional<Member> selectName(String search) 
    {
    	Optional<Member> member = memberRepository.findByName(search);
        return member;
    }

    public Optional<Member> selectEmail(String search) 
    {
    	Optional<Member> member = memberRepository.findByEmail(search);
        return member;
    }

    public List<Member> selectNameLike(String search) 
    {
    	List<Member> member = memberRepository.findByNameLike(search);
        return member;
    }

    public List<Member> selectNameLikeNameDesc(String search) 
    {
    	List<Member> member = memberRepository.findByNameLikeOrderByNameDesc(search);
        return member;
    }

    public List<Member> selectNameLike(String search, Sort sort) 
    {
    	List<Member> member = memberRepository.findByNameLike(search, sort);
        return member;
    }

}

 

  • findByName(), findByEmail 등을 쓰는 controller를 만들어보자 

MyController.java

package com.study.springboot.jpa;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MyController 
{
    @Autowired
    MemberService memberService;

    @RequestMapping("/")
    public String root() throws Exception {
        return "menu";
    }

    @RequestMapping("/insert")
    public String insert(Member member, Model model) 
    {
       memberService.insert();

        return "insert";
    }

    @RequestMapping("/selectAll")
    public String selectAll(Model model) 
    {
        List<Member> result = memberService.selectAll();
       model.addAttribute("members", result);

       return "selectAll";
    }

    @RequestMapping("/selectById")
    public String selectById(@RequestParam("id") Long search, Model model) 
    {
        Optional<Member> result = memberService.selectId(search);
       model.addAttribute("member", result.get());

       return "select_id";
    }

    @RequestMapping("/selectByName")
    public String selectByName(@RequestParam("name") String search, Model model) 
    {
        Optional<Member> result = memberService.selectName(search);
       model.addAttribute("member", result.get());

       return "select_name";
    }

    @RequestMapping("/selectByEmail")
    public String selectByEmail(@RequestParam("email") String search, Model model) 
    {
        Optional<Member> result = memberService.selectEmail(search);
       model.addAttribute("member", result.get());

       return "select_email";
    }

    @RequestMapping("/selectByNameLike")
    public String selectByNameLike(@RequestParam("name") String search, Model model) 
    {
        String name = search + "%";
        List<Member> result = memberService.selectNameLike(name);
       model.addAttribute("members", result);

       return "select_name_list";
    }

    @RequestMapping("/selectByNameLikeNameDesc")
    public String selectByNameLikeNameDesc(@RequestParam("name") String search, Model model) 
    {
        String name = search + "%";
        List<Member> result = memberService.selectNameLikeNameDesc(name);
       model.addAttribute("members", result);

       return "select_name_list";
    }

    @RequestMapping("/selectByNameLikeOrder")
    public String selectByNameLikeOrder(@RequestParam("name") String search, Model model) 
    {
        String name = search + "%";
        Sort sort = Sort.by(Sort.Order.desc("name"));
//      Sort sort = Sort.by(Sort.Order.desc("name"), Sort.Order.asc("email"));

        List<Member> result = memberService.selectNameLike(name, sort);
       model.addAttribute("members", result);

       return "select_name_list";
    }
}
  • /selectByName?name=을지문덕 같이 파라미터를 받아서 그 파라미터를 selelctName 메서드에 넣어줌
Sort sort = Sort.by(Sort.Order.desc("name"));
  • name 순서를 desc로 sort하겠다 => .desc("email")하면 한번 더 email순서로 sort 
  • orderBy가 길어질 때 sort를 쓰는 것 

 

페이지 처리 

 Member.java

generateValue 코드 추가해주기

package com.study.springboot.jpa;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@Entity(name="JPAMEMBER03")
public class Member 
{
    @Id
    @SequenceGenerator (
          name = "mySequence03",
          sequenceName = "JPAMEMBER03_SEQ",
          initialValue = 1,
          allocationSize = 1
    )
    @GeneratedValue (generator = "mySequence03")
    private Long id;
    private String name;
    private String email;
}

 

MemberRepository.java

package com.study.springboot.jpa;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> 
{
    // 제네릭타입 : long 이 아니라 Long으로 작성.
    // 기본적인 Create, Read, Update, Delete 자동으로 생성

//  List<Member> findByNameLike(String keyword, Pageable pageable);
	Page<Member> findByNameLike(String keyword, Pageable pageable);
}
  • List로 리턴값을 받을 수도 있지만 page로 받아야 페이지 기능을 사용할 수 있음 
  • 위의 예제에서는 sort를 사용했지만 이번엔 pageable 사용 

MemberService.java

@Service
public class MemberService
{
    @Autowired
    private MemberRepository memberRepository;

//  public List<Member> selectNameLike(String search, Pageable pageable) 
//  {
//  	List<Member> member = memberRepository.findByNameLike(search, pageable);
//      return member;
//  }

	public Page<Member> selectNameLike(String search, Pageable pageable) 
	{
		Page<Member> member = memberRepository.findByNameLike(search, pageable);
		return member;
	}

}

 

MyController.java

@Controller
public class MyController 
{
    @Autowired
    MemberService memberService;

    @RequestMapping("/")
    public String root() throws Exception {
        return "menu";
    }

    @RequestMapping("/selectByNameLike")
    public String selectByNameLike(@RequestParam("name") String search,
                             @RequestParam("page") String page,
                             Model model) 
    {
        System.out.println("***"+ search +"***");
        System.out.println("***"+ page +"***");
        
        String name = search + "%";
        Sort sort = Sort.by(Sort.Order.desc("name"));
        int nPage = Integer.parseInt(page) - 1;

        // 페이지는 0부터
        Pageable pageable = PageRequest.ofSize(10).withPage(nPage).withSort(sort);

        Page<Member> result = memberService.selectNameLike(name, pageable);
        List<Member> content = result.getContent(); //10개 리스트
        long totalElements = result.getTotalElements(); //24
        int  totalPages    = result.getTotalPages();//3
        int  size          = result.getSize();//10
        int  pageNumber    = result.getNumber() + 1;    // 0부터 시작하므로 +1
        int  numberOfElements = result.getNumberOfElements(); // content의 개수
        
        //모델에 넣어서 출력해보자
        model.addAttribute("members", content); 
        model.addAttribute("totalElements", totalElements);
        model.addAttribute("totalPages", totalPages);
        model.addAttribute("size", size);
        model.addAttribute("pageNumber", pageNumber);
        model.addAttribute("numberOfElements", numberOfElements);

       return "select_name_list";
    }
}
  • name, page를 파라미터로 받음
  • 정렬이 desc로 되게 sort 만들어줌
  • PageRequest를 이용해서 페이지를 요청할 때 10개씩 한페이지에 나오게 하려면 ofSize 설정
  • 전체 페이지개수는 withPage로 설정 
  • 이렇게 pageble개체를 만든 후 서비스에 selectNameLike 메소드를 넘겨줌 => 리턴값 result에 우리가 원하는 정보들이 다 들어있음 

 

JPQL 기초 

spring data jpa에서도 sql문을 직접 작성할 수 있음 

  • jpql은 엔티티임
  • member.java는 위의 예제 그대로 사용하고 MemberRepository.java만 수정

1. JPQL

package com.study.springboot.jpa;

import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> 
{
//  Page<Member> findByNameLike(String keyword, Pageable pageable);

    // 일반 JPQL쿼리, from뒤는 엔티티 명 (소문자로 할 시 에러)
    @Query("select m from JPAMEMBER03 m where m.name like :name1 order by m.id desc")
    List<Member> findMembers(@Param("name1") String name2);

    @Query("select m from JPAMEMBER03 m where m.name like :name1")
    List<Member> findMembers(@Param("name1") String name2, Sort sort);

    @Query("select m from JPAMEMBER03 m where m.name like :name1")
    Page<Member> findMembers(@Param("name1") String name2, Pageable pageable);
    
}

 

  • (데이터베이스가 아니라) 영속성 객체를 대상으로 query문을 만듬
  • 쿼리문에 영속성 객체명을 바로 사용하면 안되고 aliias( 예제에서는 m)을 만들어서 사용해야함 

2.일반 sql문

// 일반 SQL쿼리 : 테이블명 등 대소문자 가리지 않는다.
    @Query(value = "select * from jpamember03 where name like :name1 order by id desc",
          nativeQuery = true)
    List<Member> findMembersNative(@Param("name1") String name2);

 

이 repository를 사용하는 service도 수정해주자

package com.study.springboot.jpa;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

@Service
public class MemberService
{
    @Autowired
    private MemberRepository memberRepository;

    public List<Member> selectMembers1(String search) 
    {
        List<Member> member = memberRepository.findMembers(search);
        return member;
    }

    public List<Member> selectMembers2(String search, Sort sort) 
    {
        List<Member> member = memberRepository.findMembers(search, sort);
        return member;
    }

    public Page<Member> selectMembers3(String search, Pageable pageable) 
    {
        Page<Member> member = memberRepository.findMembers(search, pageable);
        return member;
    }

    public List<Member> selectMembers4(String search) 
    {
        List<Member> member = memberRepository.findMembersNative(search);
        return member;
    }

}

 

MyController.java

package com.study.springboot.jpa;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class MyController 
{
    @Autowired
    MemberService memberService;

    @RequestMapping("/")
    public String root() throws Exception {
        return "menu";
    }

    @RequestMapping("/selectNameLike1")
    public String selectMembers1(@RequestParam("name") String search,
                           Model model) 
    {
        System.out.println("***1:"+ search +"***");
        String name = search + "%";

        List<Member> result = memberService.selectMembers1(name);

        model.addAttribute("members", result);

       return "select_name_list_1";
    }

    @RequestMapping("/selectNameLike2")
    public String selectMembers2(@RequestParam("name") String search,
                           Model model) 
    {
        System.out.println("***2:"+ search +"***");
        String name = search + "%";
        Sort sort = Sort.by(Sort.Order.asc("id"));

        List<Member> result = memberService.selectMembers2(name, sort);

        model.addAttribute("members", result);

       return "select_name_list_1";
    }

    @RequestMapping("/selectNameLike3")
    public String selectMembers3(@RequestParam("name") String search,
                           @RequestParam("page") String page,
                           Model model) 
    {
        System.out.println("***3:"+ search +"***");
        System.out.println("***3:"+ page +"***");
        
        String name = search + "%";
        Sort sort = Sort.by(Sort.Order.desc("id"));
        int nPage = Integer.parseInt(page) - 1;

        // 페이지는 0부터
        Pageable pageable = PageRequest.ofSize(10).withPage(nPage).withSort(sort);

        Page<Member> result = memberService.selectMembers3(name, pageable);
        List<Member> content = result.getContent();
        long totalElements = result.getTotalElements();
        int  totalPages    = result.getTotalPages();
        int  size          = result.getSize();
        int  pageNumber    = result.getNumber() + 1;    // 0부터 시작하므로
        int  numberOfElements = result.getNumberOfElements(); // content의 개수

        model.addAttribute("members", content);
        model.addAttribute("totalElements", totalElements);
        model.addAttribute("totalPages", totalPages);
        model.addAttribute("size", size);
        model.addAttribute("pageNumber", pageNumber);
        model.addAttribute("numberOfElements", numberOfElements);

       return "select_name_list_2";
    }

    @RequestMapping("/selectNameLike4")
    public String selectMembers4(@RequestParam("name") String search,
                           Model model) 
    {
        System.out.println("***4:"+ search +"***");
        String name = search + "%";

        List<Member> result = memberService.selectMembers4(name);

        model.addAttribute("members", result);

       return "select_name_list_1";
    }
}