LUMI_dev
Spring Data JPA - #2 Spring Data JPA 사용방법_JDBC 없애기(findAll, findById,update,delete) 본문
스파르타 코딩 클럽 | 자바 심화 과정/Spring Master (입문 주차)
Spring Data JPA - #2 Spring Data JPA 사용방법_JDBC 없애기(findAll, findById,update,delete)
luminous_dev 2025. 2. 4. 04:56JpaRepository 등록
- JpaRepository<"@Entity 클래스", "@Id 의 데이터 타입"> 상속받는 interface로 선언
- Spring Data JPA에 의해 자동으로 Bean 등록 되어있음
- 제네릭스의 @Entity 클래스 위치에 Memo Entity를 추가했기 때문에
해당 MemoRepository는 DB의 memo 테이블과 연결되어 CRUD 작업을 처리하는 인터페이스가 되었음

실습) 메모장 프로젝트 Spring Data JPA 적용
JDBC 기반의 memoRepository (수정 전)
package com.sparta.sparta_memo_project.repository;
import com.sparta.sparta_memo_project.dto.MemoRequestDto;
import com.sparta.sparta_memo_project.dto.MemoResponseDto;
import com.sparta.sparta_memo_project.entity.Memo;
import jakarta.persistence.EntityManager;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
public class MemoRepository {
private final JdbcTemplate jdbcTemplate;
public MemoRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Memo save(Memo memo) {
// DB 저장
KeyHolder keyHolder = new GeneratedKeyHolder(); // 기본 키를 반환받기 위한 객체
String sql = "INSERT INTO memo (username, contents) VALUES (?, ?)";
jdbcTemplate.update(con -> {
PreparedStatement preparedStatement = con.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS);
preparedStatement.setString(1, memo.getUsername());
preparedStatement.setString(2, memo.getContents());
return preparedStatement;
},
keyHolder);
// DB Insert 후 받아온 기본키 확인
Long id = keyHolder.getKey().longValue();
memo.setId(id);
return memo;
}
public List<MemoResponseDto> findAll() {
// DB 조회
String sql = "SELECT * FROM memo";
return jdbcTemplate.query(sql, new RowMapper<MemoResponseDto>() {
@Override
public MemoResponseDto mapRow(ResultSet rs, int rowNum) throws SQLException {
// SQL 의 결과로 받아온 Memo 데이터들을 MemoResponseDto 타입으로 변환해줄 메서드
Long id = rs.getLong("id");
String username = rs.getString("username");
String contents = rs.getString("contents");
//return new MemoResponseDto(id, username, contents);
return new MemoResponseDto(id, username, contents);
}
});
}
public void update(Long id, MemoRequestDto requestDto) {
String sql = "UPDATE memo SET username = ?, contents = ? WHERE id = ?";
jdbcTemplate.update(sql, requestDto.getUsername(), requestDto.getContents(), id);
}
public void delete(Long id) {
String sql = "DELETE FROM memo WHERE id = ?";
jdbcTemplate.update(sql, id);
}
public Memo findById(Long id) {
// DB 조회
String sql = "SELECT * FROM memo WHERE id = ?";
return jdbcTemplate.query(sql, resultSet -> {
if (resultSet.next()) {
Memo memo = new Memo();
memo.setUsername(resultSet.getString("username"));
memo.setContents(resultSet.getString("contents"));
return memo;
} else {
return null;
}
}, id);
}
@Transactional(propagation = Propagation.REQUIRED)
public Memo createMemo(EntityManager em) {
Memo memo = em.find(Memo.class, 1);
//데이터 바꾸기 + 트랜잭션을 걸어놨으니 dirtycheck 변경 감지 발생 > updateQuery 실행
memo.setUsername("Robbie");
memo.setContents("@Transactional 전파 테스트 중!");
System.out.println("createMemo 메서드 종료");
return memo;
}
}
JpaRepository로 대체한 memoRepository (수정 후)
package com.sparta.sparta_memo_project.repository;
import com.sparta.sparta_memo_project.dto.MemoRequestDto;
import com.sparta.sparta_memo_project.dto.MemoResponseDto;
import com.sparta.sparta_memo_project.entity.Memo;
import jakarta.persistence.EntityManager;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
//@Repository는 자동으로 제공되므로 없애주기 + 안 달아도 이제 Bean 등록도 됨
public interface MemoRepository extends JpaRepository<Memo, Long> {
//Long = @Id(PK) 의 데이터 형식
}
그럼 Service 단에선 다음과 같이 가져올 수 있음
근데 DAO에서 메서드 작성하던 것과 달리 JpaRepository에서 이미 작성된 메서드를 사용
ex)

