JPA

brave_chicken·2024년 6월 13일

잇(IT)생 챌린지

목록 보기
71/90

info.txt

1. spring 데이터베이스 기술

  • JDBC를 편하게 사용할 수 있도록 많은 부분을 추상화해서 제공
  • spring 내부에서 모든 기능을 제공(자동화)
  • JDBC의 중복 기능을 자동화
  • select결과를 객체로 변경할 때 복잡한 작업을 자동화

1) SQL Mapper

  • JdbcTemplate, MyBatis
  • JDBC를 편하게 사용할 수 있도록 기술 제공
  • SQL을 직접 정의

2) ORM(Object Relational Mapping)

  • 대표기술 JPA(JPA의 구현체(인터페이스를 오버라이딩해서 만든 객체) 중 가장 대표적인 것이 하이버네이트)
  • 객체와 테이블(관계형데이터베이스)을 기술
  • 테이블을 정의하고 테이블에 맞는 객체를 만들면 ORM프레임워크가 중간에서 알맞은 작업을 처리해서 SQL을 생성
  • JPA는 인터페이스

2. JPA(Java Persistence API)

  • JPA를 이용하면 sql쿼리를 jpa내부에서 자동으로 만들어준다.
  • SQL과 객체를 매핑
  • JPA는 ORM기술
  • JPA가 자바진영의 표준API이고 하버네이트가 JPA를 구현한 기술
  • JPA는 스프링과 상관없이 사용할 수 있다.
  • 스프링에서 JPA를 쉽게 사용할 수 있도록 제공되는 프로젝트 spring data jpa

1) 엔티티정의(Entity)

  • 기본생성자가 반드시 있어야 한다.
  • 엔티티클래스의 멤버변수는 컬럼명과 매핑
  • 멤버변수의 단어의 첫글자에 대문자가 있으면 _을 추가하고 첫글자를 소문자로 바꾸어 테이블의 컬럼을 만든다.
    ex) deptStartDay(엔티티 클래스) = > dept_start_day(DBMS의 컬럼명)

(1) Entity

  • 이제부터 이 객체는 JPA에서 관리하는 엔티티라는 의미
  • 테이블과 이 객체를 매핑
  • 이런 클래스는 엔티티클래스라 한다.

(2) Table

  • 엔티티와 연결될 테이블을 명시
  • 클래스명과 테이블명이 동일하면 생략가능

(3) Id

  • 엔티티클래스의 @Id가 정의되어있는 필드를 기본키에 매핑

(4) GeneratedValue

  • GeneratedValue(strategy = GenerationType.AUTO)와 동일
  • DBMS에서 자동으로 값을 만들어서 매핑
  • mysql은 auto_increment, 오라클은 sequence

2) EntityManager

  • JPA에서의 핵심기능을 제공하는 클래스(JPA는 EntityManager라는 클래스를 통해서 동작)
  • 개발자는 SQL을 직접 작성하지 않고 EntityManager가 제공하는 메소드를 이용해서 작업하면 JPA내부에서 (Entity를 참조해서)자동으로 SQL문이 만들어지고 JDBC API가 호출되어 DBMS와 연동한다.
  • JPA의 모든 데이터변경(insert,delete,update)는 트랜잭션 안에서 이루어진다.
  • 트랜잭션 처리를 하지 않으면 작업을 진행할 수 없다.

3. JPA를 이용한 CLRUD(CRUD)

1) insert(C)

  • persist메소드를 이용해서 처리
  • JPA내부에서 Entity객체를 만들고 분석한 후 insert sql문을 생성
  • JDBC API를 이용해서 insert를 수행

4. JPA특징

  • 같은 트랜잭션 안에서 같은 엔티티를 반환
    : 기본옵션은 동일한 트랜잭션 안에서 같은 엔티티가 여러 개 있어도 동일한 엔티티로 간주
  • 같은 트랜잭션 내부에서 지원하는 쓰기 지연
    : 트랜잭션 안에서 여러개를 insert하는 경우 여러번 왔다갔다하지 않고 계속 모았다가 한번에 내보낸다.(네트워크 통신비용을 줄일 수 있다.)
  • 여러 개를 insert하는 경우 persist메소드를 호출하면 1차캐시와 쓰기지연을 위한 sql저장소에 sql문고 엔티티를 저장
  • 처음 요청인 경우 sql을 만들어서 작업, 두번째부터는 캐시에서 확인한다.(같은 트랜잭션에서 성립)
  • 동일한 객체는 캐시에서 먼저 확인(같은 트랜잭션인 경우)
  • 내부적으로 commit메소드를 호출하면 sql저장소에 있는 sql문이 실행

