Gallery
Search
๊ณตํต - Controller, ๊ฒ์ ํค์๋์ ํ์ด์ง ์ฒ๋ฆฌ๋ฅผ ์ํ ํ์ด์ ๋ธ ๊ฐ์ฒด
์ฐ์ ๊ณตํต์ ์ผ๋ก ์ ์ฉ๋ ์ปจํธ๋กค๋ฌ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
@Controller
@RequiredArgsConstructor
public class BooksViewController {
private final AdminCategoriesService adminCategoriesService;
private final AdminBooksService adminBooksService;
@GetMapping("/admin/booksManage")
public String adminBooksManageView( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, // ํ์ฌ ํ์ด์ง, ํ์ด์ง ํฌ๊ธฐ
@RequestParam(defaultValue = "bookId") String sort, @RequestParam(defaultValue = "ASC") String direction, // ์ ๋ ฌ ๊ธฐ์ค, ์ ๋ ฌ ๋ฐฉํฅ
@RequestParam(value = "keyword", required = false) String keyword, // ๊ฒ์ ํค์๋
@AuthenticationPrincipal UserDetailsImpl userDetails, Model model) {
long startTime = System.currentTimeMillis();//์คํ์๊ฐ ์ธก์
Sort.Direction sortDirection = Sort.Direction.fromString(direction);
PageRequest pageRequest = PageRequest.of(page, size, sortDirection, sort);
BooksPageResponseDto booksPageResponseDto =
adminBooksService.findBooksWithPaginationAndSearching(userDetails.getUser(), keyword, pageRequest);
model.addAttribute("books", booksPageResponseDto.getAdminBooksResponseDtos());
model.addAttribute("currentPage", page);
model.addAttribute("totalPages", booksPageResponseDto.getTotalPages());
long endTime = System.currentTimeMillis();
long durationTimeSec = endTime - startTime;
System.out.println(durationTimeSec + "m/s"); // ์คํ์๊ฐ ์ธก์
return "admin/booksManage";
}
ํ๋ก ํธ์๋๋ก๋ถํฐ keyword ๋ฅผ QueryString์ผ๋ก โฆ?keyword=abc์ ๊ฐ์ ํํ๋ก HTMLํผ์ผ๋ก๋ถํฐ ํน์ ๊ฐ๋ค์ ์ถ์ถ ํ ์ ์์ผ๋ฉฐ, HTTP์์ฒญ์ ๋ด์ ์ปจํธ๋กค๋ฌ๋ก ๋ฌธ์์ด์ ์ ๋ฌ ํ ์ ์์ต๋๋ค.
@RequestParam(value = "keyword", required = false) String keyword, // ๊ฒ์ ํค์๋
ํ์ด์ง ์ฒ๋ฆฌ๋ฅผ ์ํด์๋ ์์ JPA๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด Total Count๋ฅผ ๋ฝ์์์ ํ์ฌ ๋ด๊ฐ ๋ช๋ฒ์งธ ํ์ด์ง์ธ์ง๋ฅผ ๊ณ์ฐํด์ ํ์๋ฅผ ํด์ฃผ์ด์ผ ํฉ๋๋ค. ๊ทธ๋ฐ๋ฐ ์ด๋ฐ ๊ณ์ฐ์ ๋ชจ๋ ์ง์ ๊ณ์ฐํ๋ ๋ฉ์๋, ๋ณ์๋ฅผ ์ง์ ์์ฑํ๊ณ ๋งค๊ฐ๋ณ์๋ก Service์ ์ ๋ฌํ๊ฒ ๋๋ฉด ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ฒ ๋ฉ๋๋ค!ย ํ์ด์ ๋ธ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ง ์๋ ๊ฒฝ์ฐ ์ถ๊ฐ๋์ด์ผ ํ๋ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
// ์ง์ offset๊ณผ limit ๊ณ์ฐ
int offset = page * size;
int limit = size;
// ์ ์ฒด ํ์ด์ง ์ ๊ณ์ฐ ๋ฉ์๋
private int calculateTotalPages(long totalCount, int pageSize) {
return (int) Math.ceil((double) totalCount / pageSize);
}
BooksPageResponseDto booksPageResponseDto =
adminBooksService.findBooksWithPaginationAndSearching(userDetails.getUser(), keyword, offset, limit, sortDirection, sort);
[Spring][258] JPA โ JPQL โ QueryDSL ๋ณํ๊ณผ์
๊ฒ์๊ธ์ ์์๋ ๋๊ธ ์ถ๊ฐํ๋ API
CommentController์์ ๊ณ์ POSTMAN์ผ๋ก blogId ํ๋ผ๋ฏธํฐ๋ฅผ ๋ชป๋ฐ์์จ๋ค.
// ๋๊ธ ์์ฑ by User API
@PostMapping("/comment")
public CommentResponseDto createComment(@RequestBody CommentRequestDto requestDto, HttpServletRequest req) {
User user = (User) req.getAttribute("user"); // ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ถ์ถ
String blogId = req.getParameter("blogId"); // ํด๋ผ์ด์ธํธ์์ ์ ๋ฌํ blogId ๊ฐ์ ๊ฐ์ ธ์ด
// ๋๊ธ ์์ฑ์ ํ์ํ user์ blogId ์ ๋ณด๋ฅผ commentService์ ์ ๋ฌ
return commentService.createComment(requestDto, user.getUsername(), blogId);
}
๋ํ๋๋ ์ค๋ฅ๋ ๋๊ฐ์ง ์ ํ์ด์๋ค.
์ฒซ๋ฒ์งธ๋ NullPointerException์ผ๋ก blogId์์ฒด๊ฐ ์ ๋ฌ๋์ง ์๋ ์ํฉ,
2023-09-14T22:00:08.588+09:00 ERROR 35675 --- [nio-8080-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.NullPointerException: Cannot invoke "java.lang.Long.toString()" because "blogId" is null] with root cause
java.lang.NullPointerException: Cannot invoke "java.lang.Long.toString()" because "blogId" is null
at com.yzpocket.blog.controller.CommentController.createComment(CommentController.java:33) ~[main/:na]
์ด๊ฑด ๋๋ฌด ๋ปํ๋ค. ์ ์ฝ๋์์ blogId๋ฅผ req์ด๋ ๋ณ์, ์ด๋ ๊ณง HttpServletRequest์์ ๋ฐ๊ณ ์๋๋ฐ ์ฌ๊ธฐ ์์๋ฆฌ๊ฐ ์๋ค. ์๋ณธ ๊ธ์ id๋ ํด๋ผ์ด์ธํธ์์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์์ผ ํ๋ค. ๊ทธ๋์ ์๋ ์ฝ๋์๊ฐ์ด ๋ณ๊ฒฝํ๋ค.
// ๋๊ธ ์์ฑ by User, blogID API
@PostMapping("/comment")
public CommentResponseDto createComment(@RequestBody CommentRequestDto requestDto, @RequestParam("blogId") Long blogId, HttpServletRequest req) {
log.info(blogId.toString());
User user = (User) req.getAttribute("user"); // ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ถ์ถ
String blogIdStr = (blogId != null) ? blogId.toString() : null;
// ๋๊ธ ์์ฑ์ ํ์ํ user์ blogId ์ ๋ณด๋ฅผ commentService์ ์ ๋ฌ
log.debug(blogIdStr); // ๋๋ฒ๊ทธ ๋ ๋ฒจ์ ๋ก๊ทธ๋ก ์คํ ํธ๋ ์ด์ค ๋ฐ ๋๋ฒ๊ทธ ์ ๋ณด๋ฅผ ์ถ๋ ฅ
return commentService.createComment(requestDto, user.getUsername(), blogIdStr);
}
[Spring][JPA]55 ๋๊ธ๊ธฐ๋ฅ ๊ตฌํ ์ค ํธ๋ฌ๋ธ์ํ
- POSTMAN ๋ฒ๊ทธ? ์ ํ ์ํ
๋์ง ์์์ ํ๋ฌดํ๋ค. Required request parameter 'Argumentsโ for method parameter type Long is not present
CascadeType.REMOVE
โข
CASCADE์ REMOVE ์ต์
์ ์ ์ฉํ๋ฉด ํด๋น Entity ๊ฐ์ฒด๋ฅผ ์ญ์ ํ์ ๋ ์ฐ๊ด๋ Entity ๊ฐ์ฒด๋ค์ ์๋์ผ๋ก ์ญ์
โข
ํ์ง๋ง REMOVE ์ต์
๊ฐ์ ๊ฒฝ์ฐ ์ฐ๊ด๋ Entity์ ๊ด๊ณ๋ฅผ ์ ๊ฑฐํ๋ค๊ณ ํด์ ์๋์ผ๋ก ํด๋น Entity๊ฐ ์ญ์ ๋์ง๋ ์๋๋ค.
@Entity
@Getter
@Setter
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "user", cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private List<Food> foodList = new ArrayList<>();
public void addFoodList(Food food) {
this.foodList.add(food);
food.setUser(this);// ์ธ๋ ํค(์ฐ๊ด ๊ด๊ณ) ์ค์
}
}
orphanRemoval = true
@Entity
@Getter
@Setter
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST, orphanRemoval = true)
private List<Food> foodList = new ArrayList<>();
public void addFoodList(Food food) {
this.foodList.add(food);
food.setUser(this);// ์ธ๋ ํค(์ฐ๊ด ๊ด๊ณ) ์ค์
}
}
orphanRemoval ์ต์
๋ REMOVE ์ต์
๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ํด๋น Entity ์ฆ, Robbie Entity ๊ฐ์ฒด๋ฅผ ์ญ์ ํ๋ฉด ์ฐ๊ด๋ ์์ Entity๋ค์ด ์๋์ผ๋ก ์ญ์ ๋๋ค.
โข
orphanRemoval์ด๋ REMOVE ์ต์
์ ์ฌ์ฉํ ๋ ์ญ์ ํ๋ ค๊ณ ํ๋ ์ฐ๊ด๋ Entity๋ฅผ ๋ค๋ฅธ ๊ณณ์์ ์ฐธ์กฐํ๊ณ ์๋์ง ์๋์ง๋ฅผ ๊ผญ ํ์ธ ํ์
[Spring][JPA]54 ๊ณ ์ Entity ์ญ์ (๊ฒ์๊ธ-๋๊ธ)

