쌓고 쌓다
[스프링 부트] MySQL 연결 및 JPA - 2 본문
build.gradle
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' //추가 작성
implementation 'mysql:mysql-connector-java:8.0.29' // 추가 작성
...
}
java뒤에 MySQL 버전을 적어주자 -> java:<MySQL버전>
안그러면 제대로 가져오지 못해 com.mysql.cj.jdbc.Driver에 빨간줄이 뜬다.
application.properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/board
spring.datasource.username=root
spring.datasource.password=1234
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
board는 MySQL를 통해 내가 생성한 DB 이름임.
spring.jpa.show-sql=true? 아래와 같이 실행한 쿼리를 보여줌.
MySQL 테이블 생성
CREATE TABLE poster (
id bigint not null auto_increment,
title varchar(100),
writer varchar(100),
content varchar(300),
PRIMARY KEY (id)
);
자바 long은 bigint와 매칭? 맞는 자료형이라고 언뜻 들은듯하다...!
Poster
@Entity
public class Poster {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String writer;
private String content;
...
Getter, Setter
...
}
ORM은 객체-관계(Relational)-매핑의 약자이다.
객체와 Relational DB를 매핑한다는 뜻!
매핑은 어노테이션으로한다.
@Entity: JPA가 관리하는 엔티티가 된다.
@Id: PK라고 명시.
@GeneratedValue: poster의 PK는 DB에서 자동 생성하고 있었다. 이런 방식을 IDENTITY 전략이라고 함.
(시퀀스나 직접 넣는 전략도 따로 있다.)
1:N처럼 외래키를 가지는 객체는 어떻게 작성할지 궁금한데.. 나중에 공부해보자.!
@Column?
만약, 클래스 변수는 writer인데 DB의 컬럼명이 username이라고 되어있다면 아래와 같이 어노테이션을 사용한다.
@Entity
public class Poster {
...
@Column(name="username")
private String writer;
}
JpaPosterRepository
@Repository
public class JpaPosterRepository implements PosterRepository{
private final EntityManager em;
@Autowired
public JpaPosterRepository(EntityManager em) {
this.em = em;
}
@Override
public Poster save(Poster poster) {
em.persist(poster);
return poster;
}
@Override
public Optional<Poster> findById(Long id) {
return Optional.empty();
}
@Override
public Optional<Poster> findByTitle(String title) {
return Optional.empty();
}
@Override
public List<Poster> findAll() {
return em.createQuery("SELECT p FROM Poster as p", Poster.class)
.getResultList();
}
}
JPA는 EntityManger라는 것과 함께 모든 동작이 이뤄진다.
em이 DB 커넥션 정보 등 가지고 처리함.!
em.persist: INSERT 쿼리를 생성해 알아서 집어넣어준다. 반환 값이 없다.
findById
@Override
public Optional<Poster> findById(Long id) {
Poster poster = em.find(Poster.class, id);
return Optional.ofNullable(poster);
}
return 값이 Optional이고 Null 값이 나올 수 있기에 Optional.ofNullable()로 감싸서 반환.
findAll
@Override
public List<Poster> findAll() {
return em.createQuery("SELECT p FROM Poster as p", Poster.class)
.getResultList();
}
EntityManager의 createQuery 메소드는 JPQL 문법을 사용.
원래 테이블을 대상으로 쿼리를 날리지만 엔티티(객체)를 대상으로 쿼리를 날림. 이게 SQL로 번역됨.
보통 SELECT p.id로 가져오지만 SELECT p로 객체 자체를 SELECT 함.
getResultList: 쿼리로부터 반환된 결과가 하나 이상인 경우 리스트를 반환. 비어있는경우 비어있는 리스트임.
findByTitle (파라미터 바인딩)
@Override
public Optional<Poster> findByTitle(String title) {
List<Poster> posters = em.createQuery("SELECT p FROM Poster p WHERE p.title = :title", Poster.class)
.setParameter("title", title)
.getResultList();
return posters.stream().findAny();
}
(1)을 보자.
매개변수 title이 setParameter 함수에 의해
"title"이라는 키로 매개변수 title 값을 갖게 된다.
(2)를 보자
키 title이 :title로 들어간다.
이름을 바꿔 한번 살펴보면.
키(Key)로 title2를 하고.
값으로 매개변수 title의 값을 set한다.
그 키와 값을 쿼리에 사용함.
반환값이 Optional<Poster>이기에 하나만 반환하게 findAny 메소드 사용함.
여긴 @Autowired안해도 왜 되는가? 생성자 하나는 자동? 아니면 em은 스프링이 알아서?
@Autowired
public JpaPosterRepository(EntityManager em) {
this.em = em;
}
단일 생성자는 어노테이션을 붙이지 않아도 된다.
에러 - @Transactional
서비스 클래스에 @Transactional 어노테이션을 추가함으로써 해결한다.
JPA 사용시 항상 트랜잭션이 있어야한다.
데이터를 저장하거나 변경할때.
서비스에 @Transactional을 붙여주자.
write 함수에 @Transactional을 붙여도 무방하다.
최종 결과
+ 생성자 주입 vs 필드 주입
다음 목표: 게시글 상세 조회, 작성일 추가
'프로그래밍 > spring' 카테고리의 다른 글
[스프링 부트] 게시글 상세보기 with @RequestParam - 4 (0) | 2023.06.27 |
---|---|
Thymeleaf(타임리프) LocalDateTime 출력 및 작성일 만들기 - 3 (0) | 2023.06.25 |
[스프링 부트] MySQL 연결 com.mysql.cj.jdbc.driver 에러(빨간줄) (0) | 2023.06.24 |
[스프링 부트] 게시글 작성 및 전체 출력 - 1 (0) | 2023.06.23 |
[스프링 부트] 인텔리제이 프로젝트 세팅 및 실행 (0) | 2023.06.21 |