공부/Spring

RESTful Web Service 개발 - Java Persistence API 사용_2

데부한 2023. 1. 26. 22:52
반응형

 

 

Spring Boot를 이용한 RESTful Web Services 개발

Spring Data JPA를 이용한 Entity 설정과 초기 데이터 생성

  • application.yml

jpa 로그를 볼 수 있도록 설정을 해주자.  난 설정되어있었다.

spring:
	jpa:
      show-sql: true

 

  • User.java
@Entity
public class User {
    
    @Id
    @GeneratedValue
    private Integer id;
   	...
}
  • @Entity : 이 어노테이션을 붙여주면 해당 클래스의 이름으로 클래스 필드에 있는 정보들을 기반으로 데이터베이스 테이블을 생성한다.
  • @Id : 레코드를 구분하기 위한 기본키 설정. 여기선 id가 기본키가 된다.
  • @GeneratedValue : 자동 생성되는 키 값이란 의미

 

서버 실행 후 로그 확인. create table 검색

..? 에러가 발생했다. 위 에러는 'User'라는 이름이 데이터베이스의 예약어이기 때문에 발생하는 오류이다. User.java 클래스 이름을 Users.java로 변경해주자.

클래스 이름 변경 후 alt+enter > Rename usages of 'User' to 'Users'

Ignore

OK

OK

User를 사용한 클래스마다 들어가서 변경해줘야할 줄 알았는데 한방에 되서 다행이다... 서버 재실행 후 콘솔을 다시 확인해보자.

에러 없이 테이블이 정상적으로 만들어졌다. 로그를 자세히보면 drop으로 users 테이블이 존재하면 삭제, 시퀀스가 존재하고 있으면 삭제 후 시퀀스를 생성하고 users 테이블을 생성한다. 그렇기 때문에 서버를 재실행하면 users란 테이블이 매번 새로 만들어진다. 서버를 재실행하기 전 데이터는 유지가 되지 않는다는 이야기다. 

반응형

 

이번엔 h2 웹 페이지에서 테이블을 확인해보자.

테이블이 아주 잘 생성되어있다. 데이터가 비어있으니 초기 데이터 값을 넣어주자.

  • data.sql 생성 (파일명은 아무거나해도 상관없지만 확장자는 '.sql'이어야 한다.

insert into users values(1, now(), 'user1', 'test1', '700000-1111111');
insert into users values(2, now(), 'user2', 'test2', '800000-2222222');
insert into users values(3, now(), 'user3', 'test3', '900000-3333333');

세 개의 더미 데이터 입력 후 서버를 재실행한다.

..? 또 오류가 발생했다. 메세지를 보니까 Users라는 테이블을 못 찾는다는 내용의 에러였다. Create table보다 insert 쿼리가 먼저 실행되는 듯 했다.

  • application.yml
spring:
    jpa:
      show-sql: true
      defer-datasource-initialization: true

defer-datasource-initialization의 속성을 true로 변경하면 create table이 먼저 실행되서 에러가 발생하지 않는다.

스크립트 기반의 DataSource의 초기화는 기본적으로 JPA의 EntityManagerFactory 빈을 생성하기 전에 수행된다고 한다. 그렇기 때문에 create table 전에 data.sql이 실행되어 에러가 발생하는 것이였다. defer-datasource-initialization 이 속성을 true로 주면 EntityManagerFactory 빈을 생성하고 초기화를 마칠때까지 DataSource 초기화를 미룬다.

반응형

서버를 재실행하고 h2 웹 페이지에서 테이블을 조회해보자.

 

 

JPA Service 구현을 위한 Controller, Repository 생성

  • UserRepository.interface 생성

@Repository // database에 관련된 bean이라고 알려줌
//JpaRepository<객체, 객체의 id 데이터 타입>
public interface UserRepository extends JpaRepository<Users, Integer> {
}

상속받는 JpaRepository interface를 들여다보면 여러가지 메서드들이 있다. JpaRepository를 상속하는 것만으로도 이 메서드들을 통해 기본적인 CRUD 기능을 만들 수 있다.

 

  • UserJpaController.java 생성

@RestController
@RequestMapping("/jpa")
public class UserJpaController {

    @Autowired
    private UserRepository userRepository;

    // http::localhost:8088/jpa/users
    @GetMapping("/users")
    public List<Users> retrieveAllUsers() {
        return userRepository.findAll();
    }
    
}

 

서버 재실행 후 포스트맨 확인

 

반응형

 

JPA를 이용한 사용자 목록 조회 - GET HTTP Method

  • JpaRepository.interface
@Override
List<T> findAllById(Iterable<ID> ids);

JpaRepository에 있는 findAllById()를 이용해 개별 사용자 목록을 조회해보자.

 

  • UserJpaController.java
@GetMapping("/users/{id}")
public EntityModel<Users> retrieveUser(@PathVariable int id) {
    Optional<Users> user = userRepository.findById(id);
    if(!user.isPresent())
        throw new UserNotFoundException(String.format("ID[%s] not found", id));

    // 헤테오스
    //Resource<Users> resource = new Resource<>(user.get());
    EntityModel<Users> entityModel = EntityModel.of(user.get());
    WebMvcLinkBuilder linkTo = WebMvcLinkBuilder
            .linkTo(methodOn(this.getClass()).retrieveAllUsers());

    entityModel.add(linkTo.withRel("all-users"));

    return entityModel;
}

사용자 개별 조회 메서드를 작성했다. 'findById()'를 사용하고 매개변수로 id 값을 넘겨주어 개별 사용자 조회를 할 수 있도록 하였다. JpaRepository 인터페이스의 findById()의 반환 타입이 Optional<T>라 user의 타입을 Optional<Users>로 주었다.

// JpaRepository.interface
Optional<T> findById(ID id);

'isPresent()'는 Optional에 데이터가 있는지 없는지 확인해주며 있으면 true, 없으면 false를 반환한다. user 객체에 데이터가 없으면 저번에 만들었던 UserNotFoundException 예외가 발생하도록 작성했다.

헤테오스 기능까지 입혀봤는데. 강의에서 나오는 Resource<User>.... 코드는 옛날 헤테오스 사용법이라 에러가 발생했다. 그래서 EntitiyModel을 이용하여 헤테오스를 작성했다. 

서버 재실행 후 포스트맨 확인

반응형

 

 

JPA를 이용한 사용자 추가와 삭제 - POST/DELETE HTTP Method

DELETE

  • UserJpaController.java
@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable int id) {
    
    //userRepository.deleteById(id)
    Optional<Users> findUser = userRepository.findById(id);
    
    if(!findUser.isPresent()) 
        throw new UserNotFoundException(String.format("ID [%s] not found", id));
    
    userRepository.deleteById(findUser.get().getId());
}

