Spring Data JPA ๐Ÿ”ฅ

JOY๐ŸŒฑยท2023๋…„ 4์›” 14์ผ
0

๐Ÿธ JPA

๋ชฉ๋ก ๋ณด๊ธฐ
8/8

๐Ÿ“Œ ๊ธฐ๋ณธ ์…‹ํŒ…

๐Ÿ‘‰ applicaiton.yml

# server port config
server:
  port: 8001
  
# DB config
spring:
  datasource:
    driver-class-name: oracle.jdbc.driver.OracleDriver
    url: jdbc:oracle:thin:@localhost:1521:xe
    username: C##GREEDY
    password: GREEDY
    
# JPA config
  jpa:
    generate-ddl: false       # create table์„ ํ•  ๋•Œ๋งŒ true(๊ธฐ๋ณธ๊ฐ’)
    show-sql: true            # JPA๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋Š” SQL๊ตฌ๋ฌธ์„ ์ฝ˜์†”์—์„œ ํ™•์ธ ๊ฐ€๋Šฅ
    database: oracle
    properties:
      hibernate:
        '[format_sql]': true  # SQL์„ ๊ฐœํ–‰ํ•˜์—ฌ ๋ณด๊ธฐ์ข‹๊ฒŒ ์ถœ๋ ฅ

๐Ÿ‘‰ pom.xml

<!-- Menu(์˜์†์„ฑ ๊ฐ์ฒด) -> MenuDTO(๋น„์˜์†์„ฑ ๊ฐ์ฒด)๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ModelMapper ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ -->
<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
	<dependency>
		<groupId>org.modelmapper</groupId>
		<artifactId>modelmapper</artifactId>
		<version>3.1.1</version>
	</dependency>

๐Ÿ‘‰ BeanConfiguration

@Configuration
public class BeanConfiguration {

	/* ModelMapper๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€ ํ›„, ModelMapper๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด Bean ๋“ฑ๋ก */
	@Bean
	public ModelMapper modelMapper() {
		
		return new ModelMapper();
	}
}

๐Ÿ‘‰ PagingButtonInfo

public class PagingButtonInfo {
	
	private int currentPage;
	private int startPage;
	private int endPage;
	
	/* ๊ธฐ๋ณธ ์…‹ํŒ… */

}

๐Ÿ‘‰ Pagenation

public class Pagenation {
	
	public static PagingButtonInfo getPagingButtonInfo(Page page) {
		
		int currentPage = page.getNumber() + 1;	 /* ์ธ๋ฑ์Šค ๊ธฐ์ค€์ด๋ฏ€๋กœ +1 */
		int defaultButtonCount = 10;
		int startPage;
		int endPage;
		
		startPage = (int) (Math.ceil((double) currentPage / defaultButtonCount) - 1) * defaultButtonCount + 1;
		endPage = startPage + defaultButtonCount - 1;
		
		/* ๋‚ด๊ฐ€ ๊ฐ€์ง„ ์ด ํŽ˜์ด์ง€ ์ˆ˜๊ฐ€ 4์ผ ๊ฒฝ์šฐ, endPage(10)๋ณด๋‹ค ์ž‘์œผ๋ฏ€๋กœ endPage๋ฅผ 4๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๋กœ์ง */
		if(page.getTotalPages() < endPage)
			endPage = page.getTotalPages();
		
		if(page.getTotalPages() == 0 && endPage == 0)
			endPage = startPage;
		
		return new PagingButtonInfo(currentPage, startPage, endPage);
	}
    
}

๐Ÿ‘€ Templates (views)

๐Ÿ‘‰ main.html

<h1 align="center">๐ŸŒญ๐Ÿค WELCOME TO GREEDY RESTAURANT ๐Ÿณ๐Ÿ•</h1>
	
	<div align="center">
	<button onclick="location.href='/menu/81'">81๋ฒˆ ๋ฉ”๋‰ด ๋ณด๊ธฐ</button>
	<button onclick="location.href='/menu/list'">๋ฉ”๋‰ดํŒ ๋ณด๊ธฐ</button>
	<button onclick="location.href='/menu/querymethod'">์ฟผ๋ฆฌ๋ฉ”์†Œ๋“œ ํ…Œ์ŠคํŠธ</button>
	<button onclick="location.href='/menu/regist'">๋ฉ”๋‰ด ๋“ฑ๋กํ•˜๊ธฐ</button>
	<button onclick="location.href='/menu/modify'">๋ฉ”๋‰ด ์ˆ˜์ •ํ•˜๊ธฐ</button>
	<button onclick="location.href='/menu/delete'">๋ฉ”๋‰ด ์‚ญ์ œํ•˜๊ธฐ</button>
	</div>

๐Ÿ‘‰ one.html

