Spring boot JPA 활용하기

limchard·2023년 11월 1일
0

spring boot

목록 보기
4/6

스프링 JPA(Java Persistence API)

: 스프링에서데이타를처리할수있도록돕는라이브러리이다
데이타베이스에종속적인 SQL문없이도개발이가능하기때문에개발의생산성을높일수있다
기존의 JDBC 등을이용해서직접구현했던 데이타베이스관련작업을대신처리해주는추상화된계층의구현스펙이다

엔티티?

JPA에서는 엔티티는 테이블에 대응하는 하나의 클래스라고 생각하면 된다
spring-boot-starter-data-jpa 의존성을 추가하고 @Entiy 어노테이션을 붙이면 테이블과 자바 클래스가 매핑이 됩니다.
그래서 JPA에서 '하나의 엔티티 타입을 생성한다'라는 의미는 '하나의 클래스를' 작성한다는 의미가 됩니다.
엔티티라는 용어는 때로는 클래스를 의미하는 경우도 있고, 클래스에 의해 생성된 인스턴스를 의미하는 경우가 있습니다.
정확히 얘기 하자면, 엔티티클래스 와 엔티티인스턴스 혹은 엔티티객체라는 표현이 정확합니다

Hibernate

자바 언어를 위한 ORM 프레임워크. JPA의 구현체로, JPA 인터페이스를 구현하며, 내부적으로 JDBC API를 사용한다.


초기세팅

Spring project 생성

  • MySQL Driver, Spring Data JPA 두가지 추가하여 진행

pom.xml 설정

  • JAP를 사용하기 위해 아래 코드를 pom.xml 에 추가하기
		<!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime -->
		<dependency>
		    <groupId>org.glassfish.jaxb</groupId>
		    <artifactId>jaxb-runtime</artifactId>
		    <version>2.3.2</version>
		</dependency>
	
		<!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
		<dependency>
		    <groupId>javax.xml.bind</groupId>
		    <artifactId>jaxb-api</artifactId>
		    <version>2.3.1</version>
		</dependency>



application.properties 추가하기

mysql setting

  • DB 연결을 위해 mysql setting도 해줘야 한다.
# mysql setting
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/클래스명?serverTimezone=Asia/Seoul
spring.datasource.username=root
spring.datasource.password=비밀번호	

JPA setting

# JPA setting
# 스키마 생성 create:기존 테이블을 삭제 후 생성해준다. (이름이 겹치는 경우 삭제 후 만들어버린다.),
# update:변경된 부분만 반영된다. 아래 한줄이 이 개념을 설명해주는것이다.
spring.jpa.hibernate.ddl-auto=update

# ddl 생성시 데이터베이스의 고유기능 사용 유,무 설정
spring.jpa.generate-ddl=true

# api호출 시 내부적으로 실행되는 sql문을 콘솔에 보여주기 설정 유,무
spring.jpa.show-sql=true

# 사용할 database 종류 선택(mysql,oracle,mariadb 등등..)
spring.jpa.database=mysql

# mysql 상세지정하기 (mysql 고유 특성 지정) // 내부적인 기본 platform 지정.
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect

* JPA 주의사항!

  • 일반 mysql의 경우 테이블 명이 동일할 경우 생성이 안된다. 하지만 JSP에서는 기존 테이블을 삭제 후 생성하기에 이름이 겹쳐도 생성된다. 이 말은 겹치는 colmn은 유지하고, 추가된 colmn은 추가, 변경된 colmn값은 바꿔버린다.


실행하기

MyCarDto.java

  • @Entity : 테이블과 매핑
  • @Table(name="테이블명") : 자동으로 mysql에 테이블명이라는 table이 만들어진다.
  • @Id : 각 Entity를 구별할 수 있는 식별자. 즉 시퀀스라고 봐도 된다.
  • @GeneratedValue(strategy = GenerationType.AUTO) 기본키(PK)값에 대한 생성 전략을 제공한다. @Id와 함께 엔티티 또는 매핑된 슈퍼클래스의 기본 키 속성을 적용할 수 있다. @Id와 보통 같이 쓰인다.
    • GenerationType.AUTO : 기본 설정 값으로 각 데이터베이스에 따라 기본키를 자동으로 생성한다.(Auto_increment 느낌), 여기서 AUTO는 IDENTITY,SEQUENCE,TABLE 중 하나를 선택해준다.

