06_Backend 하위폴더에
04_JPAExam 폴더생성
JPAExam 프로젝트 만들기
버전 2.7.16으로 설정하고 종속성 5개 체크
build gradle (dependencies 수정)
dependencies {
// TODO : 오라클 라이브러리( 19c )
implementation 'co
m.oracle.database.jdbc:ucp:19.14.0.0'
implementation 'com.oracle.database.security:oraclepki:19.14.0.0'
implementation 'com.oracle.database.security:osdt_cert:19.14.0.0'
implementation 'com.oracle.database.security:osdt_core:19.14.0.0'
// TODO : logback , log4jdbc 설정
implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16'
implementation 'ch.qos.logback:logback-classic:1.2.11'
implementation 'org.slf4j:slf4j-api:1.7.36'
implementation 'org.slf4j:jcl-over-slf4j:1.7.36'
// TODO : JPA 라이브러리
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'co
m.oracle.database.jdbc:ojdbc8'
annotationProcessor 'org.projectlombok:lombok'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
application.properties 수정하고
코끼리 버튼 누르기(gradle)
# 서버 포트
server.port=8000
# todo: docker db 설정
spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
# todo: spring.datasource.url=jdbc:log4jdbc:oracle:thin:@ip주소:db포트번호/db이름
spring.datasource.url=jdbc:log4jdbc:oracle:thin:@localhost:1521/xepdb1
spring.datasource.username=scott
spring.datasource.password=!Ds1234567890
# todo: jpa 설정
# todo: ddl-auto - create(모든 테이블 재생성)/update(수정된 테이블만 생성)/none(안함)
spring.jpa.hibernate.ddl-auto=create
#spring.jpa.hibernate.ddl-auto=update
#spring.jpa.hibernate.ddl-auto=none
# todo: db 제품 연결 ( oracle 12이상 : Oracle12cDialect )
spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect
# todo: generate-ddl=true (테이블 자동 생성 옵션)
spring.jpa.generate-ddl=true
# todo: sql log 보기 (true/false)
spring.jpa.show-sql=true
# 1) resource/data.sql 자동 실행 ( DML 실행 )
# -> data.sql ( dml 실행 ), schema.sql ( ddl 실행 )
# todo: dml, ddl 스크립트 (실습용 샘플 테이블 ) 실행을 위한 옵션
spring.jpa.defer-datasource-initialization=true
# todo: sql log 이쁘게 보여주기
spring.jpa.properties.hibernate.format_sql=true
# todo: 로깅 레벨 : error < info < debug < trace (정보 많은 순)
logging.level.org.hibernate=info
# 2) resource/data.sql 자동 실행 ( DML 실행 )
# -> data.sql ( dml 실행 ), schema.sql ( ddl 실행 )
spring.sql.init.mode=always
# sql 에러 무시하고 스프링 서버 로딩
spring.sql.init.continue-on-error=true
# 자바 소스 변경시 스프링 서버 자동 재시작
spring.devtools.restart.enabled=true
# PUT , DELETE 방식도 form 에서 사용할 수 있게 만들어줌 : jsp 사용
spring.mvc.hiddenmethod.filter.enabled=true
파일 다운받고 resources에 넣고
com.example.japexam에 controller, dto, model, respository, service 폴더 생성
model 폴더에
BaseTimeEntity 생성
package com.example.jpaexam.model;
import lombok.Getter;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* packageName : com.example.jpaexam.model
* fileName : BaseTimeEntity
* author : GGG
* date : 2023-10-16
* description : JPA 에서 자동으로 생성일자/수정일자를 만들어 주는 클래스
* 요약 :
* <p>
* ===========================================================
* DATE AUTHOR NOTE
* —————————————————————————————
* 2023-10-16 GGG 최초 생성
*/
@Getter
// todo: 자동으로 생성일자/수정일자 컬럼을 sql 문에 추가시키는 어노테이션 2개
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {
// todo: 공통속성 : yyyy-MM-dd HH:mm:ss 아니고 기본 패턴으로 보임
private String insertTime;
private String updateTime;
// todo: 해당 테이블에 데이터가 만들어 질때(insert 문) 실행되는 이벤트 함수
@PrePersist
void OnPrePersist() {
this.insertTime
= LocalDateTime.now()
.format(DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
// todo: 해당 테이블에 데이터가 수정 질때(update 문) 실행되는 이벤트 함수
@PreUpdate
void OnPreUpdate() {
this.updateTime
= LocalDateTime.now()
.format(DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss"));
this.insertTime = this.updateTime; // 생성일시 == 수정일시 동일하게 처리
}
}
model 폴더에
Dept 만들기
package com.example.jpaexam.model;
import lombok.*;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
/**
* packageName : com.example.jpaexam.model
* fileName : Dept
* author : GGG
* date : 2023-10-16
* description : 부서 모델 클래스 ( 엔티티(entity) )
* 요약 :
* <p>
* ===========================================================
* DATE AUTHOR NOTE
* —————————————————————————————
* 2023-10-16 GGG 최초 생성
*/
// todo: @Entity - JPA 기능을 클래스에 부여하는 어노테이션
@Entity
// todo: @Table(name = "생성될테이블명")
@Table(name = "TB_DEPT")
// todo 사용법 : @SequenceGenerator(
// name = "시퀀스함수이름"
// , sequenceName = "DB에생성된시퀀스이름"
// , initialValue = 시작값
// , allocationSize = jpa에서관리용숫자(성능지표)
//)
@SequenceGenerator(
name = "SQ_DEPT_GENERATOR"
, sequenceName = "SQ_DEPT"
, initialValue = 1
, allocationSize = 1
)
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
// todo: jpa 어노테이션 sql 자동 생성시 null 값 컬럼은 제외하고 생성
// 예) insert into 테이블명(컬럼1, 컬럼2, 컬럼3) values(1, 2, null);
// => insert into 테이블명(컬럼1, 컬럼2) values(1, 2);
@DynamicInsert
@DynamicUpdate
public class Dept extends BaseTimeEntity {
@Id
// todo: @GeneratedValue(strategy = GenerationType.SEQUENCE
// , generator = "시퀀스함수이름"
// )
@GeneratedValue(strategy = GenerationType.SEQUENCE
, generator = "SQ_DEPT_GENERATOR"
)
@Column(columnDefinition = "NUMBER")
private Integer dno; // 부서번호(기본키) - 시퀀스 기능 부여
@Column(columnDefinition = "VARCHAR2(255)")
private String dname; // 부서명
@Column(columnDefinition = "VARCHAR2(255)")
private String loc; // 부서위치
}
repository 폴더에
DeptRepository
package com.example.jpaexam.repository;
import com.example.jpaexam.model.Dept;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* packageName : com.example.jpaexam.repository
* fileName : DeptRepository
* author : GGG
* date : 2023-10-16
* description : JPA 레포지토리 인터페이스 ( DB 접속 함수들(CRUD) 있음)
* == DAO 비슷함
* 요약 :
* <p>
* ===========================================================
* DATE AUTHOR NOTE
* —————————————————————————————
* 2023-10-16 GGG 최초 생성
*/
// todo: @Repository - 클래스 위에 붙이고, 스프링서버가 실행될때 자동으로
// 객체 1개를 만들어줌 ( IOC )
// 사용법 : 인터페이스명 extends JpaRepository<모델클래스명, 기본키의자료형>
@Repository
public interface DeptRepository extends JpaRepository<Dept, Integer> {
}
service 폴더에
DeptService
package com.example.jpaexam.service;
import com.example.jpaexam.model.Dept;
import com.example.jpaexam.repository.DeptRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
/**
* packageName : com.example.jpaexam.service
* fileName : DeptService
* author : GGG
* date : 2023-10-16
* description : 부서 업무 서비스
* 요약 :
* <p>
* ===========================================================
* DATE AUTHOR NOTE
* —————————————————————————————
* 2023-10-16 GGG 최초 생성
*/
@Service
public class DeptService {
@Autowired
DeptRepository deptRepository; // DI 객체 가져오기
/** 전체조회 */
public List<Dept> findAll() {
List<Dept> list = deptRepository.findAll(); // db 전체조회 함수 호출
return list;
}
/** 상세조회(1건조회) */
public Optional<Dept> findById(int dno) {
Optional<Dept> optionalDept = deptRepository.findById(dno);
return optionalDept;
}
/** 저장(수정)함수 */
public Dept save(Dept dept) {
// todo: jpa 저장함수 호출 ( 기본키 없으면 insert, 있으면 update )
Dept dept2 = deptRepository.save(dept);
return dept2; // 저장된 부서객체
}
/** 삭제함수 */
public boolean removeById(int dno) {
// existsById : jpa 함수 - 리턴값: 있으면 true, 없으면 false
if(deptRepository.existsById(dno)) {
deptRepository.deleteById(dno); // DB 삭제(dno)
return true;
}
return false;
}
}
controller 폴더에
DeptController
package com.example.jpaexam.controller.exam01;
import com.example.jpaexam.model.Dept;
import com.example.jpaexam.service.DeptService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
/**
* packageName : com.example.jpaexam.controller.exam01
* fileName : DeptController
* author : GGG
* date : 2023-10-16
* description : 부서 컨트롤러 : (@RestController)
* 요약 :
* <p>
* ===========================================================
* DATE AUTHOR NOTE
* —————————————————————————————
* 2023-10-16 GGG 최초 생성
*/
@Slf4j
@RestController
@RequestMapping("/exam01")
public class DeptController {
@Autowired
DeptService deptService; // 객체 가져오기 (DI)
/** 전체 조회 */
@GetMapping("/dept")
public ResponseEntity<Object> getDeptAll() {
try {
// todo: 전체 조회 함수 호출
List<Dept> list = deptService.findAll();
if (list.isEmpty() == false) {
// 성공
return new ResponseEntity<>(list, HttpStatus.OK);
} else {
// 데이터 없음
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/** 상세 조회 */
@GetMapping("/dept/{dno}")
public ResponseEntity<Object> getDeptId(
@PathVariable int dno
) {
try {
// todo: 전체 조회 함수 호출
Optional<Dept> optionalDept = deptService.findById(dno);
if (optionalDept.isEmpty() == false) {
// 성공
return new ResponseEntity<>(optionalDept.get(), HttpStatus.OK);
} else {
// 데이터 없음
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/** 저장함수 */
@PostMapping("/dept")
public ResponseEntity<Object> createDept(
@RequestBody Dept dept
) {
try {
// jpa 서비스 저장 함수 호출 : dept2(DB 저장된 객체)
Dept dept2 = deptService.save(dept);
return new ResponseEntity<>(dept2, HttpStatus.OK);
} catch (Exception e) {
log.debug(e.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/** 수정함수 */
@PutMapping("/dept/edit/{dno}")
public ResponseEntity<Object> updateDept(
@PathVariable int dno,
@RequestBody Dept dept
) {
try {
// jpa 서비스 수정 함수 호출 : dept2(DB 수정된 객체)
Dept dept2 = deptService.save(dept);
return new ResponseEntity<>(dept2, HttpStatus.OK);
} catch (Exception e) {
log.debug(e.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/** 삭제함수 */
@DeleteMapping("/dept/delete/{dno}")
public ResponseEntity<Object> deleteDept(
@PathVariable int dno
) {
try {
// todo: 삭제 함수 호출
boolean bSuccess = deptService.removeById(dno);
if (bSuccess == true) {
// 성공
return new ResponseEntity<>(HttpStatus.OK);
} else {
// 0건 삭제
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
결과
전체조회
상세조회
저장
수정
삭제
Emp
package com.example.jpaexam.model;
import lombok.*;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
/**
* packageName : com.example.jpaexam.model
* fileName : Emp
* author : GGG
* date : 2023-10-16
* description :
* 요약 :
* <p>
* ===========================================================
* DATE AUTHOR NOTE
* —————————————————————————————
* 2023-10-16 GGG 최초 생성
*/
@Entity
// todo: @Table(name = "생성될테이블명")
@Table(name = "TB_EMP")
// todo 사용법 : @SequenceGenerator(
// name = "시퀀스함수이름"
// , sequenceName = "DB에생성된시퀀스이름"
// , initialValue = 시작값
// , allocationSize = jpa에서관리용숫자(성능지표)
//)
@SequenceGenerator(
name = "SQ_EMP_GENERATOR"
, sequenceName = "SQ_EMP"
, initialValue = 1
, allocationSize = 1
)
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
// todo: jpa 어노테이션 sql 자동 생성시 null 값 컬럼은 제외하고 생성
// 예) insert into 테이블명(컬럼1, 컬럼2, 컬럼3) values(1, 2, null);
// => insert into 테이블명(컬럼1, 컬럼2) values(1, 2);
@DynamicInsert
@DynamicUpdate
public class Emp extends BaseTimeEntity {
@Id
// todo: @GeneratedValue(strategy = GenerationType.SEQUENCE
// , generator = "시퀀스함수이름"
// )
@GeneratedValue(strategy = GenerationType.SEQUENCE
, generator = "SQ_EMP_GENERATOR"
)
@Column(columnDefinition = "NUMBER")
private Integer eno; // 사원번호(기본키) - 시퀀스 기능 부여
@Column(columnDefinition = "VARCHAR2(255)")
private String ename; // 사원명
@Column(columnDefinition = "VARCHAR2(255)")
private String job; // 직위
@Column(columnDefinition = "VARCHAR2(255)")
private Integer manager; // 매니저
@Column(columnDefinition = "VARCHAR2(255)")
private String hiredate; // 입사일
@Column(columnDefinition = "VARCHAR2(255)")
private Integer salary; // 급여
@Column(columnDefinition = "VARCHAR2(255)")
private Integer commission; // 상여금
@Column(columnDefinition = "VARCHAR2(255)")
private Integer dno; // 부서번호
}
EmpRepository
package com.example.jpaexam.repository;
import com.example.jpaexam.model.Emp;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* packageName : com.example.jpaexam.repository
* fileName : EmpRepository
* author : GGG
* date : 2023-10-16
* description :
* 요약 :
* <p>
* ===========================================================
* DATE AUTHOR NOTE
* —————————————————————————————
* 2023-10-16 GGG 최초 생성
*/
@Repository
public interface EmpRepository extends JpaRepository<Emp, Integer> {
}
EmpService
package com.example.jpaexam.service;
import com.example.jpaexam.model.Emp;
import com.example.jpaexam.repository.EmpRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
import java.util.Optional;
/**
* packageName : com.example.jpaexam.service
* fileName : EmpService
* author : GGG
* date : 2023-10-16
* description :
* 요약 :
* <p>
* ===========================================================
* DATE AUTHOR NOTE
* —————————————————————————————
* 2023-10-16 GGG 최초 생성
*/
@Service
public class EmpService {
@Autowired
EmpRepository empRepository; // DI 객체 가져오기
/** 전체조회 */
public List<Emp> findAll() {
List<Emp> list = empRepository.findAll(); // db 전체조회 함수 호출
return list;
}
/** 상세조회(1건조회) */
public Optional<Emp> findById(int eno) {
Optional<Emp> optionalEmp = empRepository.findById(eno);
return optionalEmp;
}
/** 저장(수정)함수 */
public Emp save(Emp emp) {
// todo: jpa 저장함수 호출 ( 기본키 없으면 insert, 있으면 update )
Emp emp2 = empRepository.save(emp);
return emp2; // 저장된 사원객체
}
/** 삭제함수 */
public boolean removeById(int eno) {
// existsById : jpa 함수 - 리턴값: 있으면 true, 없으면 false
if(empRepository.existsById(eno)) {
empRepository.deleteById(eno); //
return true;
}
return false;
}
}
EmpController
package com.example.jpaexam.controller.exam01;
import com.example.jpaexam.model.Dept;
import com.example.jpaexam.model.Emp;
import com.example.jpaexam.service.EmpService;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
/**
* packageName : com.example.jpaexam.controller.exam01
* fileName : EmpController
* author : GGG
* date : 2023-10-17
* description : 회원 컨트롤러 - @RestController
* 요약 :
* <p>
* ===========================================================
* DATE AUTHOR NOTE
* —————————————————————————————
* 2023-10-17 GGG 최초 생성
*/
@Slf4j
@RestController
@RequestMapping("/exam01")
public class EmpController {
@Autowired
EmpService empService; // DI
/** 전체 조회 */
@GetMapping("/emp")
public ResponseEntity<Object> getEmpAll() {
try {
// todo: 전체 조회 함수 호출
List<Emp> list = empService.findAll();
if (list.isEmpty() == false) {
// 성공
return new ResponseEntity<>(list, HttpStatus.OK);
} else {
// 데이터 없음
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/** 상세 조회 */
@GetMapping("/emp/{eno}")
public ResponseEntity<Object> getEmpId(
@PathVariable int eno
) {
try {
// todo: 상세 조회 함수 호출
Optional<Emp> optionalEmp = empService.findById(eno);
if (optionalEmp.isEmpty() == false) {
// 성공
return new ResponseEntity<>(optionalEmp.get(), HttpStatus.OK);
} else {
// 데이터 없음
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/** 저장함수 */
@PostMapping("/emp")
public ResponseEntity<Object> createEmp(
@RequestBody Emp emp
) {
try {
// jpa 서비스 저장 함수 호출 : emp2(DB 저장된 객체)
Emp emp2 = empService.save(emp);
return new ResponseEntity<>(emp2, HttpStatus.OK);
} catch (Exception e) {
log.debug(e.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/** 수정함수 */
@PutMapping("/emp/edit/{eno}")
public ResponseEntity<Object> updateDept(
@PathVariable int eno,
@RequestBody Emp emp
) {
try {
// jpa 서비스 수정 함수 호출 : emp2(DB 수정된 객체)
Emp emp2 = empService.save(emp);
return new ResponseEntity<>(emp2, HttpStatus.OK);
} catch (Exception e) {
log.debug(e.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/** 삭제함수 */
@DeleteMapping("/emp/delete/{eno}")
public ResponseEntity<Object> deleteEmp(
@PathVariable int eno
) {
try {
// todo: 삭제 함수 호출
boolean bSuccess = empService.removeById(eno);
if (bSuccess == true) {
// 성공
return new ResponseEntity<>(HttpStatus.OK);
} else {
// 0건 삭제
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
'Spring Boot' 카테고리의 다른 글
[Spring Boot] CRUD (create, read, update, delete) (0) | 2023.10.10 |
---|---|
[Spring boot] 파라메터 (0) | 2023.10.05 |
[Spring boot]스프링 부트 프로젝트 (1) | 2023.10.04 |