<h1 align="center">โœจ ๋ฉ”๋‰ด โœจ</h1>

	<table align="center" border="1">
		<tr>
			<th>๋ฉ”๋‰ด๋ฒˆํ˜ธ</th>
			<th>๋ฉ”๋‰ด๋ช…</th>
			<th>๋ฉ”๋‰ด๊ฐ€๊ฒฉ</th>
			<th>์นดํ…Œ๊ณ ๋ฆฌ์ฝ”๋“œ</th>
			<th>ํŒ๋งค์ƒํƒœ</th>
		</tr>
		<tr>
			<td th:text="${ menu.menuCode }"></td>
			<td th:text="${ menu.menuName }"></td>
			<td th:text="${ menu.menuPrice }"></td>
			<td th:text="${ menu.categoryCode }"></td>
			<td th:text="${ menu.orderableStatus }"></td>
		</tr>
	</table>
	
	<script>
	/* ๋ฉ”๋‰ด ์ˆ˜์ • ํ›„, ํ•ด๋‹น ํŽ˜์ด์ง€๋กœ redirect ๋˜๊ธฐ ์ „ alert ๋„์šฐ๊ธฐ */
	if(window.location.hash === '#menu-modify'){
	    alert('[[${message}]]'); // message ๋ณ€์ˆ˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ „๋‹ฌํ•œ Flash Attribute
	}
	</script>

๐Ÿ‘‰ list.html

<h1 align="center">โœจ ๋ฉ”๋‰ดํŒ โœจ</h1>

	<table align="center" border="1">
		<tr>
			<th>๋ฉ”๋‰ด๋ฒˆํ˜ธ</th>
			<th>๋ฉ”๋‰ด๋ช…</th>
			<th>๋ฉ”๋‰ด๊ฐ€๊ฒฉ</th>
			<th>์นดํ…Œ๊ณ ๋ฆฌ์ฝ”๋“œ</th>
			<th>ํŒ๋งค์ƒํƒœ</th>
		</tr>
		<tr th:each="menu : ${ menuList }">
			<td th:text="${ menu.menuCode }"></td>
			<td th:text="${ menu.menuName }"></td>
			<td th:text="${ menu.menuPrice }"></td>
			<td th:text="${ menu.categoryCode }"></td>
			<td th:text="${ menu.orderableStatus }"></td>
		</tr>
	</table>
	
	<div align="center" id="paging" >
		
		<!-- ๋งจ ์•ž์œผ๋กœ ์ด๋™ -->
		<button th:onclick="'location.href=\'/menu/list?page=' + @{${paging.startPage}} + '\''">โ—€โ—€</button>
		
		<!-- ์ด์ „ ํŽ˜์ด์ง€๋กœ ์ด๋™ -->
		<button th:onclick="'location.href=\'/menu/list?page=' + @{${paging.currentPage - 1}} + '\''"
				th:disabled="${ menuList.first }">โ—€</button>
		
		<!-- ์ˆซ์ž ๋ฒ„ํŠผ -->
		<th:block th:each="page : ${ #numbers.sequence(paging.startPage, paging.endPage)}">
			<button th:onclick="'location.href=\'/menu/list?page=' + @{${page}} + '\''"
					th:text="${ page }"
					th:disabled="${ paging.currentPage eq page }">
			</button>
		</th:block>
		
		<!-- ๋‹ค์Œ ํŽ˜์ด์ง€๋กœ ์ด๋™ -->
		<button th:onclick="'location.href=\'/menu/list?page=' + @{${paging.currentPage + 1}} + '\''"
				th:disabled="${ menuList.last }">โ–ถ</button>
		
		<!-- ๋งจ ๋์œผ๋กœ ์ด๋™ -->
		<button th:onclick="'location.href=\'/menu/list?page=' + @{${paging.endPage}} + '\''">โ–ถโ–ถ</button>
	</div>
	
	
	<script>
	/* ์‹ ๊ทœ ๋ฉ”๋‰ด ๋“ฑ๋ก or ๋ฉ”๋‰ด ์‚ญ์ œ ํ›„, ํ•ด๋‹น ํŽ˜์ด์ง€๋กœ redirect ๋˜๊ธฐ ์ „ alert ๋„์šฐ๊ธฐ */
	if(window.location.hash === '#menu-regist' || window.location.hash === '#menu-remove'){
	    alert('[[${message}]]'); // message ๋ณ€์ˆ˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ „๋‹ฌํ•œ Flash Attribute
	}
	</script>

๐Ÿ‘‰ list2.html

<h1 align="center">โœจ <span th:text="${ #numbers.formatInteger(menuPrice, 3, 'COMMA') }"></span>์› ์ดˆ๊ณผ์ธ ๋ฉ”๋‰ด๋“ค โœจ</h1>

	<table align="center" border="1">
		<tr>
			<th>๋ฉ”๋‰ด๋ฒˆํ˜ธ</th>
			<th>๋ฉ”๋‰ด๋ช…</th>
			<th>๋ฉ”๋‰ด๊ฐ€๊ฒฉ</th>
			<th>์นดํ…Œ๊ณ ๋ฆฌ์ฝ”๋“œ</th>
			<th>ํŒ๋งค์ƒํƒœ</th>
		</tr>
		<tr th:each="menu : ${ menuList }">
			<td th:text="${ menu.menuCode }"></td>
			<td th:text="${ menu.menuName }"></td>
			<td th:text="${ menu.menuPrice }"></td>
			<td th:text="${ menu.categoryCode }"></td>
			<td th:text="${ menu.orderableStatus }"></td>
		</tr>
	</table>

