[JPA] Spring Boot 환경에 JPA Hibernate 설정하기

EUN JY·2022년 2월 9일
3

JAVA

목록 보기
2/7
post-thumbnail

1. Spring Boot 설정

1-1. build.gradle

dependencies {
    // Hibernate
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    compile("mysql:mysql-connector-java")
    ...
}

1-2. application.yml

spring:
  datasource:
    url: jdbc:mysql://<host>:<port>/<dbname>
    username: <username>
    password: <password>
  jpa:
    hibernate:
      ddl-auto: update # 운영시에는 validate 추천
    show-sql: true
  • spring.jpa.hibernate.ddl-auto : sessionFactory가 올라갈 때, DBMS의 스키마를 자동으로 수정하거나 검증하는 설정 값 (none/create/create-drop/update/validate)
  • spring.jpa.hibernate.format_sql : 실제 JPA의 구현체인 hibernate가 동작하면서 발생하는 SQL을 포맷팅하여 출력 (실행되는 SQL의 가독성을 높여줌)
  • spring.jpa.show-sql : JPA 처리 시 발생하는 SQL Log 유무 (true/false)

2. Entity 생성

@Entity
@Table(name="employee")
@Getter
@Setter
public class Employee {
    @id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "name", length = 30, nullable = false)
    private String name;
}

2-1. 객체와 테이블 매핑

2-1-1. @Entity

  • JPA가 Entity로서 관리한다는 것을 의미
  • 다른 Entity와 충돌이 우려될 경우 이름을 바꿔도 되나, 기본적으로는 Class명을 따름
  • 기본 생성자 필수
  • final, enum, interface, inner 클래스 사용 못함
  • 저장 필드에 final 사용 못함

2-1-2. @Table

  • Entity와 매핑할 DB Table을 지정
  • 속성
    • name : 매핑할 table 이름, 기본은 Entity 이름을 사용
    • catalog : catalog 기능이 있는 DB에서 catalog를 매핑
    • schema : schema 기능이 있는 DB에서 schema를 매핑
    • 자동으로 스키마를 생성하는 기능은 아래 값을 설정 함으로써 가능
<property name="hibernate.hbm2ddl.auto" value="create"/>
  • uniqueConstraints 속성 : DDL 생성 시에 유니크 제약조건을 만듦

    value 속성값 더보기
  • value

    • create : 기존 Table Drop + 생성
    • create-drop : create 후 종료 시 drop까지 실행
    • update : 변경된 내용만 수정(JPA 스팩에는 없고 hibernate에만 있는 설정)
    • validate : 기존 DB Table 정보와 비교해서 차이가 있다면 경고하고 애플리케이션을 실행하지 않음(JPA 스팩에는 없고 hibernate에만 있는 설정)
    • none : 설정이 없거나 유효하지 않은 값을 설정하면 기능을 사용하지 않게 됨

2-2. 기본 키 매핑

  • primary key를 설정하는 것

2-2-1. 직접할당

  • em.persist()를 호출하기 전에 사용자가 직접 ID를 설정하는 것
Board board = new Board();
board.setId("board1");
em.persist(board);

2-2-2. 자동생성

  • IDENTITY
    • 기본키의 생성을 DB에 위임하는 것
    • MySQL의 AUTO_INCREMENT와 같은 것
    • @GeneratedValue(strategy = GenerationType.IDENTITY) 로 설정 가능
  • SEQUENCE
    • 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트를 이용하는 방법
    • @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = “BOARD_SEQ_GENERATOR”) 로 설정 가능
@Entity
@SequenceGenerator(
    name = "BOARD_SEQ_GENERATOR",
    sequenceName = "BOARD_SEQ", // 실제 DB의 Sequence Name
    initialValue = 1,
    allocationSize = 1              )
public class Board {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "BOARD_SEQ_GENERATOR")
    private Long id;
}
  • TABLE
    • 키 생성 전용 Table을 만들어서 이를 SEQUENCE처럼 사용하는 것