여기서 seq역할을 하는 변수는 long type으로 사용해야 한다!!

  • @Column(name = "carname") : 멤버를 지정해준다. 말 그대로 column을 지정해준다. Dto이름과 Column 이름이 다를 경우 지정해줄 수 있다.
  • @CreationTimestamp : 엔티티가 생성되는 시점의 시간을 자동지정. 날짜는 now()로 해서 바로 기입되잖아 ^^
  • @Column(updatable = false) : 업데이트시에 이 컬럼은 수정하지 않겠다. 이게 없으면 값이 null로 들어간다(자동 업데이트). updateform에 해당 guipday를 기입하지 않기 때문이다.
package mycar.data;

import java.sql.Timestamp;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.CreationTimestamp;

import lombok.Data;

@Entity // 기본 설정
@Table(name="mycar") // 자동으로 mysql에 mycar 라는 table이 만들어진다.
@Data
public class MyCarDto {

	@Id // 각 엔티티를 구별할 수 있는 식별 id 이다. 즉 seq라고 봐도 된다. 기본키, primary key를 지정하는 것. // Id만 써도 된다 라고 하는데.. 보통 쌤은 2개 쓴다.
	@GeneratedValue(strategy = GenerationType.AUTO) // 이것도 엔티티를 구별할 수 있는 식별자. 
	private long num;
	
	// 멤버등록하기
	@Column(name = "carname") // 이름을 지정할 시에 이름을 지정해줘야 한다.	
	private String carname;
	
	@Column // 이름을 똑같이 쓸 경우 안써도 된다.
	private int carprice;
	
	@Column
	private String carcolor;
	
	@Column
	private String carguip;
	
	@CreationTimestamp // 엔티티가 생성되는 시점의 시간을 자동지정. 날짜는 now()로 해서 바로 기입되잖아 ^^
	@Column(updatable = false) // 업데이트시에 이 컬럼은 수정하지 않겠다. 이게 없으면 값이 null로 들어간다(자동 업데이트). updateform에 해당 guipday를 기입하지 않기 때문이다.
	private Timestamp guipday;
    
    @Column
	private String carphoto;
}

MyCarDaoInter.java

  • JpaRepository<Dto이름,기본키의 타입> 를 이용해서 Dto를 상속받아온다.
  • extends JpaRepository<MyCarDto, Long> 는 직접 써줘야 한다.
package mycar.data;

import org.springframework.data.jpa.repository.JpaRepository;

public interface MyCarDaoInter extends JpaRepository<MyCarDto, Long>{
	// 레포지터리 라는거 JpaRepository 는 추상 클래스이다...
	// 추상 클래스는 모든 추상메서드들을 무조건 오버라이딩 해야한다.
	// 그렇기 때문에 extends 하면 내가 사용하지 않은것들 포함하여 갯수가 너무 많다.
    // 그래서 dao에서 extends 해서 가져오는게 아니라 호출(@Autowired)해서 사용한다.
}

MyCarDao.java

  • public class MyCarDao implements MyCarDaoInter 로 상속받아 자동생성할 경우 필요없는 method를 포함하여 무수히 많은 method를 가져오기 때문에 implements MyCarDaoInter를 지우고, MyCarDaoInter를 @Autowired로 받아와서 사용한다.
  • save : id타입 유,무에 따라 insert인지 update인지 갈린다. 즉, insert,update 모두 save를 사용한다.
package mycar.data;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.ModelAttribute;

@Repository // dao를 bean에 등록
//public class MyCarDao implements MyCarDaoInter { // 상속을 수십개를 받아야 하기 때문에 implements 없애준다.
public class MyCarDao {

	@Autowired
	MyCarDaoInter carInter;
	
