JPA Hibernate - ynjch97/YNJCH_WIKI GitHub Wiki

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)
โš ๏ธ **GitHub.com Fallback** โš ๏ธ