package com.sparta.sparta_memo_project.service;
import com.sparta.sparta_memo_project.dto.MemoRequestDto;
import com.sparta.sparta_memo_project.dto.MemoResponseDto;
import com.sparta.sparta_memo_project.entity.Memo;
import com.sparta.sparta_memo_project.repository.MemoRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class MemoService {
private final MemoRepository memoRepository;
public MemoService(MemoRepository memoRepository) {
this.memoRepository = memoRepository;
}
public MemoResponseDto createMemo(MemoRequestDto requestDto) {
// RequestDto -> Entity
Memo memo = new Memo(requestDto);
// DB 저장
Memo saveMemo = memoRepository.save(memo);
// Entity -> ResponseDto
MemoResponseDto memoResponseDto = new MemoResponseDto(saveMemo);
return memoResponseDto;
}
public List<MemoResponseDto> getMemos() {
// DB 조회
return memoRepository.findAll().stream().map(MemoResponseDto::new).toList();
}
@Transactional
public Long updateMemo(Long id, MemoRequestDto requestDto) {
// 해당 메모가 DB에 존재하는지 확인
Memo memo = findMemo(id);
// memo 내용 수정
memo.update(requestDto);
return id;
}
public Long deleteMemo(Long id) {
// 해당 메모가 DB에 존재하는지 확인
Memo memo = findMemo(id);
// memo 삭제
memoRepository.delete(memo);
return id;
}
private Memo findMemo(Long id) {
return memoRepository.findById(id).orElseThrow(() ->
new IllegalArgumentException("선택한 메모는 존재하지 않습니다.")
);
}
}
메서드 정리
findAll | 해당 테이블의 전체 데이터 조회 |
findById | null 체크 및 처리 |
update | update는 없음/ 변경감지로 update |
delete | Entity (데이터)를 테이블에서 삭제 |
findAll
public List<MemoResponseDto> getMemos() {
// DB 조회
return memoRepository.findAll().stream().map(MemoResponseDto::new).toList();
}
SimpleJPARepository

findById
findById 메서드의 반환 타입은 Optional (null 체크를 해줘야 함)
이것을 간편하게 처리하기 위해서 orElseThrow를 사용
optional 참고 자료
update
@Transactional
public Long updateMemo(Long id, MemoRequestDto requestDto) {
// 해당 메모가 DB에 존재하는지 확인
Memo memo = findMemo(id); //공통된 부분은 findMemo라는 메소드로 빼주기
// memo 내용 수정
memo.update(requestDto);
return id;
}
private Memo findMemo(Long id) {
return memoRepository.findById(id).orElseThrow(() ->
new IllegalArgumentException("선택한 메모는 존재하지 않습니다.")
);
}
- SimpleJpaRepository에 update라는 메서드는 존재 x
- 영속성 컨텍스트의 변경감지를 통해 update를 진행
- 변경감지가 적용되기 위해 해당 메서드에 @Transactional을 추가
delete
public Long deleteMemo(Long id) {
// 해당 메모가 DB에 존재하는지 확인
Memo memo = findMemo(id);
// memo 삭제
memoRepository.delete(memo);
return id;
}

- delete 메서드를 사용해 해당 Entity(데이터)를 테이블에서 삭제
- 파라미터로는 삭제하려는 entity 객체
- delete 메서드에 이미 @Transactional이 적용되어 있음
전체 Service 코드
package com.sparta.sparta_memo_project.service;
import com.sparta.sparta_memo_project.dto.MemoRequestDto;
import com.sparta.sparta_memo_project.dto.MemoResponseDto;
import com.sparta.sparta_memo_project.entity.Memo;
import com.sparta.sparta_memo_project.repository.MemoRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class MemoService {
private final MemoRepository memoRepository;
public MemoService(MemoRepository memoRepository) {
this.memoRepository = memoRepository;
}
public MemoResponseDto createMemo(MemoRequestDto requestDto) {
// RequestDto -> Entity
Memo memo = new Memo(requestDto);
// DB 저장
Memo saveMemo = memoRepository.save(memo);
// Entity -> ResponseDto
MemoResponseDto memoResponseDto = new MemoResponseDto(saveMemo);
return memoResponseDto;
}
public List<MemoResponseDto> getMemos() {
// DB 조회
return memoRepository.findAll().stream().map(MemoResponseDto::new).toList();
}
@Transactional
public Long updateMemo(Long id, MemoRequestDto requestDto) {
// 해당 메모가 DB에 존재하는지 확인
Memo memo = findMemo(id);
// memo 내용 수정
memo.update(requestDto);
return id;
}
public Long deleteMemo(Long id) {
// 해당 메모가 DB에 존재하는지 확인
Memo memo = findMemo(id);
// memo 삭제
memoRepository.delete(memo);
return id;
}
private Memo findMemo(Long id) {
//memo가 널 값이 아니라면 memo가 반환, 널값이면 예외
return memoRepository.findById(id).orElseThrow(() ->
new IllegalArgumentException("선택한 메모는 존재하지 않습니다.")
);
}
}
변경 감지가 되려면 @Transactional이 제대로 적용되었는지 확인해보기
'스파르타 코딩 클럽 | 자바 심화 과정 > Spring Master (입문 주차)' 카테고리의 다른 글
Spring Data JPA - #4 Query Methods란? (1) | 2025.02.04 |
---|---|
Spring Data JPA - #3. JPA Auditing (자동으로 시간 값을 넣어주는 기능)적용하는 법(@EnableJpaAuditing) (0) | 2025.02.04 |
JDBC란? (0) | 2025.02.04 |
Spring Data JPA - #1 Spring Data JPA란? (1) | 2025.02.04 |
SpringBoot JPA_#1-3. 영속성 컨텍스트와 트랜잭션의 생명 주기_ (0) | 2025.02.04 |