@Entity
@TableGenerator(
    name = "BOARD_SEQ_GENERATOR",
    table = "MY_SEQUENCE", // 실제 DB의 Table name
    pkColumnValue = "BOARD_SEQ", 
    allocationSize = 1              )
public class Board {
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "BOARD_SEQ_GENERATOR")
    private Long id;
}
  • AUTO
    • DB 종류에 따라 JPA가 알맞은 것을 선택하는 것
    • Oracle의 경우 SEQUENCE, MySQL의 경우 IDENTITY를 선택하게 됨
    • DB 종류가 바뀌어도 소스를 수정하지 않아도 된다는 장점
    • @GeneratedValue(strategy = GenerationType.AUTO)

2-3. 필드와 컬럼 매핑

  • 사용되는 어노테이션 : @Column, @Enumerated, @Temporal, @Lob, @Transient, @Access

2-3-1. @Column

  • 객체 필드를 테이블 컬럼과 매핑해주는 가장 대표적인 어노테이션
  • name, nullable이 주로 사용됨
  • 속성
    • name : 매핑할 table 컬럼 이름, 기본은 객체의 필드 이름을 사용
    • insertable : 엔티티 저장시 이 필드도 저장하라는 의미로 기본은 true (false로 하면 Readonly일때 사용 가능, 거의 사용안됨)
    • updatable : 엔티티 수정시 이 필드도 수정하라는 의미로 기본은 true (false로 하면 Readonly일때 사용 가능, 거의 사용안됨)
    • table : 하나의 엔티티를 두 개 이상의 테이블에 매핑할때 사용 (거의 사용안됨)
    • nullable : false로 설정하면 DDL 생성 시에 'NOT NULL' 제약조건을 추가해줌
    • unique : @Table 의 uniqueConstraints와 같지만 한 컬럼에 대해서 적용할 때는 간단하게 이걸 이용 가능 (단 여러 컬럼을 사용할때는 @Table의 uniqueConstraints를 사용해야 함)
    • columnDefinition : 사용자가 직접 컬럼의 정보를 입력해줌
    • length : 문자 길이에 대한 제약조건 (String 타입에만 적용되며 기본값은 255)
    • precision, scale : BigDecimal 타입에서 사용됨 (precision은 소수점을 포함한 전체 지릿수, scale은 소수 자리수를 의미) (0float, double에는 해당되지 않음)
어노테이션 더보기

2-3-2. @Enumerated

  • enum 타입을 매핑할 때 사용
  • 속성
    • name
      • EnumType.ORDINAL : enum의 순서를 DB에 저장, 이값이 Default (숫자로 저장되므로 데이터 크기가 작아지고 빠르지만 enum의 순서를 변경할 수 없는 단점)
      • EnumType.STRING : enum 이름을 DB에 저장 (문자로 저장되므로 데이터 크기가 커지고 느리지만 enum의 순서와 상관 없이 사용 가능) (Default는 ORDINAL이지만 STRING을 더 추천)

2-3-3. @Temporal

  • 날짜 타입 매핑 시 사용 속성을 입력하지 않으면 자바의 Date과 가장 유사한 Timestamp로 저장됨(H2, Oracle, PostgreSQL)
  • DB의 종류에 따라 Datetime으로 저장되기도 함(MySQL)
  • 속성
    • value
      • TemporalType.DATE : 2013-01-23 와 같은 날짜 타입
      • TemporalType.TIME : 11:23:18 과 같은 시간 타입
      • TemporalType.TIMESTAMP : 2013-01-23 11:23:18 과 같이 DB의 Timestamp 타입과 매핑

2-3-4. @Lob

  • 별도의 속성은 없음
  • 문자열이면 CLOB, 그외의 타입에는 BLOB으로 매핑

2-3-5. @Transient

  • 이 필드는 매핑하지 말라는 의미 (임시로 중간 값을 저장하는 용도로 사용 가능)

