20240314 Spring 22 - JPA

Leafy·2024년 3월 14일
1

중앙_자바

목록 보기
59/76

JPA와 mybatis는 같이 쓰지 않음

새 프로젝트
Spring Boot Dev tools?
Spring Web
Thymeleaf
Mariadb
application.properties 설정

[Spring JPA ] 데이터베이스 방언(Dialect) 이란?

엔티티
자바에서 선언한 변수?를 그대로 db에 생성해주는 거
JPA에서 엔티티를 작성하는 방법

JPA

서비스 - service (BoardService)
DAO - Repository (BoardRepository)
DTO - Entity (JPABoard)

Controller - @Controller
Service - @Service
Repository - JpaRepository<엔티티(타입), 이름> 상속
Entity - @Entity 어노테이션, getter, setter

서로 엮어준다.
외부 용도 말고 내부 용도로만 쓰게(상속된 Repository, Entity는 안해도됨~)
@Autowired private

  • Controller : 지금까지랑 다를거없다
  • Service : 서비스도..
@Service
public class BoardService {
	@Autowired
	private BoardRepository boardRepository;
}
  • Repository
public interface BoardRepository extends JpaRepository<JPABoard, Integer> {

}
  • Entity
@Getter
@Setter
@Entity
public class JPABoard {
	//조심할 것: _못쓴다. board_no <- JPA가 못 봄 (조회 명령에서 볼 수 있음.) boardno 혹은 bno
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int jbno;
	
	@Column(columnDefinition = "TEXT")
	private String jbtitle;
	
	@Column(columnDefinition = "LONGTEXT")
	private String jbcontent;
	
	@ColumnDefault("CURRENT_TIMESTAMP")
	private LocalDateTime jbdate = LocalDateTime.now();
	
	@ColumnDefault("0")
	private int jblike;
	
	@ColumnDefault("1")
	private int jbread;
}

JPA 쓰는 목적. 영속성 (pdf참고)

entity - 컬럼 생성

진정한 PK

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int jbno;

컬럼들 속성 써주기

@Column(columnDefinition = "TEXT")
private String jbtitle;

@Column(columnDefinition = "LONGTEXT")
private String jbcontent;

컬럼 초기값도 설정

@ColumnDefault("CURRENT_TIMESTAMP")
private LocalDateTime jbdate = LocalDateTime.now();

@ColumnDefault("0")
private String jblike;

@ColumnDefault("1")
private String jbread;

application.properties에 쿼리문 보이게 설정함.

spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.show_sql=true

이클립스
Preferences - General에 show heap memory하면 얼마나 메모리 잡아먹는지 볼수있다

여기 상속받은 JpaRepository에 내용이 다 들어 있어서 BoardRepository에 아무것도 안 적어도 통신은 된다고 한다.

보드 연결

controller

@GetMapping("/board")
public String board(Model model) {
	List<JPABoard> list = boardService.boardList();
	model.addAttribute("list", list);
	return "board";
}

service

@Service
public class BoardService {

	@Autowired
	private BoardRepository boardRepository;

	public List<JPABoard> boardList() {
		return boardRepository.findAll();
	}
	
}

바로 findAll() 메서드가 뜬다.
상속받은 JpaRepository에 있기 때문.

html 뷰에 출력 [[${list}]]
해보면 이런 쿼리가 실행됐다고 뜬다

findAll()

대용량데이터 긁어오기

하나만 긁어오는건?

findById()

[JAVA] - Optional이란? 간단하고 쉽게 이해하기 (Optional 객체 생성 예제)
출처: https://ccomccomhan.tistory.com/127 [[꼼꼼한 개발자] 꼼코더:티스토리]

null값에 대처하기 위해 Optional을 사용. (NullPointException 안 일으키게)
findById()의 리턴타입은 Optional 래퍼클래스이다.
PK값 = id이므로 전달하는 파라미터는 PK


일단 Optional이 낯설기 때문에(사용은 권장)
임시방편으로 findByJbno(no) 메서드를 Repository에 생성

테스트 코드로 간다

@SpringBootTest
class Jpa1ApplicationTests {
	
	@Autowired
	BoardService boardService;

	@DisplayName("톺아보기에 데이터 오는지 확인하기")
	@Test
	void contextLoads() {
		JPABoard detail = boardService.detail(2);
		assertEquals("제목", detail.getJbtitle());
	}

}

테스트 어떻게 하더라..ㅠ
(run as - JUnite test)

findBy()에다 컬럼명 쓰면 컬럼 검색해준다?

글쓰기 기능

write.html에 form

<form action="/write" method="post">
	<div>
		<input name="title" id="title" placeholder="제목">
	</div>
	<div>
		<textarea name="content" id="content" placeholder="본문"></textarea>
	</div>
		<button type="submit">작성</button>
	</form>