๐Ÿ‘‰ querymethod.html

<h3>์ž…๋ ฅ ๊ฐ€๊ฒฉ์„ ์ดˆ๊ณผํ•˜๋Š” ๋ฉ”๋‰ด์˜ ๋ชฉ๋ก ์กฐํšŒ</h3>
<form action="/menu/test1">
		
	<input type="number" name="menuPrice">์› ์ดˆ๊ณผ์˜ ๋ฉ”๋‰ด ๋ชฉ๋ก ์กฐํšŒ<br>
	<input type="submit" value="์กฐํšŒ">
		
</form>

๐Ÿ‘‰ regist.html

<h3 align="center">์‹ ๊ทœ ๋ฉ”๋‰ด ๋“ฑ๋ก</h3>
	
<form align="center" action="/menu/regist" method="post">
	<label>๋ฉ”๋‰ด ์ด๋ฆ„ : </label><input type="text" name="menuName"><br>
	<label>๋ฉ”๋‰ด ๊ฐ€๊ฒฉ : </label><input type="number" name="menuPrice"><br>
	<label>์นดํ…Œ๊ณ ๋ฆฌ : </label>
	<select name="categoryCode" id="categoryCode">
		<!-- 
			์นดํ…Œ๊ณ ๋ฆฌ ๋‚ด์šฉ์ด ๋ณ€ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์กฐํšŒ ํ•ด์„œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋ฉฐ
			์—ฌ๊ธฐ์—์„œ๋Š” ajax๋กœ ์ฒ˜๋ฆฌ
		-->
	</select><br>
	<label>ํŒ๋งค ์ƒํƒœ : </label>
	<select name="orderableStatus">
		<option value="Y">ํŒ๋งค๊ฐ€๋Šฅ</option>
		<option value="N">ํŒ๋งค๋ถˆ๊ฐ€</option>
	</select><br>
	<input type="submit" value="์ „์†ก">
</form>

<script>
	/* ๋น„๋™๊ธฐ ํ†ต์‹ (ajax)์„ ํ†ตํ•ด ๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ ๋ฆฌ์ŠคํŠธ ์กฐํšŒ */
	$(function(){
		$.ajax({
			url : '/menu/category',
			success : function(data) {
				console.log(data);
					
				let html = '';
				/* data๋ผ๋Š” ์กฐํšŒ๋œ ๋ฐฐ์—ด์„ ๋ฐ˜๋ณตํ•˜์—ฌ ํ•ฉ์‚ฐ์‹œํ‚ค๊ธฐ */
				for(let index in data) {
					html += `<option value='${data[index].categoryCode}'>${data[index].categoryName}</option>`;
					// ์กฐํšŒ๋œ ์นดํ…Œ๊ณ ๋ฆฌ์˜ ์ด๋ฆ„์„ ํ™”๋ฉด์— ๋…ธ์ถœ์‹œํ‚ค๊ณ  ๊ทธ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์„ ํƒ(<option>)๋˜๋ฉด value๋Š” ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ์˜ ์ฝ”๋“œ
				}
				/* ๊ทธ ๊ฐ’์„ <select>์— insertํ•˜์—ฌ ํ™”๋ฉด์— ๋…ธ์ถœ๋˜๋„๋ก ํ•จ */
				document.querySelector("#categoryCode").insertAdjacentHTML('beforeend', html);
					
			},
			error : function(xhr) {
				console.log(xhr);
			}
		});
	})
</script>

