63일차
테이블을 잘마드려다 보니 중복을 제거하는 등의 일을해서
여러 테이블이 만들어졌다
묶어서 조회하는 것이 join이다.
35. leet코드
35.1 문제 175
테이블 두개가 있다. 두개의 테이블을 JOIN하여 조회하기
CREATE TABLE Person (
personId INT PRIMARY KEY,
lastName VARCHAR(200),
firstName VARCHAR(200)
);CREATE TABLE Address (
addressId INT PRIMARY KEY,
personId INT,
city VARCHAR(200),
state VARCHAR(200)
);SELECT p.firstName, p.lastName, a.city, a.state
FROM Person p LEFT
JOIN Address a
ON p.personId = a.personId;35.2 leet코드 183
두개의 테이블이 있는데 키가 겹치지 않는 데이터를 추출하기
한번도 주문하지 않은 고객 조회하기
CREATE TABLE Customers (
id INT PRIMARY KEY,
name VARCHAR(200)
);CREATE TABLE Orders (
id INT PRIMARY KEY,
customerId INT
);SELECT name Customers
FROM Customers c
LEFT JOIN Orders o ON c.id = o.customerId
WHERE customerId IS NULL ;35.3 leet코드 181
self join 하나의 테이블을 두개의 테이블처럼 간주하고 join하는 것이다.
같은테이블이라 헷갈릴뿐이지 join하는 것은 똑같다.
다음의 테이블이 있을때 본인의 관리자보다 더 많이 번사람
CREATE TABLE Employee (
id INT PRIMARY KEY,
name VARCHAR(200),
salary INT,
managerId INT
);SELECT e1.name Employee FROM Employee e1
JOIN Employee e2 ON e1.managerId = e2.id
WHERE e1.salary > e2.salary;36 서브쿼리
위 문제를 join말고도 다른방법으로도 풀 수있다.
SELECT name Employee
FROM Employee e1
WHERE salay >
(SELECT salay FROM Employee e2 WHERE e2.id = e1.managerId);어떤 쿼리에 다른 쿼리가 포함되는 경우가 있다.
이런 경우를 서브 쿼리라고 한다.
책 430
서브쿼리는 항상 SELECT문이다.
그리고 항상 괄호 안에 있어야한다.
서브쿼리안의 세미콜론은 존재하지않는다.
서브쿼리는 4군데에 나올수 잇다.
1.WHERE절
2.열 들 중 하나
3.FROM절
4.HAVING 절
서브쿼리는 SELECT뿐만아니라 INSERT DELETE UPDATE와 함께 사용할 수 있다.
1번상품의 카테고리 이름이 무엇인가?
SELECT * FROM Products WHERE ProductID = 1; -- CategoryID 1번
SELECT CategoryName FROM Categories WHERE CategoryID = 1; -- CategoryID 1번의 이름
SELECT CategoryName FROM Categories WHERE CategoryID = (SELECT CategoryID FROM Products WHERE ProductID = 1);
-- 조건에 CategoryID 1번인 것을 넣기2번공급자의 나라 와 같은 나라에 사는 고객
SELECT Country FROM Suppliers WHERE SupplierID = 2; -- USA
SELECT * FROM Customers WHERE Country = 'USA';
SELECT * FROM Customers WHERE Country = (SELECT Country FROM Suppliers WHERE SupplierID = 2);평균 가격보다 높은 가격의 상품들 조회
SELECT * FROM Products
WHERE Price > (SELECT AVG(Price) FROM Products);37 서브쿼리 - 2
여러 결과가 나오는 서브쿼리를 사용할때 사용하는 방법을 알아보자.
WHERE 절에 IN을 사용하면된다.
예제 1996-07-04에 주문한 상품명 조회
1.1996-07-04에 주문한 Orderid구하기
SELECT OrderID FROM Orders WHERE OrderDate = '1996-07-04'; 2.오더가 있는 상품 id구하기
SELECT ProductID FROM OrderDetails
WHERE OrderID IN (SELECT OrderID FROM Orders WHERE OrderDate = '1996-07-04'); -- 오더가 있는 상품 id구하기3.상품 이름 구하기
SELECT ProductName FROM Products
WHERE ProductID IN
(SELECT ProductID FROM OrderDetails WHERE OrderID IN (SELECT OrderID FROM Orders WHERE OrderDate = '1996-07-04'));서브쿼리와 JOIN 중 더 맞는 것을 사용하는 것이 좋다.
보통 JOIN이 더빠르다 JOIN이 안되면 서브쿼리를 사용해보자.
38 연습
leet183번 서브쿼리로 풀어보기
훨씬 간단한 문제가 되었다.
SELECT name FROM Customers WHERE id NOT IN (SELECT customerId FROM Orders);w3schools 데이터베이스
서브쿼리 사용, 주문한적 없는 고객들 조회
SELECT CustomerID FROM Orders; -- 주문한 고객들 조회
SELECT * FROM Customers
WHERE CustomerID NOT IN
(SELECT CustomerID FROM Orders); -- NOT IN 주문한 고객들 = 주문안한 고객39 서브쿼리 - 3
SELECT절에 특정컬럼으로 서브쿼리를 사용할 수 있다.
카테고리id와 상품명을 출력하는데 대신 카테고리 이름이 조회되게 하기
SELECT * FROM Products;
SELECT ProductName, CategoryID FROM Products;
SELECT ProductName, (SELECT CategoryName FROM Categories c WHERE c.CategoryId = p.CategoryId) CategoryName FROM Products p;연습 상품명, 상품을 공급하는 공급자 명
join과 서브쿼리을 둘다 사용할 수 있다.
SELECT ProductName,
(SELECT SupplierName FROM Suppliers s WHERE s.SupplierID = p.SupplierID) SupplierName
FROM Products p;SELECT ProductName, SupplierName
FROM Products p
JOIN Suppliers s ON s.SupplierID = p.SupplierID;연습 leetcode 175번
firstName, lastName, city, state 조회하기 서브쿼리로 작성해보기
SELECT firstName, lastName,
(SELECT city FROM Address a WHERE p.personId = a.personId) city,
(SELECT state FROM Address a WHERE p.personId = a.personId) state
FROM Person p;40. 서브쿼리 - 4
FROM절에 서브쿼리를 사용할 수 있다.
SELECT절의 결과에서 또 다른 것을 조회할때 사용한다.
FROM절에 서브쿼리를 사용할때는 별칭을 무조건 붙여줘야한다.
하나의 테이블로 조회할 수 있는데 FROM절에 사용하기 위해 쓸데없는 쿼리가 되었다.
SELECT LastName, FirstName FROM
(SELECT LastName, FirstName FROM Employees) emp
WHERE LastName >= 'd' AND LastName < 'e';고객별 소비 금액을 서브쿼리의 평균 구하기
소비금액 구한 전체가 서브쿼리 그자체가 된다.
SELECT AVG(소비금액) FROM(SELECT c.CustomerID, c.CustomerName, SUM(p.Price * d.Quantity) 소비금액
FROM Orders o JOIN OrderDetails d ON o.OrderID = d.OrderID
JOIN Customers c ON o.CustomerID = c.CustomerID
JOIN Products p ON d.ProductID = p.ProductID
GROUP BY c.CustomerID) t1;연습문제
(직원별 매출액) 평균
직원별 매출액을 구하고 그것의 평균을 내엇다.
SELECT AVG(sum) AVG FROM (SELECT SUM(Quantity * Price) sum FROM Orders o
JOIN OrderDetails d ON o.OrderID = d.OrderID
JOIN Products p ON p.ProductID = d.ProductID
JOIN Employees e ON e.EmployeeID = o.EmployeeID
GROUP BY e.EmployeeID) st;카테고리별 평균 상품가격조회(평균상품가격이 30달러 이상인것만)
서브쿼리와 join둘다 사용할 수 있다.
SELECT CategoryID, average FROM
(SELECT CategoryId, AVG(Price) average FROM Products GROUP BY CategoryID) t1
WHERE average >= 30.00;SELECT c.CategoryID, AVG(Price) average FROM Categories c
JOIN Products p ON c.CategoryID = p.CategoryID
GROUP BY c.CategoryID HAVING average > 30.00;41. method = RequestMethod
@RequestMapping어노테이션에 method를 지정해주면 get방식에만 일하게 된다.
POST방식으로 바뀌면 메소드는 일하지 않는다
특정방식의 request에만 일하게 할 수 있다.
@RequestMapping(path = "link1", method = RequestMethod.GET)
public void method1() {
System.out.println("/sub25/link1에서 메소드 일함");
}
}@RequestMapping(path = "link1", method = RequestMethod.POST)
public void method1() {
System.out.println("/sub25/link1에서 메소드 일함");
}42 @GetMapping, @PostMapping
path = "link2", method = RequestMethod.GET이렇게 쓰는것이 너무 길어서 새로 어노테이션이 나왔다.
@GetMapping이 위 역할을 한다.
@GetMapping("link2")
public void method2() {
System.out.println("/sub25/link2 GET에서 메소드 일함");
}같은 방식으로 @PostMapping어노테이션을 사용하면 포스트 방식의 일을 처리한다.
@RequestMapping(path = "link3", method = RequestMethod.POST)
@PostMapping("link3")
public void method3() {
System.out.println("/sub25/link3에서 메소드 일함");
}43 요청경로에 변수담기
요청 코드의 일부분을 변수를 담아서 사용할 수 있다.
링크에{변수} 넣고 아규먼트에 @PathVariable어노테이션을 달아주면 요청경로에 이 변수가 들어가게 된다.
이런것을 Path Map이라고 한다.
@GetMapping("link4/{var1}")
public void method4(@PathVariable("var1") String p1) {
System.out.println("var1: " + p1);
}다음 메소드에서 /sub25/link5/abc/55로 요청하면
id에 abc 나이에 55가 들어가게 된다.
@GetMapping("link5/{id}/{age}")
public void method5(@PathVariable("id") String id,
@PathVariable("age") int age) {
System.out.println(id + ", " + age);
}중간중간 섞어서도 사용할 수 있다.
경로의 일부분을 값인 것처럼 사용할 수 있다.
// /sub25/link6/id/3/name/son
@GetMapping("link6/id/{var1}/name/{var2}")
public void method6(@PathVariable("var1") String id,
@PathVariable("var2") String name) {
System.out.println(id + ", " + name);
}44. redirect
반환타입을 String으로하면 해당주소의 jsp를 찾으려고 한다.
redirect를 하는방법을 알아보자.
기존의 방법처럼 Servlet api를 사용해서 response.sendRedirect할 수도 있다.
스프링을 활용하면 redirect: prefix를 사용하면된다.
결론적으로 그냥 주소를 적으면 forward String 리턴타입으로 prefix를 붙이면 redirect를 할 수 있다.
@GetMapping("link8")
public String method8() {
return "redirect:/sub25/link7";
}45. RedirectAttributes
포워드할때는 Model을 통해서 값을 전달했엇는데
redirect에서는 값을 어떻게 전달할까
redirect는 다른 곳으로 가라는 요청이기때문에 Model에 담을 수 없다.
RedirectAttributes객체를 사용해서 값을 넘길 수 있다.
이 객체의 addFlashAttribute메소드를 사용하면 redirect에 데이터를 담아 보낼 수 있다.
JSP에선 EL로 꺼내서 사용할 수 있다.
@GetMapping("link11")
public String method11(RedirectAttributes rttr) {
rttr.addFlashAttribute("attr1", "redirect attribute!");
return "redirect:/sub25/link10";
}두번째 타입이 Object타입이기때문에 아무 객체나 넣을 수 있다.
@GetMapping("link12")
public String method12(RedirectAttributes rttr) {
rttr.addFlashAttribute("list", List.of("java", "spring"));
return "redirect:/sub25/link13";
}@GetMapping("link13")
public void method13() {
}46 RedirectAttributes - 2
RedirectAttributes객체에 addFlashAttribute메소드를 사용했었다.
addFlashAttribute메소드는 redirect되는 곳에 model attribute를 만들어서 보내는 것이다.
RedirectAttributes객체의 addAttribute메소드는 쿼리스트링으로 붙어서 간다.
@GetMapping("link14")
public String method14(RedirectAttributes rttr) {
// 목적지의 Model Attribute로 넣기
//rttr.addFlashAttribute(null, rttr);
//쿼리 스트링으로 붙어서
rttr.addAttribute("address", "seoul");
return "redirect:/sub25/link15";
// /sub25/link15?address=seoul}
@RequestParam을 활용해서 값을 꺼내서 사용할 수 있다.
@GetMapping("link15")
public String method15(RedirectAttributes rttr) {
rttr.addAttribute("email", "123456@email.com");
rttr.addAttribute("location", "seoul");
return "redirect:/sub25/link16";
}@GetMapping("link16")
public void method16(String email, String location) {
System.out.println("email: " + email);
System.out.println("location: " + location);
}결론적으로 RedirectAttributes객체를 잘 활용하면
model에 붙여서 값을 보내거나 requestparam으로 값을 보낼 수 있다.
이 값을 받아와서 잘 활용하면된다.
47 @Autowired
Spring에 관련된 이야기이다.
스프링 프로젝트를 실행하면 메인메소드가 하나 있다.
프로젝트명Application
메인메소드에서
SpringApplication의 run메소드가 실행된다.
이게 실행되면 스프링과 관련된 객체들이 생성된다.
이것들을 Spring Bean이라하고 스프링이 관리하는 객체들이다.
어떤게 스프링이 관리하는 객체들이나면
우리가 제공하거나 스프링이 필요해서 만들어 낼 수 있다.
우리가 객체를 직접 만든다면 객체를 만들고
필요한 값을 setter로 프로퍼티에 직접 할당했다.
그런데 스프링은 어떤 객체가 필요하다고 설정파일을 제공하면된다.
예전에는 XML로 주었는데 현재는 자바파일로 제공한다.
설정파일이 어딘가 있어야한다.
지금의 설정파일은 프로젝트명Application의 하위패키지나 같은패키지에 두면된다
보통 config 패키지를 만들어서 설정을 둔다.
설정파일이라는 것을 알려주기 위해 @Configuration어노테이션을 사용한다.
여기에 스프링이 관리하는 객체를 리턴하는 것을 만들어줘야한다.
스프링이 이 메소드를 사용해서 @Bean어노테이션이 붙은 객체를 만든다.
이것을 스프링 빈 (스프링이 관리하는 객체)이고 빈 이름 = 메소드 이름과 같다.
이 객체들을 ApplcationContext객체에 담아 둔다.
ApplcationContext객체에 우리가 설정해 놓았던 객체가 들어있다.
ApplcationContext객체의 getBean메소드를 사용하면 객체를 만들어내는데 이때 객체는 하나만 만들어준다
@Configuration
public class MyConfig1 {
@Bean
public Object bean1() {
return new Object();
}
@Bean
public Employee employee() {
return new Employee();
}
}@SpringBootApplication
public class Boot2023Application {
public static void main(String[] args) {
//Spring Bean : 스프링이 관리하는 객체들
ApplicationContext context = SpringApplication.run(Boot2023Application.class, args);
Object o = context.getBean("bean1");
System.out.println(o);
Object o2 = context.getBean("employee");
System.out.println(o2);
}
}객체를 주입하는 방법은 set메소드로 하거나 생성자로 주입할 수 있다.
얘를 스프링이 관리하는 Bean을 만드는데 사용하는 클래스라면 설정파일에 추가해줘야한다.
ComponentA는 ComponentB에 의존하고 있다.
스프링이 만든 bean객체를 심어줘야한다.
스프링이 만든 객체는 bean2 bean3모두 있는 것이다.
main에서 bean2만들때 bean3객체를 만들어서 넣엇다.
그러면 main에서만든 bean3객체와 미리 만들어놓은 bean3객체가 같냐? 같다.
결론적으로는 여러번 사용한다해도 스프링 bean객체는 하나만 만들어진다.
@Bean
public ComponentA bean2() {
ComponentA o = new ComponentA(bean3());
return o;
}
@Bean
public ComponentB bean3() {
return new ComponentB();
}
@Bean
public ComponentC bean4() {
ComponentC o = new ComponentC();
o.setComp(bean3());
return o;
}public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Boot2023Application.class, args);
Object o4 = context.getBean("bean2");
Object o5 = context.getBean("bean3");
System.out.println(o4); //ComponentA@3f4dd429
System.out.println(o5); //ComponentB@2ea1de51
ComponentA c1 = (ComponentA) o4;
System.out.println(c1.getComp()); // ComponentB@2ea1de51
ComponentC c3 = (ComponentC) context.getBean("bean4");
System.out.println(c3.getComp()); // ComponentB@2ea1de51
}결론적으로 스프링 Bean객체를 만들어내서 다른 객체의 필드로 사용할 수 있다.
그런데 만들어 내는 것은 스프링에서 미리 만들어서 컨테이너에 담게 된다.
그래서 여러번 사용하더라도 같은 객체를 참조하게 된다.
48 @Autowired - 2
설정파일에 @Bean으로 객체를 만들어두면 스프링이 설정파일을 보고 객체를 만든다.
그런데 이것조차 하기가 불편하다.
클래스를 만들고 @Component어노테이션을 만들면
스프링이 이 어노테이션이 붙은 클래스를 보고 Spring Bean으로 만들어준다.
Spring Bean의 이름은 클래스 이름의 lowerCamelCase로 지어진다.
@Configuration 파일에 메소드를 만든 상태를 주게 된다.
@Component
public class ComponentD {
}ComponentD c4 = (ComponentD) context.getBean("componentD");
System.out.println(c4); //.ComponentD@3dc98b0e그런데 만약 다른 클래스를 필드로 필요로 한다면 @Autowired어노테이션으로
Spring bean으로 만들어둔 것을 자동으로 할당해준다.
자동으로 Injection해준다. DI해준다.
그러면 이전에 만들어놓은 객체와 같은 객체가 된다.
@Component
public class ComponentE {
@Autowired
private ComponentD comp;
public ComponentD getComp() {
return comp;
}
}ComponentE c5 = (ComponentE) context.getBean("componentE");
System.out.println(c5.getComp()); // .ComponentD@3dc98b0e우리가 만든 컨트롤러에 @Controller라고 붙엿엇다.
@Component를 붙이고 @Autowired로 mapper를 주입한 것이다.
Mapper는 마이바티스가 bean을 만들어 둔 것이고
이것을 사용한 것이다.
결론은 우리는 @Autowired어노테이션을 활용해서 객체를 DI하고
business logic만 잘 작성하면 된다는 것이다.
@Controller
@RequestMapping("sub23")
public class Controller23 {
@Autowired
private Mapper04 mapper;
}간단한 프로젝트만들기
간단한 프로젝트 만들기
회원제 게시판 만들기
쿼리는 다배웟다? 마이바티스에서 복잡한 쿼리 작성은 나중에...
게시물작성 댓글 사진 회원가입 로그인 검색
좋아요
지금배운것만으론 하기 힘듦
게시물 테이블, 댓글 테이블, 파일테이블, 회원테이블, 좋아요테이블 등등
정규화
일단은 기본적인 게시글 테이블만 만들어볼예정이다.
5월말정도까지 간단한 CRUD게시판
6월에 팀프로젝트
7월에는 팀프로젝트 - 기업과제
다 배운게 아니라서 프로젝트 만들고 배우면서 왓다갓다하면서 만들 것이다.
mybaits, mariadb, spring web, lombok, devtools를 기본적으로 사용하고
나중에 스프링 시큐리티 추가예정이다.
필요한 테이블을 일단 만들것이다.
게시물을 만드는 Board DB를 만들것이다.
테이블명 : UpperCamelCase
컬럼명 : lowerCamelCase
Board 테이블 title(제목), body(본문), inserted(작성일), writer(작성자)
기본키로 id를 새로 넣어준다.
CREATE TABLE Board (
id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(50) NOT NULL,
body VARCHAR(1000) NOT NULL,
writer VARCHAR(20),
inserted DATETIME DEFAULT NOW()
);프로젝트 datasource를 위해 application.properties에 작성하는데
연결정보를 숨기기위해 외부설정파일 custom.properties에 작성하고 연결해준다.
spring.config.import=custom.properties
또한 보안을위해 gitignore에 추가해서 커밋안되게 하자.
src/main/resources/custom.properties
2023.04.25
갑자기 프로젝트를 시작하게 되엇다. 간단한 CRUD게시판이다.
오늘은 세팅만했다 드디어 프로젝트를 들어간다니 두렵고 한편으로는 설렌다.
도움이 되서 팀에서도 민폐를 끼치지 않고 진행하고 싶다.
'국비 > SpringBoot' 카테고리의 다른 글
| 2023.04.24 62일차 SpringBoot (0) | 2023.04.24 |
|---|---|
| 2023.04.21 61일차 SpringBoot (0) | 2023.04.24 |
| 2023.04.20 60일차 SpringBoot (0) | 2023.04.20 |
| 2023.04.19 59일차 SpringBoot (1) | 2023.04.19 |
| 2023.04.18 58일차 SpringBoot (0) | 2023.04.18 |
