JPA와 mybatis는 같이 쓰지 않음
새 프로젝트
Spring Boot Dev tools?
Spring Web
Thymeleaf
Mariadb
application.properties 설정
[Spring JPA ] 데이터베이스 방언(Dialect) 이란?
엔티티
자바에서 선언한 변수?를 그대로 db에 생성해주는 거
JPA에서 엔티티를 작성하는 방법
서비스 - service (BoardService)
DAO - Repository (BoardRepository)
DTO - Entity (JPABoard)
Controller - @Controller
Service - @Service
Repository - JpaRepository<엔티티(타입), 이름> 상속
Entity - @Entity
어노테이션, getter, setter
서로 엮어준다.
외부 용도 말고 내부 용도로만 쓰게(상속된 Repository, Entity는 안해도됨~)
@Autowired
private
@Service
public class BoardService {
@Autowired
private BoardRepository boardRepository;
}
public interface BoardRepository extends JpaRepository<JPABoard, Integer> {
}
@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참고)
진정한 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}]]
해보면 이런 쿼리가 실행됐다고 뜬다
대용량데이터 긁어오기
하나만 긁어오는건?
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이 들어가게 된다.
no만 전달해서 삭제
<button th:onclick="|location.href='@{/postDel(no=${detail.jbno})}'|">삭제</button>
// 삭제 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";
}
public void postDel(int no) {
boardRepository.deleteById(no); // no만 전달해서 삭제하기
}
@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";
}
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가 메서드 이름을 보고 유추해서 만들어주는 것.
잘못 적으면 정렬이 이렇게 안 된다.
이렇게도 할 수 있다~
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?는 레거시에서도 사용가능
@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
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;
경찰과 총 관계...
소유 관계였던 것 같다
멤버 레코드를 몇 개 넣어준다.
있는 멤버로
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/
그렇구나