๐Ÿ‘‰ modify.html

	<h3 align="center">๋ฉ”๋‰ด ์ˆ˜์ •</h3>
	
	<form align="center" action="/menu/modify" method="post">
		<label>์ˆ˜์ •ํ•  ๋ฉ”๋‰ด : </label>
		<select name="menuCode" id="menuCode"></select><br>
		<label>์ƒˆ๋กœ์šด ๋ฉ”๋‰ด ์ด๋ฆ„ : </label>
		<input type="text" name="menuName"><br>
		<label>์ƒˆ๋กœ์šด ๋ฉ”๋‰ด ๊ฐ€๊ฒฉ : </label>
		<input type="number" name="menuPrice"><br>
		<label>์ƒˆ๋กœ์šด ๋ฉ”๋‰ด์˜ ์นดํ…Œ๊ณ ๋ฆฌ : </label>
		<select name="categoryCode" id="categoryCode"></select><br>
		<label>ํŒ๋งค ์ƒํƒœ : </label>
		<select name="orderableStatus">
			<option value="Y">ํŒ๋งค๊ฐ€๋Šฅ</option>
			<option value="N">ํŒ๋งค๋ถˆ๊ฐ€</option>
		</select><br>
		<input type="submit" value="์ „์†ก">
	</form>
	
	<script>
		/* ๋น„๋™๊ธฐ ํ†ต์‹ (ajax)์„ ํ†ตํ•ด ๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ ๋ฆฌ์ŠคํŠธ ์กฐํšŒ */
		$(function(){
			$.ajax({
				url : '/menu/category',
				success : function(data) {
					console.log(data);
					
					let html = '';
					/* data๋ผ๋Š” ์กฐํšŒ๋œ ๋ฐฐ์—ด์„ ๋ฐ˜๋ณตํ•˜์—ฌ ํ•ฉ์‚ฐ์‹œํ‚ค๊ธฐ */
					for(let index in data) {
						html += `<option value='${data[index].categoryCode}'>${data[index].categoryName}</option>`;
						// ์กฐํšŒ๋œ ์นดํ…Œ๊ณ ๋ฆฌ์˜ ์ด๋ฆ„์„ ํ™”๋ฉด์— ๋…ธ์ถœ์‹œํ‚ค๊ณ  ๊ทธ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์„ ํƒ(<option>)๋˜๋ฉด value๋Š” ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ์˜ ์ฝ”๋“œ
					}
					/* ๊ทธ ๊ฐ’์„ <select>์— insertํ•˜์—ฌ ํ™”๋ฉด์— ๋…ธ์ถœ๋˜๋„๋ก ํ•จ */
					document.querySelector("#categoryCode").insertAdjacentHTML('beforeend', html);
					
				},
				error : function(xhr) {
					console.log(xhr);
				}
			});
		})
		
		/* ๋น„๋™๊ธฐ ํ†ต์‹ (ajax)์„ ํ†ตํ•ด ๋ชจ๋“  ๋ฉ”๋‰ด ๋ฆฌ์ŠคํŠธ ์กฐํšŒ */
		$(function(){
			$.ajax({
				url : '/menu/menu',
				success : function(data) {
					console.log(data);
					
					let html = '';
					/* data๋ผ๋Š” ์กฐํšŒ๋œ ๋ฐฐ์—ด์„ ๋ฐ˜๋ณตํ•˜์—ฌ ํ•ฉ์‚ฐ์‹œํ‚ค๊ธฐ */
					for(let index in data) {
						html += `<option value='${data[index].menuCode}'>[${data[index].menuCode}] ${data[index].menuName}</option>`;
						// ์กฐํšŒ๋œ ๋ฉ”๋‰ด์ฝ”๋“œ์™€ ์ด๋ฆ„์„ ํ™”๋ฉด์— ๋…ธ์ถœ์‹œํ‚ค๊ณ  ๊ทธ ๋ฉ”๋‰ด๊ฐ€ ์„ ํƒ(<option>)๋˜๋ฉด value๋Š” ํ•ด๋‹น ๋ฉ”๋‰ด์˜ ์ฝ”๋“œ
					}
					/* ๊ทธ ๊ฐ’์„ <select>์— insertํ•˜์—ฌ ํ™”๋ฉด์— ๋…ธ์ถœ๋˜๋„๋ก ํ•จ */
					document.querySelector("#menuCode").insertAdjacentHTML('beforeend', html);
					
				},
				error : function(xhr) {
					console.log(xhr);
				}
			});
		})
	</script>

๐Ÿ‘‰ delete.html

	<h3 align="center">๋ฉ”๋‰ด ์‚ญ์ œ</h3>
	
	<form action="/menu/delete" method="post">
		<label>์‚ญ์ œํ•  ๋ฉ”๋‰ด์˜ ๋ฒˆํ˜ธ : </label>
		<input type="number" name="menuCode"><br>
		<input type="submit" value="์ „์†ก">
	</form>

๐Ÿ‘€ Entity

๐Ÿ‘‰ Category

@Entity
@Table(name="TBL_CATEGORY")
public class Category {

	@Id
	@Column(name="CATEGORY_CODE")
	private int categoryCode;
	
	@Column(name="CATEGORY_NAME")
	private String categoryName;
	
	@Column(name="REF_CATEGORY_CODE")
	private Integer refCategoryCode;
    
    /* ๊ธฐ๋ณธ ์…‹ํŒ… */
    
}

๐Ÿ‘‰ Menu

@Entity
@Table(name="TBL_MENU")
@SequenceGenerator(						
		name="SEQ_MENU_CODE_GENERATOR",	
		sequenceName="SEQ_MENU_CODE",	
		initialValue=100,				
		allocationSize=1				
)
public class Menu {
	
	@Id							
	@Column(name="MENU_CODE")	
	@GeneratedValue(								
			strategy=GenerationType.SEQUENCE,		
			generator="SEQ_MENU_CODE_GENERATOR"		
	)
	private int menuCode;
	@Column(name="MENU_NAME")
	private String menuName;
	@Column(name="MENU_PRICE")
	private int menuPrice;
	@Column(name="CATEGORY_CODE")
	private int categoryCode;
	@Column(name="ORDERABLE_STATUS")
	private String orderableStatus;
    
    /* ๊ธฐ๋ณธ ์…‹ํŒ… */
    
}

