[Spring Boot] JPA

고운·2023년 5월 23일
0

Spring Boot

목록 보기
8/13
post-custom-banner

application.properties

spring.jpa.hibernate.ddl-auto=create      #테이블 자동생성 해줌. 한 번 테이블 생성된 후에는 주석처리하기
spring.jpa.properties.show_sql:true
spring.jpa.properties.format_sql:true
logging.level.org.hibernate.SQL:debug

Entity

(VO와 같은 것)

BookVO

@Entity
@Data
@Table(name="book")
public class BookVO {
	
	@Id
	private int bookid;
	private String bookname;
	private String publisher;
	private int price;
}

@Entity :이 테이블을 만들어줌.
@Table(name=”book") : 테이블 이름을 book으로 지정. 이걸 안 하면 클래스 이름으로 테이블 만들어줌
@Id: 테이블의 pk

CustomerVO

@Entity
@Data
@Table(name="customer")
public class CustomerVO {
	@Id
	private int custid;
	private String name;
	private String address;
	private String phone;
}

OrdersVO

(Book, Customer 테이블 join)

@Entity
@Data
@Table(name="orders")
public class OrdersVO {
	@Id
	private int orderid;
	
	@ManyToOne
	@JoinColumn(name="custid",insertable=true,updatable=true)
	private CustomerVO customerVO;
	
	@ManyToOne
	@JoinColumn(name="bookid",insertable=true,updatable=true)
	private BookVO bookVO;
	
	private int saleprice;
	private String orderdate;
}

View Entity

(book, customer 테이블과 join해서 custid, bookid 대신 name(고객이름)과 bookname이 바로 보이게 만든 뷰)

@Entity
@Data
@Table(name="view_listorders")
public class View_ListOrders {
	@Id
	private int orderid;
	private String name;
	private String bookname;
	private String orderdate;
	private int saleprice,price;
}

or

orderid 대신 name과 bookname을 합친 OrdersViewID를 Primary key로 사용할 수도 있다.

그러기 위해 OrdersViewID라는 class를 만들어 준다.

@Embeddable
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrdersViewID implements Serializable {
	@Column(name="name")
	private String name;
	@Column(name="bookname")
	private String bookname;
}
❓ Serializable: JVM에 이 클래스는 직렬화(Serialization)하라고 알려주는 인터페이스. 자바 직렬화: 자바 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 자바 시스템에서도 사용할 수 있도록 바이트(byte) 형태로 데이터 변환하는 기술과 바이트로 변환된 데이터를 다시 객체로 변환하는 기술(역직렬화)을 아울러서 말한다. 간단히 말하면 해당 클래스를 HashMap, ArrayList처럼 만들어 준다.

@Column(name="name") = DB에서의 컬럼 이름이 name이다.

@Embeddable : 여러 개의 칼럼을 합쳐서 하나의 칼럼으로 만들려고 할 때, 그 하나의 칼럼(여기서는 OrdersViewID)을 클래스로 만들어서 이 어노테이션을 붙인다.

Entity에서 이 칼럼을 호출해 멤버변수로 사용할 때에는 @EmbeddedId 라는 어노테이션을 붙인다.

@Entity
@Data
@Table(name="view_listorders2")
public class View_ListOrders2 {
	@EmbeddedId
	private OrdersViewID id;
	private String orderdate;
	private int saleprice,price;
}

@Transient: DB에 테이블 자동 생성할 때 들어가지 말아야 할 칼럼을 지정하는 어노테이션

DAO

public interface {DAO이름} extends JpaRepository<{엔티티 클래스}, {PK 타입}>

@Repository
public interface BookDAO extends JpaRepository<BookVO, Integer>{

}

기본 CRUD는 메소드를 따로 생성하지 않아도 기본으로 제공함.

bookname이 뫄뫄인 거 찾기 (메소드 내용은 안 써도 됨)

public List<BookVO> findByBookname(String Bookname);

bookname에 뫄뫄가 포함된 것 찾기

public List<BookVO> findByBooknameContaining(String bookname);

bookid가 뫄뫄보다 큰 거 찾기

public List<BookVO> findByBookidGreaterThan(int bookid);

쿼리문 사용

@Query("select nvl(max(orderid),0) +1 from OrdersVO")
public int getNextNo();
@Modifying
@Query(value="insert into orders o(o.orderid,o.custid,o.bookid,o.saleprice,orderdate) values(:#{#o.orderid},:#{#o.customerVO.custid},:#{#o.bookVO.bookid},:#{#o.saleprice},sysdate)",nativeQuery=true)
@Transactional
public void insert(@Param("o") OrdersVO o);

@Param : 파라미터로 넘어온 값을 변수로 지정

nativeQuery=true : 기존의 데이터베이스에서 사용하던 쿼리를 그대로 사용

@Transactional: 데이터베이스의 상태를 변경하는 작업을 할 때 사용. begin, commit을 자동으로 해주고 예외 발생시 rollback 해준다.

파라미터로 넘어온 값을 쿼리문에서 사용할 때 아래처럼 매개변수 순서대로 ?1 ?2 … 이렇게 쓰거나 위 예시처럼 @Param 사용해서 이름을 지정해줌

@Query(value="select c.* from (select rownum r, a.* from (select * from Board b order by b.b_ref desc, b.b_step asc) a) c where c.r between ?1 and ?2", nativeQuery=true)
public List<Board> selectAll(int start,int end);

param을 사용할 때 (1) 객체 자체를 넘길 수도 있고 (2) 필드 값들을 넘겨줄 수도 있음

(1)

쿼리문 안에서 쓸 이름을 지정하기 : @Param(”지정할 객체이름”) 타입 객체명

쿼리문 안에서 쓸 때: :#{#객체이름.칼럼이름}

@Modi..
@Query("update User set name = :#{#paramUser.name}, age = :#{#paramUser.age}, ssn = :#{#paramUser.ssn} where id = :#{#paramUser.id}")
int updateSpecificAttribute(@Param("paramUser") User user );

(2)

쿼리문 안에서 쓸 이름을 지정하기 : @Param(”지정할 객체이름”) 타입 객체명

쿼리문 안에서 쓸 때: :변수명

@Modi..
@Query("update User set name = :name, age = :age, ssn = :ssn where id = :id")
int updateSpecificAttribute(@Param("name") String name, @Param("age") Integer age, @Param("ssn") String ssn, @Param("id") Long id );

Service

findAll

public List<BookVO> findAll(){
	return dao.findAll();
}

insert, update

public void save(BookVO b) {
	dao.save(b);
}

save: 주어진 vo의 pk값에 해당하는 레코드가 없으면 새로 추가, 있으면 있는 걸 수정해줌

findById

public Optional<BookVO> findById(int bookid) {
	return dao.findById(bookid);
}

findById()는 return type이 Optional<VO>임. 해당 id로 vo를 찾을 수 없어도 예외가 발생하지 않음.

Optional 타입의 객체는 get()을 하면 그 안의 vo(or anything in that optional object)를 리턴해줌. (Controller에서 처리)

delete

public void delete(int bookid) {
	dao.deleteById(bookid);
}

Controller

select

@RequestMapping("/book/list")
public void list(Model model, String keyword, String col, HttpServletRequest request) {
	//Get 방식일 경우
	if(request.getMethod().equals("GET")) {
		model.addAttribute("list",bs.findAll());
	//Post 방식일 경우 (검색어 제출했을 경우)
	}else {
		model.addAttribute("list",bs.search(keyword,col));
	}
}

update

@GetMapping("/customer/update/{custid}")
public ModelAndView update(@PathVariable int custid) {
	ModelAndView mav=new ModelAndView("/customer/update");  //뷰페이지로 다시 돌아가게 해줌. 이거 안 하면 없는 페이지로 돌아감. (예를 들어 전달해준 custid가 5면 /customer/update/5로 가는데 여기에 해당하는 뷰페이지가 없으므로 404에러 뜸)
	mav.addObject("c",cs.findById(custid).get());
	return mav;
}

Optional 객체 처리

Optional<Member> option=dao.findById(id);
if(option.isPresent()) {
	//레코드가 존재할 때 수행할 코드
}else {
	//레코드가 존재하지 않을 때 수행할 코드
}

View

view(DB)를 사용하지 않았을 경우 상태유지한 변수 불러오기

<tr th:each="o:${list}">
	<td th:text="${o.orderid}"></td>
	<td th:text="${o.customerVO.name}"></td>
	<td th:text="${o.bookVO.bookname}"></td>
	<td th:text="${o.orderdate}"></td>
	<td th:text="${o.saleprice}"></td>
	<td th:text="${o.bookVO.price}"></td>
</tr>

view 테이블을 만들어서 ordersViewID를 pk로 사용했을 경우 상태유지한 변수 불러오기

<tr th:each="o:${list}">
	<td th:text="${o.id.name}"></td>
	<td th:text="${o.id.bookname}"></td>
	<td th:text="${o.orderdate}"></td>
	<td th:text="${o.saleprice}"></td>
	<td th:text="${o.price}"></td>
</tr>

view 테이블을 만들고 ordersViewID 사용 x일 경우 (ordersid가 pk)

<tr th:each="v:${list}">
	<td th:text="${v.orderid}"></td>
	<td th:text="${v.name}"></td>
	<td th:text="${v.bookname}"></td>
	<td th:text="${v.orderdate}"></td>
	<td th:text="${v.saleprice}"></td>
	<td th:text="${v.price}"></td>
</tr>

테이블 조인

member의 id를 board의 writer와 조인

entity

member의 필드로 board의 리스트를 넣기

import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import lombok.Data;
@Entity
@Data
@Table(name="member")
public class Member {
	@Id
	private String id;
	private String pwd,name,role;
	
	@OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
	private List<Board> board;
}

FetchType.EAGER: 부모 요청할 때 자식도 읽어들이기
FetchType.LAZY: 나중에 자식 요청할 때 읽어들이기

board의 필드로 Member를 넣기

@Entity
@Data
@Table(name="board")
public class Board {
	@Id
	private int no;
	private String title, pwd, content;
	private Date regdate;
	private int hit;
	private String ip;
	
	private int b_ref,b_step,b_level;
	
	@Transient	//DB에는 이 칼럼 만들지 말라는 뜻
	private MultipartFile uploadFile;
	private String fname;
	
	@ManyToOne
	@JoinColumn(name="id", insertable = true, updatable = true)
	private Member member;
}

@OneToMany안에 cascade=CascadeType.REMOVE => 부모 레코드를 지우면 자식레코드도 같이 지워짐.
이걸 안 하면 자식 있으면 삭제가 안 됨

profile
백엔드 개발자
post-custom-banner

0개의 댓글