본문 바로가기
Sesac 웹 풀스택[새싹X코딩온]/Spring

6. JPA(Java Persistence API) 및 실습

by 이쟝 2023. 3. 16.
자바의 ORM 기술 표준
JAP가 개발자 대신 적합한 SQL을 생성하고 DB에 전달하고, 객체를 자동으로 Mapping 해주기에 SQL을 직접 작성할 필요가 없다.
ex) Hibernate ( JPA를 구현한 대표적 오픈소스)
장점 단점
생산성이 뛰어나고 유지보수가 용이하다 JPA의 장점을 살려 잘 사용하기 위해서는 학습 비용이 높고, 복잡한 쿼리를 사용할 때 불리하다.
DBMS에 대한 종속성이 줄어든다. 잘못 사용할 경우 SQL을 직접 사용하는 것보다 성능이 떨어질 수 있다.

 

JPA 사용하기

1. build.gradle

  • JPA를 사용하기 위해 dependency 추가

2. application.properties 수정

#기본 설정
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/sesac?useUnicode=yes&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Seoul
spring.datasource.username=root
spring.datasource.password=3306

#mysql connect
spring.jpa.database=sesac
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect

#DB function use
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto=create
# none : not create / create : auto create / update : only update structure

logging.level.org.hibernate=info

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
  • create : 정의되어 있는 table을 기반으로 만들어준다. 아무리 데이터를 많이 넣어도 삭제하고 다시시 만듦
  • 데이터 정보를 유지하고 싶으면 update를 해야한다. 

Entity

- 데이터베이스에 쓰일 필드와 여러 Entity 간의 관계를 설정하는 것
- @Entity를 이용해 해당 클래스가 Entity임을 알려주고, JPA에 정의된 필드를 바탕으로 데이터베이스에 테이블을 만들 수 있다.

Entity 관련 Annotation

@Entity 데이터베이스의 Table 임을 의미 @Table  테이블의 이름 명시
@Id primary key를 의미 @GeneratedValue primary key의 생성 전략
@Column 테이블의 컬럼을 의미    

 


3. domain/UserEntity

package sesac.jdbc.sesac.jdbc.domain;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

@Entity  // 해당 클래스가 Entity 클래스라는 것을 알려준다.
@Table(name="user")  // 테이블 이름 명시
@Getter
@Setter
public class UserEntity {

    @Id
    @GeneratedValue // AutoIncrement
    private int id;  // id primary key auto_increment

    @Column(length = 5, nullable = false)  // 길이, null 허용 여부
    private String name;

    // nickname varchar(10) not null
    @Column(length = 10, nullable = false)
    private String nickname;

}

4. repository/UserRepository

Repository 

- Entity에 의해 생성된 DB에 접근하는 메서드를 사용하기 위한 인터페이스
- JPARepository를 상속받으면 기본적인 DB 접근 메서드를 사용할 수 있다.
  • findAll( ) , findById( ), findBy컬럼명( ), save( )
package sesac.jdbc.sesac.jdbc.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import sesac.jdbc.sesac.jdbc.domain.UserEntity;

import java.util.Optional;

@Repository
public interface UserRepository extends JpaRepository<UserEntity, Integer> {
    Optional<UserEntity> findByName(String name);    // SELECT ~~ WHERE name = #{name}
    
//    Optional<UserEntity> findById(int id); // SELECT ~~ WHERE id = #{id}
//    Optional<UserEntity> findByIdName(int id, String name); // SELECT ~~ WHERE id = #{id} AND name= #{name}
//    findBy뒤에 대문자로 오는 컬럼을 기준으로 불러온다. (jpa 기능)


    // UserEntity userEntity
    // Optional<UserEntity> user; user.get()
    // null 일 수도 있는 객체를 담는 클래스

}

5. Service/MainService

Optional

- Null 일 수도 있는 객체를 감싸는 Wrapper 클래스
- Optional<T> option
  • Option 변수 내부에는 null이 아닌 T 객체가 있을 수도 있고 null이 있을 수도 있다.
  • 즉, Optional 클래스는 여러가지 API를 제공해 null일 수도 아닐 수도 있는 객체를 다룰 수 있다.
  •  nullexception 오류를 예방하기 위해서 optional을 사용
@Service
public class MainService {

    @Autowired
    private UserRepository userRepository;

    public List<UserDTO> getUserList() {
        List<UserEntity> result = userRepository.findAll(); // select * from
        List<UserDTO> users = new ArrayList<UserDTO>();

        for(int i=0; i<result.size();i++) {
            UserDTO user = new UserDTO();
            user.setId(result.get(i).getId());
            user.setName(result.get(i).getName());
            user.setNickname(result.get(i).getNickname());
            user.setNo(i+1); // no는 전달하기 위한 값

            users.add(user);
        }
        return users;
    }

    public void addUser(UserEntity user) {
        userRepository.save(user);  // insert into
    }

    public ArrayList<UserDTO> getUserName(String name) {
        Optional<UserEntity> user = userRepository.findByName(name);
        ArrayList<UserDTO> userList = new ArrayList<>();

        if(user.isPresent()) {
            UserDTO dto = new UserDTO();
            dto.setId(user.get().getId());
            dto.setNo(0);
            dto.setName(user.get().getName());
            dto.setNickname(user.get().getNickname());
            userList.add(dto);
        }
        return userList;
    }


}

6. controller/MainController

@Controller
public class MainController {

    @Autowired
    MainService mainService;

    @GetMapping("/users")
    public String getUsers(Model model) {
        ArrayList<UserDTO> userList = (ArrayList<UserDTO>) mainService.getUserList();
        model.addAttribute("list", userList);
        return "user";
    }

    @GetMapping("/user/insert")
    public String getInsertUser(@RequestParam String name, @RequestParam String nickname, Model model) {
        UserEntity user = new UserEntity();
        user.setName(name);
        user.setNickname(nickname);

        mainService.addUser(user);

        model.addAttribute("list", null);
        return "user";
    }

    @GetMapping("/user")
    public String getUser(@RequestParam String name, Model model) {
        ArrayList<UserDTO> userList = mainService.getUserName(name);

        model.addAttribute("list", userList);
        return "user";
    }

}

<결과>

localhost:8080/users를 하면 DB와 연결된 모든 유저들을 볼 수 있다. 

localhoat:8080/user/insert?name=이름&nickname=닉네임을 하면 user 디비에 추가할 수 있다.

localhost:8080/user?name=이름 을 하면 name을 기준으로 한 컬럼을 select 할 수 있다.

해당 이름이 DB에 없는 경우


  • myBatis로만 해봤는데 JPA를 배우니까 mybatis를 안쓰고 JPA를 쓰는 이유를 알겠다....
  • sequelize를 쓰는 것 같은 느낌이다. JPA를 쓰기 위해 API를 좀 더 공부해야 겠다는 생각이 든다. (기존 SQL문을 잘 다룬다는 하에ㅣ...!)