Back-end/Spring

[Spring] Java Persistence API 사용

shoney9254 2022. 11. 6. 17:42
반응형

Java Persistence API의 개요

 


JPA를 사용을 위한 Dependency 추가와 설정

pom.xml 에 아래와 같이 추가한다. (h2는 버전이 1.4.197 이하로 했을 때 진행 됐었다. —>1.3.176)

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
		<groupId>com.h2database</groupId>
		<artifactId>h2</artifactId>
		<scope>runtime</scope>
		<version>1.3.176</version>
</dependency>

위처럼 pom.xml을 설정하고 http://localhost:8088/h2-console로 접속하면 아래와 같이 로그인 화면이 나온다.(기존에 설정한 유저와 패스워드를 입력하면된다.)

하지만, 이전 시간에 적용한 security 때문에 접속이 안된다.

그래서 SecurityConfig.java 에서 아래와 같은 오버라이드 메서드로 해당 접속할때만 옵션이 꺼놓도록 하자

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests().antMatchers("/h2-console/**").permitAll();
    http.csrf().disable();
    http.headers().frameOptions().disable();
}

컨넥트 하려는 정보는 아래와 같이 mem에 testdb로 접근하자.

Connet를 누르면 아래와 같이 접속되는 것을 확인할 수 있다.


 

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

유저 도메인 클래스를 엔티티와 연동하기 위한 작업을 한다.

유저 도메인에다가 엔티티 어노테이션을 추가하면, 해당 클래스로 테이블이 추가된다.

클래스에 선언된 필드를 기준으로 테이블의 컬럼을 만들어준다.

고유한 키값을 설정하기 위한것은 id 필드 위에 어노테이션 id를 설정해주면 된다.

어노테이션 GeneratedValue를 사용하면 자동으로 값이 생성된다.

JPA 로그를 확인하려면 아래와 같이 yml파일에다가 설정을 해줘야한다.

spring:
  jpa:
    show-sql: true

User 도메인 클래스에 아래와 같이 어노테이션을 작성하자.

@Entity
//@JsonFilter("UserInfo")
@ApiModel(description = "사용자 상세 정보를 위한 도메인 객체")
public class User {
    //lombok으로 프로퍼티만 만들어도 됨
    @Id
    @GeneratedValue
    private Integer id;

이렇게 하고 스프링부트를 실행하면 아래와 같은 콘솔 로그를 확인할 수 있다.

Hibernate: drop table if exists user CASCADE

Hibernate: drop sequence if exists hibernate_sequence

Hibernate: create sequence hibernate_sequence start with 1 increment by 1

Hibernate: create table user (id integer not null, join_date timestamp, name varchar(255), password varchar(255), ssn varchar(255), primary key (id))

 

그리고 h2-console의 JDBC URL 을 콘솔 로그에서 확인해서 붙여넣기 해야한다.

2022-11-06 14:56:37.980 INFO 10870 --- [ restartedMain] o.s.b.a.h2.H2ConsoleAutoConfiguration : H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:d48c3fac-f9f0-4c08-8dba-f6e778c44420’

 

여기서 보면 jdbc mem 주소를 알수있다. 이걸 그대로 복사해서 붙여넣기를 하자.

h2 콘솔에 접속해보면 아래와 같이 User라는 테이블이 추가 된 것을 확인할 수 있다.

쿼리를 실행할 수 있다. 하지만, 지금은 비어있는 테이블이기 때문에 값이 조회되지 않는다.

resource 폴더에다가 data.sql 파일을 생성한다.

해당 파일에 아래와 같이 인서트문을 작성한다.

insert into user values(1, sysdate(), 'User1', 'test1','999999-1111111');
insert into user values(2, sysdate(), 'User2', 'test2','899999-1111111');
insert into user values(3, sysdate(), 'User3', 'test3','799999-1111111');

그런데, hibernate가 초기화 되기 전에 data.sql이 실행되면서 USER테이블을 찾을 수 없다는 에러가 발생했다. 해당 에러가 발생한다면 yml에 아래와 같이 설정을 추가해야한다.

spring:
  jpa:
    defer-datasource-initialization: true

다시, USER 테이블의 데이터를 조회해보면 조회되는 것을 확인할 수 있다.


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

User Repository 인터페이스를 생성한다.

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {

}

JpaRepository를 상속 받는데, 어떤 형식으로 받을 것인지 타입을 지정해줘야한다.

어떠한 엔티티를 다룰 것인지 먼저 User를 입력하고, ID로 사용하는 것이 Integer 타입이기 때문에 위와 같이 작성한다.

UserJapController를 생성한다.

@RestController
@RequestMapping("/jpa") //이렇게 하면 모든 url앞에 해당 스트링 값이 추가된다.
public class UserJpaController {

