Java/Spring & Spring Boot

Spring Data JDBC

마손리 2023. 4. 20. 23:08

Spring Data JDBC

Spring Data JDBCSpring에서 데이터베이스에 접근하는 다양한 기술들중 하나JDBC API 안에서 작동한다. 또한 ORM기술이 적용되어 SQL문을 직접 작성하는 것이 아닌 객체를 통해 데이터베이스에 접근 할수 있는 기술이다. 

 

ORM (Object Relational Mapping) 이란 객체 지향 프로그래밍 언어와 SQL문을 맵핑해주는, 즉, 두 언어를 서로 변환시켜 관계형 데이터베이스에 접근 할수 있도록 해주는 기술이다.

 

 

Spring Data JDBC 사용

라이브러리 추가

dependencies {
	...
	...
	implementation 'org.springframework.boot:spring-boot-starter-data-jdbc' //Spring Data JDBC
	runtimeOnly 'com.h2database:h2' // h2 (In-memory DB)
}

 

h2 사용을 위해 src/main/resources의 application.properties의 확장자를 application.yml로 변경한뒤 설정 추가

spring:
  h2:
    console:
      enabled: true
      path: /h2     # Context path
  datasource:
    url: jdbc:h2:mem:test     # JDBC URL
  sql:
    init:
      schema-locations: classpath*:db/h2/schema.sql
      data-locations: classpath*:db/h2/data.sql
logging:    #DB logging
  level:
    org:
      springframework:
        jdbc:
          core: TRACE

여기서 주의할점은 schema.sql의 내용이 비어있으면 런타임 에러가 발생하므로 적당히 스키마를 추가해주거나 schema-locations 옵션을 지워주자

 

 

DTO와 Entity, Controller 작성

DTO

@Getter
public class MemberPostDto {
    @NotBlank
    @Email
    private String email;
    @NotBlank(message = "이름은 공백이 아니어야 합니다.")
    private String name;
    @Pattern(regexp = "^010-\\d{3,4}-\\d{4}$",
            message = "휴대폰 번호는 010으로 시작하는 11자리 숫자와 '-'로 구성되어야 합니다.")
    private String phone;
}

Entity

@Getter
@Setter
public class Member {
    @Id
    private Long memberId;
    private String email;
    private String name;
    private String phone;
}

Controller

@RestController
@RequestMapping("/v10/members")
@Validated
@Slf4j
public class MemberController {
    private final static String MEMBER_DEFAULT_URL = "/v10/members";
    private final MemberService memberService;
    private final MemberMapper mapper;

    public MemberController(MemberService memberService, MemberMapper mapper) {
        this.memberService = memberService;
        this.mapper = mapper;
    }

    @PostMapping
    public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberDto) {
        Member member = mapper.memberPostDtoToMember(memberDto);
        Member resultMember = memberService.createMember(member);

        URI location = UriCreator.createUri(MEMBER_DEFAULT_URL, resultMember.getMemberId());
		// URI를 이용하여 ResponseBody를 작성할수 있다하여 시도해봣지만 실패했다... 
        return ResponseEntity.created(location).build();
    }

    @GetMapping("/{member-id}")
    public ResponseEntity getMember(
            @PathVariable("member-id") @Positive long memberId) {
        Member response = memberService.findMember(memberId);
        return new ResponseEntity<>(mapper.memberToMemberResponseDto(response)
                , HttpStatus.OK);
    }
}

 

 

Spring Data JDBC의 ORM 기능을 사용하기 위한 repository 인터페이스 생성

(이전까지는 Spring Data JDBC를 사용하기 위한 사전단계였다면 지금부터 직접 사용한다.)

public interface MemberRepository extends CrudRepository<Member, Long> {//엔티티와 Primary Key의 타입
    Optional<Member> findByEmail(String email);
    // Optional을 이용하면 여러가지 편리한 기능 사용가능
    
    // @Query("SELECT * FROM MEMBER WHERE EMAIL = :email")
    // Optional<Coffee> findByEmailUsingQuery(String email);
}

CrudRepository를 구현하여 Spring Data JDBC의 기본적인 ORM을 사용할수 있으며 findByEmail 메서드와 같이 지정된 네이밍 법칙을 이용해 메서드를 추상화해주어 로직작성 없이도 간단하게 커스텀해 줄수 있다.

또한 @Query 에너테이션을 이용하여 새로운 쿼리문을 작성도 가능

 

Service에서 repository사용

@Service
public class MemberService {
    private final MemberRepository memberRepository;
    
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

	// 실제 로직
    public Member createMember(Member member) {
        // 이미 등록된 이메일인지 확인
        verifyExistsEmail(member.getEmail());

        return memberRepository.save(member);//CrudRepository의 save() ORM
    }
    // Patch요청에 대한 컨트롤러와 dto는 없음, 예시를 위해 서비스에만 작성
    public Member updateMember(Member member) {
        Member findMember = findVerifiedMember(member.getMemberId()); //verification

		// Optional을 이용하여 Null체크
        Optional.ofNullable(member.getName())
                .ifPresent(name -> findMember.setName(name));
        Optional.ofNullable(member.getPhone())
                .ifPresent(phone -> findMember.setPhone(phone));

        return memberRepository.save(findMember); 
    }
    public Member findMember(long memberId) {
        return findVerifiedMember(memberId);
    }
    
    // 예외 체크 메서드
    public Member findVerifiedMember(long memberId) {
        Optional<Member> optionalMember = memberRepository.findById(memberId);//CrudRepository 기본 ORM
        Member findMember =
                optionalMember.orElseThrow(() ->	// Null일 경우 예외 강제 발생
                        new BusinessLogicException(ExceptionCode.MEMBER_NOT_FOUND));
        return findMember;
    }
    
    private void verifyExistsEmail(String email) {
        Optional<Member> member = memberRepository.findByEmail(email); //Custom ORM
        if (member.isPresent()) // Null체크, boolean 리턴
            throw new BusinessLogicException(ExceptionCode.MEMBER_EXISTS);
    }
}

위와 같이 Optional과 CrudRepository를 이용하여 간단히 데이터베이스와 연동

'Java > Spring & Spring Boot' 카테고리의 다른 글

JPA(Java Persistence API)  (0) 2023.04.23
Spring Data JDBC에서 Offset-Pagination 적용하기  (0) 2023.04.21
JDBC(Java Database Connectivity)  (0) 2023.04.20
Exception, 예외 처리  (0) 2023.04.18
Entity와 Mapper  (0) 2023.04.13