카테고리 없음

JPA란?

worldstroy 2025. 4. 13. 09:04

JPA(Java Persistence API) - 자바 진영의 ORM 기술 표준 

JPA의 특징 JPA가 개발자를 대신하여 적합한 SQL을 생성하여 DB에 전달하고, 객체를 자동으로 Mapping 해주기에 SQL을 직접 작성하지 않음
ex. Hibernate(JPA를 구현한 대표적인 오픈소스)
JPA의 장점 DBMS에 대한 종속성이 줄어들어  생산성이 뛰어나고 유지보수가 용이하다
JPA의 단점 JPA의 장점을 살려 잘 사용하기 위해서는 학습 비용이 높고, 복잡한 쿼리를 사용할 때 불리하다.
잘못 사용할 경우,  SQL을  직접 사용하는 것보다 성능저하 이슈가 발생할 수 있다.

 

JPA를 실제로 사용하는 방법

1. build.gradle 파일의 dependencies에 JPA 관련 항목을 추가한다.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.mysql:mysql-connector-j' // Database가 MySQL임을 알려줌
2. JPA를 구현하기 위한 기초적인 application.properties 정보를 설정한다.
spring.datasource.driver-class-name=cohttp://m.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/milkway?useUnicode=yes&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Seoul
spring.datasource.username=root
spring.datasource.password=1234
spring.database=milkway
spring.jpa.hibernate.ddl-auto=update    
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
3. 데이터베이스 설정에 맞는 Entity 클래스를 실제로 작성한다.

 

@Entity
@Table(name = "Inqurie")
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Builder
public class InquireEntity
{
    @Id
    @Column(name = "inquireId")
    private String inquireId;
    @Column(name = "address", nullable = false)
    private String address;
    @Column(name = "phoneNumber", nullable = false)
    private String phoneNumber;
    @Column(name = "inquire", nullable = false)
    private String inquire;
}

 

데이터베이스 테이블과 매핑되는 JAVA 클래스로 데이터베이스에 쓰일 여러 Entity 간의 관계를 설정하는 것
@Entity를 이용해 해당 클래스가 Entity 임을 알려주고, JPA에 정의된 필드를 바탕으로 데이터베이스에 테이블을 제작할 수 있다.
다른 말로는 도메인(domain) 모델이라고도 함.
Enttiy 관련 Annotation
@Entity : 해당 클래스를 엔터티로 인식할 수 있도록 하며, 데이터베이스 테이블과 매핑됨
@Table : 엔터티와 매핑할 데이터베이스 테이블 지정
@Id : primary key 지정
@GeneratedValue : primary key의 생성 전략
GenerationType.IDENTITY: 고유한 번호를 자동으로 생성함
GenerationType.AUTO : 데이터베이스 설정에 따라 적절한 전락을 자동으로 선택함
@Colum : 엔터티 필드와 매핑할 테이블의 칼럼 지정
@PrePersist : 엔터티가 데이터베이스에 저장되기 전에 필요한 초기화 작업을 수행함
4. 데이터베이스 설정에 맞는 DTO 객체를 작성한다.

 

@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class InquireDTO
{

    private String inquireId;
    private String address;
    private String phoneNumber;
    private String inquire;

}

 

@Builder를 사용할 때에는 생성자 관련 어노테이션이 반드시 필요하다.
5.  설정한 Entity 정보를 이용해 테이블 데이터에 접근(CRUD)할 수 있도록 하는 Repository를 설계한다.

 

@Repository
public interface InqurieRepository extends JpaRepository<InquireEntity, String>
//<>는 Entity와 PK의 타입이 들어감, PK의 타입의 경우 Wapper 클래스로 기입할 것 
{
    InquireEntity findByInquireId(String InquireId);
    boolean existsByInquireId(String InquireId);
    void deleteByInquireId(String InqurieId);
}

 

JPA Repository 인터페이스의 함수명의 경우, JPA 홈페이지에 존재하는 설정 메뉴얼에 따라서 지어야 한다.
대소문자 차이나 오탈자 등을 허용하지 않으니, 설정 후 다시 한번 꼼꼼하게 확인할 것.
save() : 데이터 저장, findAll() : 전체 데이터 조회, findById() : ID 조회는 인터페이스에 자동적으로 포함되는 함수임
6. 설정한 Entity, Repository를 이용해서 관련 설정을 실제 수행하는 Service 클래스를 작성한다.

 

	@Service
	public class InquireService
	{
    	@Autowired
    	InqurieRepository inqurieRepository;

    	public InquireEntity Insert(InquireEntity inquireEntity)
    	{
        	boolean bool = inqurieRepository.existsByInquireId(inquireEntity.getInquireId());
        	if(bool)
        	{
            	throw new FindFailedException("해당 inquireId를 가진 정보는 이미 존재해요.");
        	}
        	else
        	{
            	InquireEntity inquireEntity1 = inqurieRepository.save(inquireEntity);
            	if (inquireEntity1 != null) {
                	return inquireEntity1;
            	} else {
                	throw new InsertFailedException("날짜 가능 문의 등록이 실패하였습니다.");
            	}
         }
      }
    }

 

본 코드는 객체 주입을 통해 Repository interface를 실행하는 함수를 주입 받아, 사용하는 Service 클래스의 예제이다.
7. 마지막으로 URL에 따라 정해진 명령을 수행하도록 Service 객체에 따른 URL을 Controller 클래스에 매핑한다.

 

@RestController
@RequestMapping("/inqurie")
public class InqurieController
{
    @Autowired
    InquireService inquireService;
    
    ResponseDTO<InquireDTO> responseDTO = new ResponseDTO<>();

    LoginSuccess loginSuccess = new LoginSuccess();

    @PostMapping("/Insert")
    public ResponseEntity<?> Insert(@RequestBody InquireDTO inquireDTO)
    {
        try
        {
            InquireEntity inquireEntity1 = ConvertToEntity(inquireDTO);
            InquireEntity inquireEntity2 = inquireService.Insert(inquireEntity1);
            if (inquireEntity2 != null)
            {
                InquireDTO inquireDTO1 = ConvertToDTO(inquireEntity2);
                return ResponseEntity.ok().body(responseDTO.Response("success", 
                "데이터 추가에 성공했습니다.", Collections.singletonList(inquireDTO1)));
            }
            else
            {
                throw new InsertFailedException("inquire 저장 실패, 정보를 가져올 수 없어요");
            }
        }
        catch (Exception e)
        {
            return ResponseEntity.badRequest()
            .body(responseDTO.Response("error",e.getMessage()));
        }
    }

 

8. 이후 설계한 기능이 정상적으로 동작하는지 PostMan이나 Swagger를 이용해서 Test를 수행한다.