	// insert
	public void insertMyCar(MyCarDto dto) {
		
		carInter.save(dto); // insert를 save라고 한다. update도 save라고 한다.
		// id type의 유,무에 따라 insert와 update가 자동으로 갈린다... 
		// insert는 auto_increment로 id가 추가되고(id값을 모르는 상태), update는 원래 있는 id를 기준으로(id를 알고있는 상태) 변경하기에 구분 해준다.
		
	}
	
	// select
	public List<MyCarDto> getAllData(){
		
		return carInter.findAll();
	}
	
	// update하기
	// num에 대한 값(dto) 반환
	public MyCarDto getData(long num) {
		
		return carInter.getReferenceById(num);
	}
	
	// update
	public void updateCar(MyCarDto dto) { // insert와 동일하므로, insert로 사용해도 무관함
		
		carInter.save(dto);	
	}
	
	// delete
	public void deleteCar(long num) {
		carInter.deleteById(num);
	}	
}

MyCarController.java

  • controller 자체는 크게 차이 없음.
package mycar.data;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/car") // 중복 경로 전체 자동 기입 
public class MyCarController {

	@Autowired
	MyCarDao dao;
	
	@GetMapping("/carlist") //..시작 // 중간경로는 해도 되고, 안해도 되고
	public ModelAndView list() {
		
		ModelAndView model=new ModelAndView();
		
		List<MyCarDto> list=dao.getAllData();
		
		model.addObject("list", list);
		model.addObject("totalCount", list.size());
		
		model.setViewName("carlist");
			
		return model;
	}
	
	@GetMapping("/carform")
	public String form() {
		
		return "addform";
	}
	
	// insert
	@PostMapping("/insert") // multipart 관련사항 수정해주기. MultiPart, HttpSession,등등 받아오면 끝!
	public String insert(@ModelAttribute MyCarDto dto,
			MultipartFile carupload,
			HttpSession session) {
		
		// 업로드할 save 위치 구하기
		String path=session.getServletContext().getRealPath("/save");
		
		// 업로드한 파일 dto얻기 
		dto.setCarphoto(carupload.getOriginalFilename());
		
		// 실제 업로드
		try {
			carupload.transferTo(new File(path+"/"+carupload.getOriginalFilename()));
		} catch (IllegalStateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		// insert
		dao.insertMyCar(dto);
		
		return "redirect:carlist";
	}
	
	// updateform 연결하기
	@GetMapping("/updateform")
	public String uform(Model model,@RequestParam long num) {
		
		MyCarDto dto=dao.getData(num);
		
		model.addAttribute("dto", dto);
		
		return "uform";	
	}
	
    // 업데이트	
	@PostMapping("/updatecar")
	public String updatecar(@ModelAttribute("dto") MyCarDto dto) {
		
		dao.updateCar(dto); // insert와 동일하므로, insert로 사용해도 무관함
		
		return "redirect:carlist";
	}
	
    // 삭제	
	@GetMapping("/deletecar")
	public String delete(@RequestParam long num) {
		dao.deleteCar(num);
		
		return "redirect:carlist";
	}	
    
    // detail page
    @GetMapping("/detail")
	public String detail(@RequestParam long num,Model model) {
		
		MyCarDto dto=dao.getData(num);
		
		model.addAttribute("dto", dto);
		
		return "detail";
	}
}

SpringBootJpaApplication.java

  • @ComponentScan({"mycar.data"}) : 패키징 등록
  • @EntityScan("mycar.data") : dto 인식
  • @EnableJpaRepositories("mycar.data") : dao인식
package boot.mvc.tea;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@ComponentScan({"mycar.data"})// 패키징 등록
@EntityScan("mycar.data") // table이 어디에 생성되는지 지정 // dto 인식
@EnableJpaRepositories("mycar.data") // dao인식 (method인식) // Repositoriy를 bean에 자동 등록해줘!!
public class SpringBootJpaApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootJpaApplication.class, args);
	}
}

jsp 파일