[미션]

1. BoardEntity

  • board테이블에서 작업되도록 처리
  • boardNo(시퀀스)
  • writer, createDate, updateDate, title, content컬럼 정의

2. BoardDAOImpl클래스에

insert(BoardEntity)
update()
delete()
read()
list()
=> PersonDAOImpl과 동일한 매개변수와 리턴타입으로 작업하기
=> insert는 매개변수로 전달된 엔티티 한 개만 삽입

3. BoardDAOImplTest만들고 테스트하기

=> PersonDAOImplTest와 동일한 형식으로 테스트하기

4. com.example.jpatest.entitymanager.board패키지에서 작업하기

=> BoardEntity, BoardDAOImpl

실습

  • 스프링부트는 디폴트가 히카리씨피..내부에서 동작

application.properties

spring.application.name=jpatest
server.port=9000
server.servlet.context-path=/jpatest

#db에 대한 설정
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@127.0.0.1:1521:xe
spring.datasource.username=erp
spring.datasource.password=erp

#jpa에 대한 설정
#???? ??? ???? ?????. drop??? ???? create
#spring.jpa.hibernate.ddl-auto=create
spring.jpa.hibernate.ddl-auto=none

#sysout?? ???? ??? ?? - sql ??
spring.jpa.show-sql=true

#JPA log
logging.level.org.hibernate.sql=debug
logging.level.org.hibernate.type.descripter.sql=trace

#JPA? ??? sql? ??? ??
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true

PersonEntity

//JPA가 관리하는 객체 - 테이블과 매핑되는 객체
@Entity
@Table(name="person")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PersonEntity {
    //person테이블의 컬럼을 엔티티의 멤버변수로 정의
    //기본키로 설정 - long타입의 시퀀스, UUID
    @Id
    @GeneratedValue
    private Long id;
    @Column(name = "pass")
    private String password;
    private String name;
    //처음 데이터가 저장될때 시간을 저장
    @CreationTimestamp
    private Date createDate;
    //update쿼리가 실행될때 현재 날짜와 시간 값을 저장해 마지막 수정시간을 자동으로 저장
    @UpdateTimestamp
    private Date modifyDate;
    private int checkVal;
    //오라클의 데이터타입을 BLOB,CLOB을 정의할때 사용
    //@Lob으로 정의된 필드가 String이면 자동으로 CLOB타입으로 정의
    @Lob
    private String info;
    //디비테이블에 컬럼으로 포함시키지 않겠다는 의미
    @Transient
    private String testval;

    public PersonEntity(String password, String name, int checkVal, String info) {
        this.password = password;
        this.name = name;
        this.checkVal = checkVal;
        this.info = info;
    }

    public PersonEntity(Long id, String password, String info) {
        this.id = id;
        this.password = password;
        this.info = info;
    }
}

PersonDAO

public interface PersonDAO {
	void insert(PersonEntity dto) ;
	PersonEntity read(String id);
	void delete(String id);
	void update(PersonEntity dto);
	List<PersonEntity> list();
}

PersonDAOImpl