Controller

@PostMapping("/write")
public String write(@RequestParam("title") String title, @RequestParam("content") String content) {
	JPABoard entity = new JPABoard();
	entity.setJbtitle(title);
	entity.setJbcontent(content);
	boardService.write(entity);
	return "redirect:/board";
}

html에서 name을 컬럼명과 동일하게 해뒀다면 이것도 될것

@PostMapping("/write")
public String write(JPABoard post) {
	boardService.write(post);
	return "redirect:/board";
}

Service
save()가 저장하기.

public void write(JPABoard entity) {
	boardRepository.save(entity); // 저장하기
}

차이점

그동안 했던 거랑 다른 점이 있다.

생성한 글쓰기로 글을 써보면
기본값이 1인 조회수가 0으로 들어가있다.
나노초...?

차이의 원인

JPA는 entity 객체를 new로 새로 만들어서 그걸 통째로 저장하는 방식.
필드 값을 안 적어주면 자료형 기본값인 0이나 null이 들어가게 된다.

글삭제

deleteById()

no만 전달해서 삭제

  • detail.html
<button th:onclick="|location.href='@{/postDel(no=${detail.jbno})}'|">삭제</button>
  • controller
    2가지 방법: no 세팅한 entity전달, no만 전달
// 삭제 2가지. entity에 값을 넣고 그걸 삭제 or no값만 주고
@GetMapping("/postDel")
public String postDel(@RequestParam("no") String no) {
	int reNo;
	try {
		reNo = Integer.parseInt(no);
	} catch (Exception e) {
		reNo = 1;
	}
	//deleteById -> no값만 주는듯
	//delete
	boardService.postDel(reNo);
	return "redirect:/board";
}
  • service
public void postDel(int no) {
	boardRepository.deleteById(no); // no만 전달해서 삭제하기
}

delete()

  • controller
@GetMapping("/postDel")
public String postDel(@RequestParam("no") String no) {
	int reNo;
	try {
		reNo = Integer.parseInt(no);
	} catch (Exception e) {
		reNo = 1;
	}
	JPABoard post = new JPABoard();
	post.setJbno(reNo);
	
	boardService.postDel2(post);
	
	return "redirect:/board";
}
  • service
public void postDel2(JPABoard post) {
	boardRepository.delete(post); // no가 들어간 entity 전달해서 삭제
}

이렇게 해보기

// 삭제 2가지. entity에 값을 넣고 그걸 삭제 or no값만 주고
@GetMapping("/postDel")
public String postDel(@RequestParam("no") int no) {
	JPABoard post = new JPABoard();
	post.setJbno(no);
	
	//JPABoard result = boardService.postDel2(post);
	post = boardService.postDel2(post);
	
	return "redirect:/board";
}

👆
삭제하기 전에 그 내용 가져오는 거 만들ㅇㅓ보려고 했는데 안함.


글 수정

repository에 update 메서드가 없어서 만들려다가
그냥 save() 사용
-> 댓글도 없고 글쓴이도 없는 상황이니까 이렇게 한다

save()는 변화된 값을 넣어주면 바꿔준다,,?



findAll(Pageable) <- 페이지 연산을 넣어서 find


정렬

findAllByOrderByJbnoDesc()를 만들어준다.

public List<JPABoard> boardList() {
	//return boardRepository.findAll();
	return boardRepository.findAllByOrderByJbnoDesc();
}

findAllByOrderByJbnoDesc()는 JPA가 메서드 이름을 보고 유추해서 만들어주는 것.
잘못 적으면 정렬이 이렇게 안 된다.

JPA - sql 직접 적기 (JSQL?)

이렇게도 할 수 있다~

public interface BoardRepository extends JpaRepository<JPABoard, Integer> {
	@Query(value = "SELECT * FROM jpaboard j WHERE j.jbno=?1", nativeQuery = true)
	JPABoard findByJbno(int no);

	List<JPABoard> findAllByOrderByJbnoDesc();
}

pstmt 썼을 때처럼 ?1 <- 첫번째 빈자리..?

NatvieQuery는 JPQL이 아닌 SQL를 직접 정의하여 사용하는 방식이다. 위에서 이야기 한것과 같이 function과 join를 하는 경우 JPQL를 사용할 수도 있지만 SQL를 직접 정의할수있는 NativeQuery를 사용할 수 있다.


엔티티
컬럼 이렇게도 쓸 수 있다.

@Column(name="jbtitleJAVA", columnDefinition = "VARCHAR(50)")
private String jbtitle;

name은 자바에서 쓰는 거.
length = 50는 길이 얘기다

@Column(name="jbtitleJAVA", length = 50)
private String jbtitle;

이것도 있다. 길이만

@BatchSize(size = 50)
private String jbtitle;