2-3-6. @Access

  • JPA가 엔티티 데이터에 접근하는 방식
  • @Access를 설정하지 않으면 @Id의 설정 위치에 따라 접근 방식이 결정됨
  • @Id가 필드에 붙어 있으면 FIELD접근 방식을 의미하므로 Getter가 없어도 되며, 프로퍼티에 있으면 PROPERTY접근 방식을 의미
  • 위의 두 가지 방식을 섞어서 사용도 가능
    • AccessType.FIELD : 필드 접근, Private이어도 접근 가능하다.
    • AccessType.PROPERTY : 프로퍼티 접근, 접근자(Getter)를 이용한다.

2-4. 연관관계 매핑

3. EmployeeController.java

@Controller
public class EmployeeController {
    private EmployeeService employeeService;

    @Autowired
    public EmployeeController(EmployeeService employeeService) {
        this.employeeService = employeeService;
    }
    
    @PostMapping("/employee")
    public @ResponseBody Result addEmployee(@RequestBody Employee employee) {
        return employeeService.addEmployee(employee);
    }

    @GetMapping("/employees")
    public @ResponseBody Result getEmployees() {
        return employeeService.getEmployees();
    }

    @GetMapping("/employees/{id}")
    public @ResponseBody Result getEmployeeById(@PathVariable Long id) {
        return employeeService.getEmployeeById(id);
    }

    @PutMapping("/employees/{id}")
    public @ResponseBody Result updateEmployeeById(@PathVariable Long id, @RequestBody Employee employee) {
        return employeeService.updateEmployeeById(id, employee);
    }

    @DeleteMapping("/employees/{id}")
    public @ResponseBody Result deleteEmployeeById(@PathVariable Long id) {
        return employeeService.deleteEmployeeById(id);
    }
}

4. EmployeeServiceImplement.java

  • EmployeeService 생략
@Service
public class EmployeeServiceImplement implements EmployeeService {
    private EmployeeRepository employeeRepository;

    @Autowired
    public EmployeeServiceImplement(EmployeeRepository employeeRepository) {
        this.employeeRepository = employeeRepository;
    }
    
    @Override
    public Result addEmployee(Employee employee) {
        employeeRepository.save(employee);
        return new Result(ResultCode.OK);
    }

    @Override
    public Result getEmployees() {
        Result result = new Result(ResultCode.OK);
        result.setData(employeeRepository.findAll());
        return result;
    }

    @Override
    public Result getEmployeeById(Long id) {
        Result result = new Result(ResultCode.OK);
        result.setData(employeeRepository.findEmployeeById(id));
        return result;
    }

    @Override
    public Result updateEmployeeById(Long id, Employee employee) {
        employee.setId(id);
        employeeRepository.save(employee);
        return new Result(ResultCode.OK);
    }

    @Override
    public Result deleteEmployeeById(Long id) {
        employeeRepository.deleteById(id);
        return new Result(ResultCode.OK);
    }
}

5. EmployeeRepository.java

  • CrudRepository : 기본 CRUD 제공 (extends Repository)
  • PagingAndSortingRepository : pagination, sorting 제공 (extends CrudRepository)
  • JpaRepository : flush, deleting records in a batch 와 같은 함수 제공 (extends PagingAndSortingRepository, QueryByExampleExecutor)
@Transactional
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
    Employee findEmployeeById(Long id);
    List<Employee> findEmployeeByName(String name);
} 

6. 관련 에러

  • Unable to build Hibernate SessionFactory
    • hibernate.ddl-auto 의 validate 설정을 사용하다보면 [PersistenceUnit: default] Unable to build Hibernate SessionFactory 에러를 보게 됨
    • PersistenceUnit 에러는 대체적으로 코드 부분과 DB의 필드 부분이 일치하지 않을 때 발생
      • create table 시에 type 을 int 로 해놨는데 Entity는 Long 으로 설정했을 때 (Long => bigint)
      • type char 인데, Entity는 String (String => varchar)
      • auto_increment 설정인데 AUTO를 사용함 (ENTITY => auto_increment)
profile
개린이

0개의 댓글