carlist.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link href="https://fonts.googleapis.com/css2?family=Bagel+Fat+One&family=Dongle:wght@300&family=East+Sea+Dokdo&family=Gamja+Flower&family=Gowun+Dodum&family=Nanum+Gothic+Coding&family=Nanum+Pen+Script&family=Orbit&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
<script src="https://code.jquery.com/jquery-3.7.0.js"></script>
<title>Insert title here</title>

<style type="text/css">
.box{
	border: 1px solid gray;
	width: 30px;
	height: 30px;
	border-radius: 30px;
}

</style>

</head>
<body>
	<div style="margin: 50px 100px;">
	<img alt="" src="../20.jpg" style="width: 150px;">
		<button type="button" class="btn btn-info" onclick="location.href='carform'">자동차정보입력</button>
		
		<br><br>
		<h3 class="alert alert-info">
			<b>총 ${totalCount }개의 자동차 정보가 있습니다.</b>
		</h3>
		<table class="table table-bordered">
			<tr>
				<th style="width: 60px;">번호</th>
				<th style="width: 120px;">자동차명</th>
				<th style="width: 100px;">색상</th>
				<th style="width: 160px;">가격</th>
				<th style="width: 220px;">구입일</th>
				<th style="width: 220px;">등록일</th>
				<th style="width: 200px;">편집</th>
			</tr>
			
 			
			<c:forEach var="dto" items="${list }" varStatus="i">
				<tr>
					<td>${i.count }</td>
					<td>
						<a href="detail?num=${dto.num }">
							<img alt="" src="../save/${dto.carphoto }" width="40" height="40" border="1" hspace="10">
							${dto.carname }
						</a>
					</td>
					<td><div style="background-color: ${dto.carcolor}" class="box"></div></td>
					<td><fmt:formatNumber value="${dto.carprice }" type="currency"/></td>
					<td>${dto.carguip }</td>
					<td><fmt:formatDate value="${dto.guipday }" pattern="yyyy.MM.dd"/></td>
					<td>
						<button type="button" class="btn btn-outline-warning" onclick="location.href='updateform?num=${dto.num}'">수정</button>
						<button type="button" class="btn btn-outline-danger" onclick="location.href='deletecar?num=${dto.num}'">삭제</button>
					</td>
				</tr>
			</c:forEach>
			
		</table>
	</div>
</body>
</html>

addform.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link href="https://fonts.googleapis.com/css2?family=Bagel+Fat+One&family=Dongle:wght@300&family=East+Sea+Dokdo&family=Gamja+Flower&family=Gowun+Dodum&family=Nanum+Gothic+Coding&family=Nanum+Pen+Script&family=Orbit&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
<script src="https://code.jquery.com/jquery-3.7.0.js"></script>
<title>Insert title here</title>
</head>
<body>

<div style="margin: 100px 100px; width: 500px;">
	<form action="insert" method="post" enctype="multipart/form-data">
		<table class="table table-bordered">
			<tr>
				<th>자동차명</th>
				<td>
					<input type="text" name="carname" class="form-control" style="width: 120px;" required="required">
				</td>
			</tr>
			<tr>
				<th>가격</th>
				<td>
					<input type="text" name="carprice" class="form-control" style="width: 150px;" required="required">
				</td>
			</tr>
			<tr>
				<th>색상</th>
				<td>
					<input type="color" name="carcolor" class="form-control" style="width: 200px;" required="required" value="#ff0000">
				</td>
			</tr>
			<tr>
				<th>구입일</th>
				<td>
					<input type="date" name="carguip" class="form-control" style="width: 200px;" value="2023-10-01">
				</td>
			</tr>
			
			<!-- 파일 업로드의 경우 이름과 dto가 같다고 해서 바로 올라가는게 아니다.. -->
			<!-- 이름을 똑같이 할 경우 오류 찾기가 어려워,, 구분을 위해 dto name과 여기 input의 Name을 다르게 준다. -->
			<tr>
				<th>자동차 이미지</th>
				<td>
					<input type="file" name="carupload" class="form-control" style="width: 200px;" required="required">
				</td>
			</tr>

			<tr>
				<td colspan="2" align="center">
					<button type="submit" class="btn btn-outline-info">저장</button>
					<button type="button" class="btn btn-outline-success" onclick="location.href='carlist'">목록</button>
				</td>
			</tr>
		</table>
	</form>