DB 양 너무 많아져서 int 말고 long으로 잡는 경우도 있다

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int jbno;

JPA?는 레거시에서도 사용가능

JPA 속성들...

@Getter
@Setter
@Entity
public class JPAMember {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int jmno;
	
	@Column(unique = true, columnDefinition = "VARCHAR(10)", nullable = false)
	private String jmid;
	
	@Column(columnDefinition = "VARCHAR(30)", nullable = false)
	private String jmpw;
	
	@Column(name="jmname", columnDefinition = "VARCHAR(20)", nullable = false)
	private String name;
	// 컬럼 name이 컬럼용 // String name이 JPA용
	
	@Column(columnDefinition = "VARCHAR(15)")
	private String jmtel;
	
}

jpa.. 이미 만든 컬럼 속성을 바꿀 수 없다..?!

[JPA] hibernate의 ddl-auto 속성의 종류와 주의해야할 점

jpa의 update 설정이 변경하는 것

#JPA
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDB106Dialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.show_sql=true

[JPA] @ManyToOne, @OneToMany 이해하기

엔티티 연결

@Getter
@Setter
@Entity
public class JPABoard {
	//조심할 것: _못쓴다. board_no <- JPA가 못 봄 (조회 명령에서 볼 수 있음.) boardno 혹은 bno
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int jbno;
	
	//@BatchSize(size = 50)
	//@Column(name="jbtitleJAVA", length = 50)
	@Column(name="jbtitle", columnDefinition = "VARCHAR(50)")
	private String jbtitle;
	
	@Column(columnDefinition = "LONGTEXT")
	private String jbcontent;
	
	@ColumnDefault("CURRENT_TIMESTAMP")
	private LocalDateTime jbdate = LocalDateTime.now();
	
	@ColumnDefault("0")
	private int jblike;
	
	@ColumnDefault("1")
	private int jbread;
	
	@ManyToOne
	@JoinColumn
	private JPAMember jpaMember;
}

재기동, db확인

외래키로 member가 생겨있다
JPA @OneToMany, @ManyToOne으로 연관관계 관리하기

@ManyToOne
@JoinColumn
private JPAMember jpaMember;

경찰과 총 관계...
소유 관계였던 것 같다

글쓰기 + member

멤버 레코드를 몇 개 넣어준다.
있는 멤버로

controller

@PostMapping("/write")
public String write(@RequestParam("title") String title, @RequestParam("content") String content) {
	JPABoard entity = new JPABoard();
	entity.setJbtitle(title);
	entity.setJbcontent(content);
	
	JPAMember jpaMember = new JPAMember();
	//우리가 알고 있는 것은 세션에 올라간 mid뿐...
	jpaMember = boardService.findByJmid("apple");
	entity.setJpaMember(jpaMember);
	
	boardService.write(entity);
	return "redirect:/board";
}

board.html에서

<td th:text="${row.jpaMember.name}"></td>

JPAMember

@Getter
@Setter
@Entity
public class JPAMember {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int jmno;
	
	@Column(unique = true, columnDefinition = "VARCHAR(10)", nullable = false)
	private String jmid;
	
	@Column(columnDefinition = "VARCHAR(30)", nullable = false)
	private String jmpw;
	
	@Column(name="jmname", columnDefinition = "VARCHAR(20)", nullable = false)
	private String name;
	// 컬럼 name이 컬럼용 // String name이 JPA용
	
	@Column(columnDefinition = "VARCHAR(15)")
	private String jmtel;
	
}

컬럼 name이 "jmname"이고 필드명이 name이면 th에도 name이라고 써야한다.

public class JPABoard {
	//조심할 것: _못쓴다. board_no <- JPA가 못 봄 (조회 명령에서 볼 수 있음.) boardno 혹은 bno
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int jbno;
	
	//@BatchSize(size = 50)
	//@Column(name="jbtitleJAVA", length = 50)
	@Column(name="jbtitle", columnDefinition = "VARCHAR(50)")
	private String jbtitle;
	
	@Column(columnDefinition = "LONGTEXT")
	private String jbcontent;
	
	@ColumnDefault("CURRENT_TIMESTAMP")
	private LocalDateTime jbdate = LocalDateTime.now();
	
	@ColumnDefault("0")
	private int jblike;
	
	@ColumnDefault("1")
	private int jbread;
	
	@ManyToOne
	@JoinColumn
	private JPAMember jpaMember;
}

jpaMember.name으로 쓰게 된 이유는 위의 joincolumn에 적은게 이거라


개인프젝 : 타임리프 + 부트 + JPA 추천


https://vladmihalcea.com/map-jpa-entity-to-view-or-sql-query-with-hibernate/

1개의 댓글

comment-user-thumbnail
2024년 3월 14일

그렇구나

답글 달기