[Spring][JPA]53 Fetch Type, ์ง์ฐ๋ก๋ฉ(LAZY), ์ฆ์๋ก๋ฉ(EAGER)
JPAย Auditing์ด๋?
Java์์ ORM ๊ธฐ์ ์ธ JPA๋ฅผ ์ฌ์ฉํ์ฌ ๋๋ฉ์ธ์ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ์ ๋งคํํ ๋ ๊ณตํต์ ์ผ๋ก ๋๋ฉ์ธ๋ค์ด ๊ฐ์ง๊ณ ์๋ ํ๋๋ ์ปฌ๋ผ๋ค์ด ์กด์ฌํ๋ค. ๋ํ์ ์ผ๋ก ์์ฑ์ผ์, ์์ ์ผ์, ์๋ณ์ ๊ฐ์ ํ๋ ๋ฐ ์ปฌ๋ผ์ด ์๋ค.
๋๋ฉ์ธ๋ง๋ค ๊ณตํต์ผ๋ก ์กด์ฌํ๋ค๋ ์๋ฏธ๋ ๊ฒฐ๊ตญ ์ฝ๋๊ฐ ์ค๋ณต๋๋ค๋ ๋ง์ด๋ฉฐ ์ค๋ณต์ ์ ๊ฑฐํ๋ ๊ฒ์ ํ๋ก์ ํธ์ ์ ์ง๋ณด์์ ์ธก๋ฉด์์ ํต์ฌ์ ์ธ ์ฌ์์ด๋ค.
๋ฐ์ดํฐ๋ฒ ์ด์ค์์ย ๋๊ฐ, ์ธ์ ํ์๋์ง ๊ธฐ๋ก์ ์ ๋จ๊ฒจ๋์์ผ ํ๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์์ฑ์ผ, ์์ ์ผ ์ปฌ๋ผ์ ๋๋จํ ์ค์ํ ๋ฐ์ดํฐ๋ค.
๊ทธ๋์ JPA์์๋ย Audit์ด๋ผ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๊ณ ์๋ค. Audit์ย ๊ฐ์ํ๋ค, ๊ฐ์ฌํ๋ค๋ผ๋ ๋ป์ผ๋ก Spring Data JPA์์ ์๊ฐ์ ๋ํด์ ์๋์ผ๋ก ๊ฐ์ ๋ฃ์ด์ฃผ๋ ๊ธฐ๋ฅ. ๋๋ฉ์ธ์ ์์์ฑ ์ปจํ
์คํธ์ ์ ์ฅํ๊ฑฐ๋ ์กฐํ๋ฅผ ์ํํ ํ์ update๋ฅผ ํ๋ ๊ฒฝ์ฐ ๋งค๋ฒ ์๊ฐ ๋ฐ์ดํฐ๋ฅผ ์
๋ ฅํ์ฌ ์ฃผ์ด์ผ ํ๋๋ฐ, audit์ ์ด์ฉํ๋ฉด ์๋์ผ๋ก ์๊ฐ์ ๋งคํํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ
์ด๋ธ์ ๋ฃ์ด์ค ์ ์๋ค.
์์กด์ฑ ์ถ๊ฐ
๊ธฐ๋ณธ์ ์ผ๋ก ์คํ๋ง ๋ถํธ์์ gradle๋ก ์์กด์ฑ์ ๊ด๋ฆฌํ๊ฒ ๋ ๊ฒฝ์ฐย spring-boot-starter-data-jpa๋ง ์ถ๊ฐํด๋ Audit์ ํ๋๋ฐ๋ ๋ฌธ์ ๊ฐ ์๋ค. ์ด ๋ง์ Spring-Data-JPA ์์ ํด๋น ๊ธฐ๋ฅ์ด ์๋ค๋ ๋ง๊ณผ ๊ฐ๋ค.
dependencies {
...
//SpringBoot JPA
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
...
}
[Spring][JPA]52 JPA Auditing์ด๋? SpringBoot์์ ๊ตฌ์ฑ ๋ฐ ํ์ฑํ ์ํค๊ธฐ ์ค์ต
๋ค๋์ผ (N:1) ๊ด๊ณ ๋จ๋ฐฉํฅ๊ณผ ์๋ฐฉํฅ
1. ๋ค๋์ผ ๊ด๊ณ ๋จ๋ฐฉํฅ
๋ค๋์ผ (N:1) ๊ด๊ณ (๋จ๋ฐฉํฅ)
โข
์์: ํ๋์ ์ฃผ๋ฌธ(Order)์ ํ๋์ ๊ณ ๊ฐ(Customer)์ ์ํฉ๋๋ค.
โข
์ค๋ช
:
โข
Spring JPA ์ด๋
ธํ
์ด์
@Entity
public class Order {
// ๋ค๋ฅธ ํ๋๋ค...
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;
// Getter์ Setter...
}
@Entity
public class Customer {
// ๋ค๋ฅธ ํ๋๋ค...
// Getter์ Setter...
}
2. ๋ค๋์ผ ๊ด๊ณ ์๋ฐฉํฅ
[Spring][JPA]49_2 Entity ์ฐ๊ด๊ด๊ณ ํ๊ฐ๋ ค์ ์์๋ฅผ ํตํด ํ๋ฒ ๋ ์ ๋ฆฌ
์ฐ๊ด๊ด๊ณ ๋งคํ์ ๊ณ ๋ ค์ฌํญ 3๊ฐ์ง
โข
๋ค์ค์ฑ
โข
๋จ๋ฐฉํฅ, ์๋ฐฉํฅ
โข
์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ
๋ค๋์ผ[N:1] ์ฐ๊ด๊ด๊ณ
๋ค๋์ผ[N:1] ๋จ๋ฐฉํฅ
โข
๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ ์ฐ๊ด๊ด๊ณ
โข
๋ค๋์ผ์ ๋ฐ๋๋ ์ผ๋๋ค
[Spring][JPA]49 Entity ์ฐ๊ด๊ด๊ณ N:1, 1:N, 1:1, N:M ๋งตํ ๋ฐ ๋ฐฉํฅ
์ ์์ฝ, ํ ์์
JPA๋ ์๋ฐ ์ง์์ ORM ๊ธฐ์ ์ ๋ํ API ํ์ค ๋ช
์ธ์ด๋ฉฐ
Hibernate๋ JPA์ ๊ตฌํ์ฒด์ด๊ณ , ๋ด๋ถ์ ์ผ๋ก JDBC๋ฅผ ์ด์ฉํ๋ค.
Spring Data JPA๋ JPA๋ฅผ ์ฌ์ฉํ๊ธฐ ์ฝ๊ฒ ์คํ๋ง์์ ์ ๊ณตํ๋ ๋ชจ๋๋ก ๋ด๋ถ์ ์ผ๋ก JPA ๊ตฌํ์ฒด๋ฅผ ์ด์ฉํ๋ค.
JPA์ด์ ์ ๋ฐฐ๊ฒฝ, SQL Mapper๋?
SQL Mapper๋ ๊ฐ๋ฐ์๊ฐ ์ง์ SQL๋ฌธ์ ์์ฑํด ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ Persistence Framework์ด๋ค. ๊ฐ์ฒด์ ํ
์ด๋ธ ๊ฐ์ ๊ด๊ณ๋ฅผ ์ง์ ๋งคํํ๋ ๊ฒ์ด ์๋, SQL๋ฌธ์ ์คํํด ์ฟผ๋ฆฌ ์ํ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ค ๊ฐ์ฒด์ ๋งคํํ ์ง ๋ฐ์ธ๋ฉ ํ๋ ๋ฐฉ๋ฒ์ด๋ค.
๋ฐ๋ผ์ DBMS์ ์ข
์์ ์ธ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ํ ์ ์๋ค. ๋ํ์ ์ผ๋ก MyBatis๊ฐ ์๋ค. ์ฌ๊ธฐ ๋จ๊ณ๊น์ง๋ JDBC Template๋ฅผ ๊ฐ์ ํด์ ์ฌ์ฉํ์ง๋ง, ๊ฒฐ๊ตญ SQL๋ฌธ์ ์ง์ ์์ฑํด์ผ๋๋ค๋ ๋จ์ ์ด ์๋ค.
์ฅ์
[Spring][JPA]51 JPA๋? MyBatis(SQL Mapper)๋ถํฐ ์์ ํต์ฌ์์ฝ
ํต์ฌ ์ด๋
ธํ
์ด์
@Entity
โข
@Entity ์ด๋
ธํ
์ด์
์ด ๋ถ์ ํด๋์ค๋ JPA๊ฐ ๊ด๋ฆฌํ๋ ์ํฐํฐ์ด๋ค
โข
JPA๋ฅผ ์ฌ์ฉํด์ ํ
์ด๋ธ๊ณผ ๋งคํํ ํด๋์ค๋ @Entity ์ด๋
ธํ
์ด์
์ด ํ์๋ค
โข
๊ธฐ๋ณธ ์์ฑ์ ํ์(ํ๋ผ๋ฏธํฐ๊ฐ ์๋ public ๋๋ protected ์์ฑ์)
โข
final ํด๋์ค, enum, interface, inner ํด๋์ค ์ฌ์ฉ X
โข
์ ์ฅํ ํ๋์ final ์ฌ์ฉ X
โข
name ์์ฑ์ ์ฌ์ฉํ๋ฉด JPA์์ ์ฌ์ฉํ ์ํฐํฐ ์ด๋ฆ์ ์ง์ ํ ์ ์๋ค.
[Spring][JPA]41 ์ํฐํฐ ๋งคํ, ํ๋ ์ปฌ๋ผ ๋งคํ, ๊ธฐ๋ณธํค ๋งคํ ๋ฑ ํ์ฌ๊น์ง ์ฃผ์ ์ด๋
ธํ
์ด์
[Spring][JPA]39 MySql - Java ๋ฐ์ดํฐ ํ์
Load more