๐Ÿ‘€ DTO

๐Ÿ‘‰ CategoryDTO

public class CategoryDTO {

	private int categoryCode;
	private String categoryName;
	private Integer refCategoryCode;
    
    /* ๊ธฐ๋ณธ ์…‹ํŒ… */
    
}

๐Ÿ‘‰ MenuDTO

public class MenuDTO {
	
	private int menuCode;
	private String menuName;
	private int menuPrice;
	private int categoryCode;
	private String orderableStatus;
    
	/* ๊ธฐ๋ณธ ์…‹ํŒ… */

}

๐Ÿ‘€ Controller

๐Ÿ‘‰ MainController

@Controller
public class MainController {

	@GetMapping(value = {"/", "/main"})
	public String main() {
		
		return "main/main";
	}
	
}

๐Ÿ‘‰ MenuController

@Controller
@RequestMapping("/menu")
@Slf4j
public class MenuController {

	private final MenuService menuService;
	
	public MenuController(MenuService menuService) {
		this.menuService = menuService;
	}
	
	@GetMapping("/{menuCode}")
	public String findMenuByCode(@PathVariable int menuCode, Model model) {
		
		MenuDTO menu = menuService.findMenuByCode(menuCode);
		
		model.addAttribute("menu", menu);
		
		return "menu/one";
	}
	
	/* ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ ์ „ */
//	@GetMapping("list")
//	public String findMenuList(Model model) {
//		
//		List<MenuDTO> menuList = menuService.findMenuList();
//		model.addAttribute("menuList", menuList);
//		
//		return "menu/list";
//	}
	
	/* ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ ํ›„ */
	@GetMapping("list")
	public String findMenuList(@PageableDefault Pageable pageable, Model model) {
		
		/* page -> number, size, sort ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ Pageable ๊ฐ์ฒด์— ๋‹ด๊น€ */
		log.info("pageable : {}", pageable);
		
		Page<MenuDTO> menuList = menuService.findMenuList(pageable); // ๋ฐ˜ํ™˜๊ฐ’, ๋งค๊ฐœ๋ณ€์ˆ˜ ํƒ€์ž…์ด ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ ์ „๊ณผ ๋‹ค๋ฆ„
		
		log.info("์กฐํšŒํ•œ ๋‚ด์šฉ ๋ชฉ๋ก : {}", menuList.getContent());
		log.info("์ด ํŽ˜์ด์ง€ ์ˆ˜ : {}", menuList.getTotalPages());
		log.info("์ด ๋ฉ”๋‰ด ์ˆ˜ : {}", menuList.getTotalElements());
		log.info("ํ•ด๋‹น ํŽ˜์ด์ง€์— ํ‘œ์‹œ ๋  ์š”์†Œ ์ˆ˜ : {}", menuList.getSize());
		log.info("ํ•ด๋‹น ํŽ˜์ด์ง€์˜ ์‹ค์ œ ์š”์†Œ ์ˆ˜ : {}", menuList.getNumberOfElements());
		log.info("์ฒซ ํŽ˜์ด์ง€ ์—ฌ๋ถ€ : {}", menuList.isFirst());
		log.info("๋งˆ์ง€๋ง‰ ํŽ˜์ด์ง€ ์—ฌ๋ถ€ : {}", menuList.isLast());
		log.info("์ •๋ ฌ ๋ฐฉ์‹ : {}", menuList.getSort());
		log.info("์—ฌ๋Ÿฌ ํŽ˜์ด์ง€ ์ค‘ ํ˜„์žฌ ์ธ๋ฑ์Šค : {}", menuList.getNumber());
		
		/* StartPage์™€ EndPage๋ฅผ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•œ ๋กœ์ง(commonํŒจํ‚ค์ง€์— Pagenation, PagingButtonInfo ํ•„์š”) */
		PagingButtonInfo paging = Pagenation.getPagingButtonInfo(menuList);
		
		model.addAttribute("paging", paging);
		model.addAttribute("menuList", menuList);
		
		
		return "menu/list";
	}
	
	/* ์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ์˜ ์‚ฌ์šฉ */
	@GetMapping("/querymethod")
	public void queryMethodPage() {}
	
	@GetMapping("/test1")
	public String menuTest1(@RequestParam Integer menuPrice, Model model) { /* name ์†์„ฑ๊ณผ ์ผ์น˜์‹œํ‚ค๊ธฐ */
		
		List<MenuDTO> menuList = menuService.menuTest1(menuPrice);
		
		model.addAttribute("menuList", menuList);
		model.addAttribute("menuPrice", menuPrice);
		
		return "menu/list2";
	} 
	
	/* ๋ฉ”๋‰ด ๋“ฑ๋กํ•˜๊ธฐ (jpql, native query ์‚ฌ์šฉ) */
	@GetMapping("/regist")
	public void registPage() {}
	