@Repository
@RequiredArgsConstructor
public class PersonDAOImpl implements PersonDAO{
    //JPA내부에서 EntityManager를 이용해서 CRUD작업을 처리
    //-> spring data jpa내부에서도 EntityManager를 사용
    private final EntityManager entityManager;
    @Override
    public void insert(PersonEntity dto) {
        //
        System.out.println("persist메소드 호출 전");
        entityManager.persist(dto);
        entityManager.persist(dto);
        entityManager.persist(new PersonEntity("1234","bts",100,"test"));
        entityManager.persist(new PersonEntity("2222","석진",10000,"test"));
        System.out.println("=======================================");
        System.out.println(dto.getId());

        //sql실행
        entityManager.flush();

        //캐시를 비우는 작업 - 캐시가 날라가므로 저장될 객체가 없어진다.
        entityManager.clear();

        //db에서 저장된 레코드를 조회하는 작업
        //select * from person where id=1
        //캐시에 저장되어있는 객체가 있으면 디비에서 조회하지 않고 캐시에서 객체를 꺼내온다
        //(그래서 여기서 sql 셀렉트문이 안만들어짐)
        PersonEntity data = entityManager.find(PersonEntity.class,dto.getId());
        System.out.println(data);
        System.out.println(dto==data);
        System.out.println("*********************************");

    }

    @Override
    public PersonEntity read(String id) {
        return entityManager.find(PersonEntity.class,Long.parseLong(id));
    }

    @Override
    public void delete(String id) {
        //조회한 후 삭제
        PersonEntity person = entityManager.find(PersonEntity.class,Long.parseLong(id));
        System.out.println("=========================삭제전===========================");
        System.out.println(person);
        System.out.println(person.getId());
        entityManager.remove(person);
    }

    @Override
    public void update(PersonEntity dto) {
        //조회 후 수정
        //수정할 객체를 조회한 후에 수정
        PersonEntity person = entityManager.find(PersonEntity.class,dto.getId());
        person.setPassword(dto.getPassword());
        person.setInfo(dto.getInfo());
    }
    /*
    JPA는 내부에서 객체지향 쿼리를 사용해서 조회
    객체지향쿼리 - JPQL
    JPQL을 이용하면 이미 셋팅되어있는 테이블에 맞춰서 쿼리가 만들어진다.
    복잡한 검색 조건이나 조인이 들어가있는 경우 적용이 가능
    select, from, where, group by, having, join 지원 가능
    밑엔 select * from Person이말임
    */
    @Override
    public List<PersonEntity> list() {
        List<PersonEntity> list =
                entityManager.createQuery("select p from PersonEntity as p",
                                PersonEntity.class).getResultList();
        return list;
    }
}

PersonService

public interface PersonService {
	void insert(PersonEntity dto) ;
	PersonEntity read(String id);
	void delete(String id);
	void update(PersonEntity dto);
	List<PersonEntity> list();
}

PersonServiceImpl

@RequiredArgsConstructor
@Service
public class PersonServiceImpl implements PersonService{
    private final PersonDAO dao;
    @Override
    public void insert(PersonEntity dto) {
        dao.insert(dto);
    }

    @Override
    public PersonEntity read(String id) {
        return dao.read(id);
    }

    @Override
    public void delete(String id) {
        dao.delete(id);
    }

    @Override
    public void update(PersonEntity dto) {
        dao.update(dto);
    }

    @Override
    public List<PersonEntity> list() {
        return dao.list();
    }
}

PersonDAOImplTest

@SpringBootTest
//하나의 메소드는 같은 트랜잭션으로 처리되어야함
@Transactional
//테스트작업시 롤백
@Rollback(value = false)
class PersonDAOImplTest {
    @Autowired
    PersonDAO dao;
    @Test
    @Disabled
    public void test(){
        //dao의 insert메소드를 테스트
        //insert할때 Entity객체를 매개변수로 전달
        //EntityManager의 persist메소드 내부에서 Entity객체의 정의된 내용을 보고 자동으로 insert문을 만들어서 저장
        System.out.println("--------------------");
        dao.insert(new PersonEntity("1234","bts",100, "기타"));
        System.out.println("--------------------");
    }

    @Test
    @Disabled
    public void readTest(){
        PersonEntity person = dao.read("3");
        System.out.println(person);
    }

    @Test
    @Disabled
    public void deleteTest(){
        dao.delete("3");
    }

    @Test
    @Disabled
    public void updateTest(){
        dao.update(new PersonEntity(1L,"7777","자유~~~"));
    }

    @Test
    public void listTest(){
        System.out.println(dao.list());
    }
}

본 포스팅은 멀티캠퍼스의 멀티잇 백엔드 개발(Java)의 교육을 수강하고 작성되었습니다.

0개의 댓글