    @Autowired
    private UserRepository userRepository;

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

}

UserRepository를 의존성 주입을 위해서 @Autowired를 작성한다.

그리고 findAll을 사용하기 위해서 retrieveAllUsers 메서드를 만들었다.

(@RequestMapping을 이용하면 모든 url앞에다가 추가된다.)

스프링부트를 실행하고 포스트맨에서 조회 해보면 정상적으로 조회되는 것을 확인 할 수 있다.


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

UserJapController.java 에서 아래와 같은 코드를 추가한다.

@GetMapping("/users/{id}")
public EntityModel<User> retrieveUser(@PathVariable int id) {
    Optional<User> user = userRepository.findById(id);

    if (!user.isPresent()) {
        throw new UserNotFoundException(String.format("ID[%s] not found", id));
    }

    // HATEOAS 작업
    EntityModel<User> model = EntityModel.of(user.get());
    WebMvcLinkBuilder linkTo =linkTo(methodOn(this.getClass()).retrieveAllUsers());
    model.add(linkTo.withRel("all-users"));

    return model;//옵셔널 객체는 이런식으로 겟 하면 사용할 수 있다.
}

userRepository는 JpaRepository를 상속받았는데, findById는 Optional 객체를 통해서 반환이 가능하게 되어있다. (user가 없는 경우를 대비하기 위함)

Optional 객체에서 User를 가져오는 방법은 user.get()으로 유저를 불러올 수 있다.

위와 같이 소스 코드를 작성하면, 아래와 같은 결과를 받을 수 있다.

 


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

삭제 메서드 구현

@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable int id) {
    userRepository.deleteById(id);

}

추가 메서드 구현

@PostMapping("/users")
public ResponseEntity<User> creatUser(@Valid @RequestBody User user) {
    User savedUser = userRepository.save(user);

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

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

URI 객체로 리턴하는 이유는 아래와 같이 추가 됐을 때 정상적으로 들어갔는지 확인하기 위해서입니다. 어떤 값으로 조회 가능한지 헤더의 로케이션에서 조회 가능하게됩니다.


게시물 관리를 위한 Post Entity 추가와 초기 데이터 생성

Post 도메인을 추가한다.

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Post {

    @Id
    @GeneratedValue
    private Integer id;

    private String description;

    // User : Post -> 1: N
    // User가 메인테이블 (parent)
    @ManyToOne(fetch = FetchType.LAZY) //지연 로딩 방식 : 사용자 엔티티를 조회할 때 매번 포스트 엔터티를 조회한다는 뜻이 아니라,포스트 데이터가 로딩 될 때, 필요한 사용자 데이터를 로딩한다는 뜻
    @JsonIgnore //제이슨으로 보여질 때, 보여지지 않게 해줌
    private User user;

}

User: Posts = 1: N 관계이다. 그래서 Post는 Many이고, User는 One이라고 생각하면 어노테이션을 쉽게 적용할 수 있다. User 필드 위에 @ManyToOne 어노테이션을 작성한다. fetch 타입을 Lazy로 지정하면서, 필요할때만 user 엔터티를 조회 하는 방식을 사용한다.

기존에 있던 User 도메인에도 Post 리스트를 추가한다.

@OneToMany(mappedBy = "user")
private List<Post> posts;

여기서도 마찬가지로 유저가 One이고 포스트가 Many이기 때문에 어노테이션을 @OneToMany로 적용한다.

data.sql에는 포스트 정보를 초기화 하도록 소스코드를 추가한다.

insert into post values(10001, 'My first post', 90001);
insert into post values(10002, 'My second post', 90001);

이제 h2-console을 통해서 db가 잘 생성 됐는지 확인해 보자.


게시물 조회를 위한 Post Entity와 User Entity와의 관계 설정

@GetMapping("/users/{id}/posts")
public List<Post> retrieveAllPostsByUser(@PathVariable int id) {
    Optional<User> user = userRepository.findById(id);

    if (!user.isPresent()) {
        throw new UserNotFoundException(String.format("ID[%s] not found", id));
    }

    return user.get().getPosts();
}

이렇게 설정을 하면 아래와 같이 조회 할 수 있다.


JPA를 이용한 새 게시물 추가 - POST HTTP Method

@PostMapping("/users/{id}/posts")
public ResponseEntity<Post> creatPost(@PathVariable int id, @RequestBody Post post) {
    Optional<User> user = userRepository.findById(id);

    if (!user.isPresent()) {
        throw new UserNotFoundException(String.format("ID[%s] not found", id));
    }

    post.setUser(user.get());
    Post savedPost = postRepository.save(post);

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

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

위처럼 작성하면, 새 개시물 추가할 수 있다.

전체 유저 조회 시 게시물이 등록된 것을 확인할 수 있다.

 

반응형