	/* ์นดํ…Œ๊ณ ๋ฆฌ ๋ฆฌ์ŠคํŠธ๋ฅผ ์กฐํšŒํ•˜๋Š” ๋น„๋™๊ธฐ ํ†ต์‹ (ajax) */
	@GetMapping(value="category", produces="application/json; charset=UTF-8")
	@ResponseBody /* ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’์ด ๊ณง ์‘๋‹ต๋˜๋Š” ๊ฐ’์˜ ๋ฐ”๋”” */
	public List<CategoryDTO> findCategoryList() {
		
		return menuService.findAllCategory();
	}
	
	@PostMapping("/regist")
	public String registNewMenu(MenuDTO newMenu, RedirectAttributes rttr) {
		
		menuService.registNewMenu(newMenu);
		
		rttr.addFlashAttribute("message", "๋ฉ”๋‰ด ๋“ฑ๋ก ์„ฑ๊ณต! ๋“ฑ๋ก๋œ ๋ฉ”๋‰ด๋ฅผ ํ™•์ธํ•˜์„ธ์š” ๐Ÿฅณ");
		
		return "redirect:/menu/list#menu-regist";
	}
	
	/* ๋ฉ”๋‰ด ์ˆ˜์ •ํ•˜๊ธฐ */
	@GetMapping("/modify")
	public void modifyPage() {}
	
	/* ๋ฉ”๋‰ด ๋ฆฌ์ŠคํŠธ๋ฅผ ์กฐํšŒํ•˜๋Š” ๋น„๋™๊ธฐ ํ†ต์‹ (ajax) */
	@GetMapping(value="menu", produces="application/json; charset=UTF-8")
	@ResponseBody /* ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’์ด ๊ณง ์‘๋‹ต๋˜๋Š” ๊ฐ’์˜ ๋ฐ”๋”” */
	public List<MenuDTO> findMenuList() {
		
		return menuService.findMenuList();
	}
	
	@PostMapping("/modify")
	public String menuModify(@ModelAttribute MenuDTO menu, RedirectAttributes rttr) {
		
		menuService.modifyMenu(menu);
		
		rttr.addFlashAttribute("message", "๋ฉ”๋‰ด ์ˆ˜์ • ์„ฑ๊ณต! ์ˆ˜์ •๋œ ๋ฉ”๋‰ด๋ฅผ ํ™•์ธํ•˜์„ธ์š” ๐Ÿ˜");
		
		/* ์ˆ˜์ •๋œ ๋ฉ”๋‰ด์˜ ์ƒ์„ธํŽ˜์ด์ง€๋กœ ์ด๋™ */
		return "redirect:/menu/" + menu.getMenuCode() + "#menu-modify";
	}
	
	/* ๋ฉ”๋‰ด ์‚ญ์ œํ•˜๊ธฐ */
	@GetMapping("/delete")
	public void deletePage() {}
	
	@PostMapping("/delete")
	public String deleteMenu(@RequestParam Integer menuCode, RedirectAttributes rttr) {
		
		menuService.deleteMenu(menuCode);
		
		rttr.addFlashAttribute("message", "๋ฉ”๋‰ด ์‚ญ์ œ ์„ฑ๊ณต! ๐Ÿ‘€");
		
		return "redirect:/menu/list#menu-remove";
	}
	
}

๐Ÿ‘€ Service

๐Ÿ‘‰ MenuService

@Service
public class MenuService {
	
	private final CategoryRepository categoryRepository;
	private final MenuRepository menuRepository;
	private final ModelMapper modelMapper;
	
	public MenuService(CategoryRepository categoryRepository, MenuRepository menuRepository, ModelMapper modelMapper) {
		this.categoryRepository = categoryRepository;
		this.menuRepository = menuRepository;
		this.modelMapper = modelMapper;
	}
	
	/* 1. findById (ํŠน์ • ๋ฉ”๋‰ด ๋ณด๊ธฐ) */
	public MenuDTO findMenuByCode(int menuCode) {
		
		/* findById ๋ฉ”์†Œ๋“œ๋Š” ์ด๋ฏธ ๊ตฌํ˜„๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์ธํ„ฐํŽ˜์ด์Šค์— ๋”ฐ๋กœ ์ •์˜ํ•  ํ•„์š” X 
		 * findById์˜ ๋ฐ˜ํ™˜๊ฐ’์„ Optional ํƒ€์ž…. Optional ํƒ€์ž…์€ NPE๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์ด ์กด์žฌ
		 * ํ•ด๋‹น id๋กœ ์กฐํšŒ๋˜์ง€ ๋ชป ํ–ˆ์„ ๊ฒฝ์šฐ, IllegalArgumentException ๋ฐœ์ƒ์‹œํ‚ด */
		Menu menu = menuRepository.findById(menuCode).orElseThrow(IllegalArgumentException::new); 
		// ๋ฐ˜ํ™˜ํ˜•์ธ Optional<T>์€ ์กฐํšŒ๊ฐ€ ์•ˆ๋˜์—ˆ์„ ๊ฒฝ์šฐ(null์ผ ๊ฒฝ์šฐ)๋ฅผ ์ปค๋ฒ„ํ•ด์ฃผ๊ธฐ ์œ„ํ•œ ๋ฐ์ดํ„ฐํƒ€์ž… (NPE ๋ฐฉ์ง€)
		// [์ฝ”๋“œํ•ด์„] menuCode๊ฐ€ null์ผ ๊ฒฝ์šฐ, orElseThrow()๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด IllegalArgumentException์„ ๋ฐœ์ƒ์‹œํ‚ด
		
		/* modelMapper๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ entity๋ฅผ DTO๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ฐ˜ํ™˜ */
		return modelMapper.map(menu, MenuDTO.class);
	}
	