</div>
	
</body>
</html>

ufrom.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link href="https://fonts.googleapis.com/css2?family=Bagel+Fat+One&family=Dongle:wght@300&family=East+Sea+Dokdo&family=Gamja+Flower&family=Gowun+Dodum&family=Nanum+Gothic+Coding&family=Nanum+Pen+Script&family=Orbit&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
<script src="https://code.jquery.com/jquery-3.7.0.js"></script>
<title>Insert title here</title>
</head>
<body>

<div style="margin: 100px 100px; width: 500px;">
	<form action="updatecar" method="post">
	<input type="hidden" name="num" value="${dto.num }">
		<table class="table table-bordered">
			<tr>
				<th>자동차명</th>
				<td>
					<input type="text" name="carname" class="form-control" style="width: 120px;" required="required" value="${dto.carname }">
				</td>
			</tr>
			<tr>
				<th>가격</th>
				<td>
					<input type="text" name="carprice" class="form-control" style="width: 150px;" required="required" value="${dto.carprice }">
				</td>
			</tr>
			<tr>
				<th>색상</th>
				<td>
					<input type="color" name="carcolor" class="form-control" style="width: 200px;" required="required" value="${dto.carcolor }">
				</td>
			</tr>
			<tr>
				<th>구입일</th>
				<td>
					<input type="date" name="carguip" class="form-control" style="width: 200px;" value="${dto.carguip }">
				</td>
			</tr>
			<tr>
				<td colspan="2" align="center">
					<button type="submit" class="btn btn-outline-info">저장</button>
					<button type="button" class="btn btn-outline-success" onclick="location.href='carlist'">목록</button>
				</td>
			</tr>
		</table>
	</form>
</div>

</body>
</html>

detail.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link href="https://fonts.googleapis.com/css2?family=Bagel+Fat+One&family=Dongle:wght@300&family=East+Sea+Dokdo&family=Gamja+Flower&family=Gowun+Dodum&family=Nanum+Gothic+Coding&family=Nanum+Pen+Script&family=Orbit&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
<script src="https://code.jquery.com/jquery-3.7.0.js"></script>
<title>Insert title here</title>
<style type="text/css">
.photo{
	width: 300px;
	height: 300px;
	border: 1px solid gray;
	border-radius: 20px;	
}
div.box{
	width: 50px;
	height: 50px;
	border-radius: 30px;
	border: 1px solid gray;
}

</style>

</head>
<body>
	<h3 class="alert alert-info">구입 자동차 정보</h3>
	<table class="table table-bordered" style="width: 500px;"> 
		<tr>
			<td width="320" rowspan="4">
				<img alt="" src="../save/${dto.carphoto }" class="photo">
			</td>
			<td>
				<div class="box" style="background-color: ${dto.carcolor};"></div>
				${dto.carcolor}
			</td>
		</tr>
		<tr>
			<td>자동차명: ${dto.carname }</td>
		</tr>
		<tr>
			<td>단가: <fmt:formatNumber value="${dto.carprice }" type="currency"></fmt:formatNumber></td>
		</tr>
		<tr>
			<td>구입일 :${dto.carguip }</td>
		</tr>
		<tr>
			<td colspan="2" align="center">
				<button type="button" class="btn btn-outline-warning" onclick="location.href='updateform?num=${dto.num}'">수정</button>
				<button type="button" class="btn btn-outline-danger" onclick="location.href='deletecar?num=${dto.num}'">삭제</button>
				<button type="button" class="btn btn-outline-info" onclick="location.href='carform'">등록</button>
				<button type="button" class="btn btn-outline-success" onclick="location.href='carlist'">목록</button>
			</td>
		</tr>
		
	</table>
</body>
</html>

결과


profile
java를 잡아...... 하... 이게 맞나...

0개의 댓글