강의에서는 주석처리 된 한 줄만 작성되었는데 나는 뭔가 id 값이 없을 경우도 있을 거 같애서 내 마음대로 코드를 좀 더 추가해봤다. id 값으로 일단 사용자가 존재하는지, 아닌지 판별 후에 존재하지 않으면 ID가 존재하지 않는다는 예외를 발생시키고, 존재한다면 delete 되도록 작성했다.

서버를 재실행 후 포스트맨 확인

제대로 된 건지는 모르겠지만 일단 200 OK가 떨어졌다. 사용자 전체 목록 조회도 해보자.

1번 회원이 삭제되었다. 1번 회원을 다시 삭제해보면 아래와 같은 메세지를 볼 수 있다.

반응형

 

POST

  • UserJpaController.java
@PostMapping("/users")
public ResponseEntity<Users> createUser(@Valid @RequestBody Users user) {
    Users savedUser = userRepository.save(user);

    URI location = ServletUriComponentsBuilder.fromCurrentRequest()
            .path("/{id}")
            .buildAndExpand(savedUser.getId())
            .toUri();

    return ResponseEntity.created(location).build();
}

새로운 사용자를 추가하는 메서드이다. save()의 경우 정상적으로 저장이 되었으면 user 객체를 return해주기 때문에 별다른 로직 추가는 하지 않았다. 

 

서버 재실행 후 포스트맨 확인

에러가 발생한다. 괜찮다. 이번엔 나만 나는 에러는 아니다. 에러가 왜 났냐면 더미 데이터로 생성한 user 데이터에서 id 값이 1~3까지 쓰고 있어서 primary key가 중복되어 발생하는 에러이다. 시퀀스를 활용해 id의 값이 저장되어 1부터 시작이라 그렇다. 뭐.. send 버튼을 두 번 누른 후에 데이터를 추가해보면 정상적으로 추가된다. 그래도 이상하니까 더미 데이터의 id 값을 변경해주자.

  • data.sql
insert into users values(9901, now(), 'user1', 'test1', '700000-1111111');
insert into users values(9902, now(), 'user2', 'test2', '800000-2222222');
insert into users values(9903, now(), 'user3', 'test3', '900000-3333333');

 

서버 재실행 후 포스트맨 확인

에러 없이 정상적으로 실행되었다. (201 코드 확인)

반응형

사용자 전체 조회를 했을 때에도 조회가 잘 된다.

 

 


- 출처 : 인프런 Spring Boot를 이용한 RESTful Web Services 개발 강의

 

반응형