	/* 2. findAll (๋ฉ”๋‰ดํŒ ๋ณด๊ธฐ) */
	public List<MenuDTO> findMenuList() {
		
		/* findAll ๋ฉ”์†Œ๋“œ๋Š” ์ด๋ฏธ ๊ตฌํ˜„๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์ธํ„ฐํŽ˜์ด์Šค์— ๋”ฐ๋กœ ์ •์˜ํ•  ํ•„์š” X 
		 * Sort(์ •๋ ฌ) ๊ธฐ์ค€์„ ์ „๋‹ฌํ•˜๋ฉฐ ์กฐํšŒํ•  ์ˆ˜๋„ ์žˆ์Œ */
		List<Menu> menuList = menuRepository.findAll(Sort.by("menuCode").descending()); /* Sort.by().descending() : ๋‚ด๋ฆผ์ฐจ์ˆœ(์ตœ์‹ ์ˆœ)์œผ๋กœ ์ •๋ ฌ๋œ ํ˜•ํƒœ๋กœ ๊ฐ€์ ธ์˜ด */
		
		/* List<MenuDTO>์œผ๋กœ ๋ณ€ํ™˜์‹œํ‚ค๋Š” ์ฝ”๋“œ */
		return menuList.stream().map(menu -> modelMapper.map(menu, MenuDTO.class)).collect(Collectors.toList());
	}

	/* 3. Page -> ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ ํ›„ */
	public Page<MenuDTO> findMenuList(Pageable pageable) {

		/* page ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ Pageable์˜ number ๊ฐ’์œผ๋กœ ๋„˜์–ด์˜ค๋Š”๋ฐ, ํ•ด๋‹น ๊ฐ’ ์กฐํšŒ ์‹œ์—๋Š” ์ธ๋ฑ์Šค ๊ธฐ์ค€์ด ๋˜์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— -1 ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š” */
		pageable = PageRequest.of(pageable.getPageNumber() <= 0 ? 0 : pageable.getPageNumber() -1, 	// ๋ช‡ ๋ฒˆ์งธ ํŽ˜์ด์ง€์ธ์ง€?
								  pageable.getPageSize(),											// ๋ช‡ ๊ฐœ์”ฉ ๋ณด์—ฌ์ค„๊ฑด์ง€?
								  Sort.by("menuCode").descending());								// ์ •๋ ฌ ๊ธฐ์ค€
		
		Page<Menu> menuList = menuRepository.findAll(pageable);
		
		/* Page๋Š” map()์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด ๊ฐ„๋žตํ•˜๊ฒŒ ์ž‘์„ฑ ๊ฐ€๋Šฅ (Stream์œผ๋กœ ๋ณ€ํ™˜ ํ•„์š” X) */
		return menuList.map(menu -> modelMapper.map(menu, MenuDTO.class));
	}

	/* 4. QueryMethod */
	public List<MenuDTO> menuTest1(Integer menuPrice) {
		
//		List<Menu> menuList = menuRepository.findByMenuPriceGreaterThan(menuPrice);
//		List<Menu> menuList = menuRepository.findByMenuPriceGreaterThanOrderByMenuPrice(menuPrice);
		List<Menu> menuList = menuRepository.findByMenuPriceGreaterThan(menuPrice, Sort.by("menuPrice"));
		
		return menuList.stream().map(menu -> modelMapper.map(menu, MenuDTO.class)).collect(Collectors.toList());
	}

	/* 5. JPQL / native query (์นดํ…Œ๊ณ ๋ฆฌ ๋ฆฌ์ŠคํŠธ ์กฐํšŒ) */
	public List<CategoryDTO> findAllCategory() {

		List<Category> categoryList = categoryRepository.findAllCategory();
		
		return categoryList.stream().map(category -> modelMapper.map(category, CategoryDTO.class)).collect(Collectors.toList());
	}

	/* 6. save (๋ฉ”๋‰ด ๋“ฑ๋กํ•˜๊ธฐ) */
	@Transactional
	public void registNewMenu(MenuDTO newMenu) {
		
		/* CrudRepository์— ๋ฏธ๋ฆฌ ์ •์˜๋˜์–ด์žˆ๋Š” save() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ํ•˜๋‚˜์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ €์žฅ ๊ฐ€๋Šฅ (persist์™€ ๋™์ผ) */
		menuRepository.save(modelMapper.map(newMenu, Menu.class));
		
	}

	/* 7. findById (๋ฉ”๋‰ด ์ˆ˜์ •ํ•˜๊ธฐ-์—”ํ‹ฐํ‹ฐ ์กฐํšŒ ํ›„ ๊ฐ’ ๋ณ€๊ฒฝ) */
	@Transactional
	public void modifyMenu(MenuDTO menu) {
		
		Menu foundMenu = menuRepository.findById(menu.getMenuCode()).orElseThrow(IllegalArgumentException::new);
		
		foundMenu.setMenuName(menu.getMenuName());
		foundMenu.setMenuPrice(menu.getMenuPrice());
		foundMenu.setCategoryCode(menu.getCategoryCode());
		foundMenu.setOrderableStatus(menu.getOrderableStatus());
		
	}

	/* 8. delete (๋ฉ”๋‰ด ์‚ญ์ œํ•˜๊ธฐ) */
	@Transactional
	public void deleteMenu(Integer menuCode) {
		
		menuRepository.deleteById(menuCode);
		
	}

}

๐Ÿ‘€ Repository

๐Ÿ‘‰ MenuRepository

public interface CategoryRepository extends JpaRepository<Category, Integer> {

	/* findAll ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์—ฌ๊ธฐ์„œ๋Š” ์ง์ ‘ jpql / native query๋ฅผ ์ž‘์„ฑํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ์˜ ์„ค์ •์„ ํ™•์ธ */
	
	/* jpql์„ ์ž‘์„ฑ ์‹œ์—๋Š” value๋งŒ ์ž‘์„ฑํ•ด๋„ ๋˜๋‚˜, native query ์ž‘์„ฑ ์‹œ์—๋Š” ๋ฐ˜๋“œ์‹œ nativeQuery=true ์†์„ฑ์„ ์ •์˜ํ•ด์•ผํ•จ */
	@Query(value="SELECT CATEGORY_CODE, CATEGORY_NAME, REF_CATEGORY_CODE FROM TBL_CATEGORY ORDER BY CATEGORY_CODE ASC"
			, nativeQuery=true)
	public List<Category> findAllCategory(); /* ํ•ด๋‹น ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ์‹œ, ์œ„์˜ ์ฟผ๋ฆฌ๋ฌธ ์‹คํ–‰ */
	
}

๐Ÿ‘‰ CategoryRepository

/* JpaRepository๋ฅผ ์ƒ์†ํ•  ๊ฒฝ์šฐ, @Repository ์–ด๋…ธํ…Œ์ด์…˜์ด ์—†์–ด๋„ ๋นˆ ์Šค์บ๋‹ ๋จ */
public interface MenuRepository extends JpaRepository<Menu, Integer> { /* ์‚ฌ์šฉํ•  ์—”ํ‹ฐํ‹ฐ(Menu)๋ฅผ ์ œ๋„ค๋ฆญ์— ์„ ์–ธ ํ›„ import */

	/* ์ƒ์†ํ•œ JpaRepository์— ๊ตฌํ˜„์ด ๋˜์–ด์žˆ๋Š” ๊ฒƒ์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์ž‘์„ฑํ•  ์ฝ”๋“œ X 
	 * But, JpaRepository๋ฅผ ์ƒ์† ๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์†Œ๋“œ ์™ธ์˜ ๋ฉ”์†Œ๋“œ๋Š” ์ง์ ‘ ์ •์˜ */
	
	/* ์ „๋‹ฌ ๋ฐ›์€ ๊ฐ€๊ฒฉ์„ ์ดˆ๊ณผํ•˜๋Š” ๋ฉ”๋‰ด์˜ ๋ชฉ๋ก์„ ์กฐํšŒํ•˜๋Š” ๋ฉ”์†Œ๋“œ */
	List<Menu> findByMenuPriceGreaterThan(Integer menuPrice);

	/* ์ „๋‹ฌ ๋ฐ›์€ ๊ฐ€๊ฒฉ์„ ์ดˆ๊ณผํ•˜๋Š” ๋ฉ”๋‰ด์˜ ๋ชฉ๋ก์„ '๊ฐ€๊ฒฉ ์ˆœ'์œผ๋กœ ์กฐํšŒํ•˜๋Š” ๋ฉ”์†Œ๋“œ */
	List<Menu> findByMenuPriceGreaterThanOrderByMenuPrice(Integer menuPrice);
	
	/* ์ „๋‹ฌ ๋ฐ›์€ ๊ฐ€๊ฒฉ์„ ์ดˆ๊ณผํ•˜๋Š” ๋ฉ”๋‰ด์˜ ๋ชฉ๋ก์„ '์ „๋‹ฌ ๋ฐ›๋Š” ์ •๋ ฌ ๊ธฐ์ค€(sort)'์œผ๋กœ ์กฐํšŒํ•˜๋Š” ๋ฉ”์†Œ๋“œ */
	List<Menu> findByMenuPriceGreaterThan(Integer menuPrice, Sort sort);

}
profile
Tiny little habits make me

0๊ฐœ์˜ ๋Œ“๊ธ€