국비/SpringBoot

2023.04.20 60일차 SpringBoot

춘핑이 2023. 4. 20. 17:48

60일차

DB에 테이블을 만들때 타입, 제약사항등을 추가할 수있엇다.

28 데이터타입 JDBC

DB의 자료형은 엄격하진 않다.
INTEGER형식이면 알아서 변환되서 들어가고
String에 숫자를 넣으면 알아서 문자열로 변경되어 들어가게 된다.

28.1 INT, VARCHAR

CREATE TABLE MyTable30(
    Col1 INT,
    Col2 VARCHAR(30)
);

INSERT INTO MyTable30 (Col1, Col2) VALUES(30, 'java'); -- ok
INSERT INTO MyTable30 (Col1) VALUES('33'); -- ok
INSERT INTO MyTable30 (Col1) VALUES('Three'); -- fail
INSERT INTO MyTable30 (Col2) VALUES(77); -- ok string으로 변경후 들어감

JDBC에서 PreparedStatement에 매핑을 할때 setter가 완전히 일치할 필요는 없다.
하지만 맞춰서 넣어주는게 좋으니 확인하고 넣어주자.

@RequestMapping("link2")
public void method2() throws Exception {
    String sql = "INSERT INTO MyTable30 (Col1, Col2) VALUES (?,?)";

    try (Connection con = DriverManager.getConnection(url, dbId, pwd);
            PreparedStatement pstmt = con.prepareStatement(sql);) {

        //pstmt.setInt(1, 99);
        //pstmt.setString(2, "hello");
        pstmt.setString(1, "888");
        pstmt.setInt(2, 12345);
        int cnt = pstmt.executeUpdate();
        System.out.println(cnt + "개 행이 입력됨");
    }
}

28.2 DATE

DATE YYYY-MM-DD
DATETIME YYYY-MM-DD HH:mm:SS
JDBC에서 DATE값을 세팅할때 setString으로 해도된다.
DATE는 setDate는 Date.valueOf()메소드로 포장한값을 넣어줄수 있고
DATETIME은 setTimestamp로 Timestamp.valueOf()포장한값을 넣어줄수 있다.

@RequestMapping("link3")
public void method3() throws Exception {
    String sql = "INSERT INTO MyTable31 (Col1, Col2) VALUES (?,?)";

    try (Connection con = DriverManager.getConnection(url, dbId, pwd);
            PreparedStatement pstmt = con.prepareStatement(sql);) {
        //pstmt.setString(1, "1923-03-01");
        //pstmt.setString(2, "1811-11-12 23:50:49");
        pstmt.setDate(1, Date.valueOf("2023-03-01"));
        pstmt.setTimestamp(2, Timestamp.valueOf("2000-01-01 22:22:11"));
        int cnt = pstmt.executeUpdate();
        System.out.println(cnt + "개 행이 입력됨");
    }
}

28.3 연습

경로 /sub19/link5 MyTable32에 새 레코드 추가하는 메소드 작성하기

@RequestMapping("link5")
public void method4() throws Exception {
    String sql = "INSERT INTO MyTable32 VALUES (?,?,?,?,?)";

    try (Connection con = DriverManager.getConnection(url, dbId, pwd);
            PreparedStatement pstmt = con.prepareStatement(sql);) {
        pstmt.setString(1, "park");
        pstmt.setInt(2, 26);
        pstmt.setDouble(3, 123.45);
        pstmt.setDate(4, Date.valueOf("1998-01-05"));
        pstmt.setTimestamp(5, Timestamp.valueOf("2023-04-20 09:42:59"));

        int cnt = pstmt.executeUpdate();
        System.out.println(cnt + "개 행이 입력됨");
    }
}

28.4 form + JDBC

사용자로부터 폼을 사용해서 RequestParam을 받아서 JDBC값을 넣어보자
input 박스를 date로 만들면 연월일 datetime-local로 하면 연월일시분초까지 가능하다
파라미터는 결국 String으로 받아오니 requestparam은 String으로 받아오면된다.

@RequestMapping("link7")
public void method7(String name, String age, String price, String birth, String inserted) throws Exception {
    String sql = "INSERT INTO MyTable32 VALUES (?,?,?,?,?)";

    try (Connection con = DriverManager.getConnection(url, dbId, pwd);
            PreparedStatement pstmt = con.prepareStatement(sql);) {
        pstmt.setString(1, name);
        pstmt.setString(2, age);
        pstmt.setString(3, price);
        pstmt.setString(4, birth);
        pstmt.setString(5, inserted);

        int cnt = pstmt.executeUpdate();
        System.out.println(cnt + "개 행이 입력됨");
    }
}

물론 그냥 String으로 넣어도되지만 타입을 맞춰서 넣어주는게 보기 좋으니 타입을 맞춰서 넣어주자.
Date는 LocalDate datetime-locald은 LocalDateTime으로 받아주자.
setter는 setDate setTimestamp으로 받기

@RequestMapping("link8")
public void method8(String name, int age, double price, LocalDate birth, LocalDateTime inserted) throws Exception {
    String sql = "INSERT INTO MyTable32 VALUES (?,?,?,?,?)";

    try (Connection con = DriverManager.getConnection(url, dbId, pwd);
            PreparedStatement pstmt = con.prepareStatement(sql);) {
        pstmt.setString(1, name);
        pstmt.setInt(2, age);
        pstmt.setDouble(3, price);
        pstmt.setDate(4, Date.valueOf(birth));
        pstmt.setTimestamp(5, Timestamp.valueOf(inserted));

        int cnt = pstmt.executeUpdate();
        System.out.println(cnt + "개 행이 입력됨");
    }
}

28.5 form + JDBC - 2

다음 테이블에 값을 넣는 form과 JDBC를 만들어보자
method9 : form이 있는 view로 포워드
method10 : 전송된 데이터를 MyTable33에 추가
연습을 위해 DTO를 만들어서 처리했다.

CREATE TABLE MyTable33(
    Title VARCHAR(30),
    Published DATE,
    Price INT,
    Updated DATETIME,
    Weight DEC(10,3)
);

<h1>MyTable33</h1>
<form action="/sub19/link10" method="post">
    제목 : <input type="text" name="title"/> <br />
    출판 : <input type="date" name="published"/> <br />
    가격 : <input type="number" name="price" /> <br />
    수정 : <input type="datetime-local" name="updated"/> <br />
    무게 : <input type="text" name="weight"/> <br />
    <input type="submit" value="전송" />
</form>

@Data
public class MyTable33 {
    private String title;
    private LocalDate published;
    private int price;
    private LocalDateTime updated;
    private double weight;
}

@RequestMapping("link10")
public void method10(MyTable33 myTable33) throws Exception {
    String sql = "INSERT INTO MyTable33 VALUES (?,?,?,?,?)";

    try (Connection con = DriverManager.getConnection(url, dbId, pwd);
            PreparedStatement pstmt = con.prepareStatement(sql);) {
        pstmt.setString(1, myTable33.getTitle());
        pstmt.setDate(2, Date.valueOf(myTable33.getPublished()));
        pstmt.setInt(3, myTable33.getPrice());
        pstmt.setTimestamp(4, Timestamp.valueOf(myTable33.getUpdated()));
        pstmt.setDouble(5, myTable33.getWeight());
        int cnt = pstmt.executeUpdate();
        System.out.println(cnt + "개 행이 입력됨");
    }
}

28.6 데이터 읽기

모두 String타입으로 읽어오거나 각 타입에 맞게 읽어올 수 있다.

@RequestMapping("link11")
public void method11() throws Exception {
    String sql = "SELECT * FROM MyTable33";
    try (Connection con = DriverManager.getConnection(url, dbId, pwd);
            PreparedStatement pstmt = con.prepareStatement(sql);
            ResultSet rs = pstmt.executeQuery();) {
        if(rs.next()) {
            String title = rs.getString("title");
            String published = rs.getString("published");
            String price = rs.getString("price");
            String updated = rs.getString("updated");
            String weight = rs.getString("weight");

            System.out.println("제목:" + title);
            System.out.println("출판일:" + published);
            System.out.println("가격:" + price);
            System.out.println("수정일:" + updated);
            System.out.println("무게:" + weight);
        }
    }
}

@RequestMapping("link12")
public void method12() throws Exception {
    String sql = "SELECT * FROM MyTable33";
    try (Connection con = DriverManager.getConnection(url, dbId, pwd);
            PreparedStatement pstmt = con.prepareStatement(sql);
            ResultSet rs = pstmt.executeQuery();) {
        if (rs.next()) {
            String title = rs.getString("title");
            LocalDate published = rs.getDate("published").toLocalDate();
            int price = rs.getInt("price");
            LocalDateTime updated = rs.getTimestamp("updated").toLocalDateTime();
            double weight = rs.getDouble("weight");

            System.out.println("제목:" + title);
            System.out.println("출판일:" + published);
            System.out.println("가격:" + price);
            System.out.println("수정일:" + updated);
            System.out.println("무게:" + weight);
        }
    }
}

28.7 읽기 연습 - 1

MyTable32의 데이터 조회후 출력하는 14번 메소드 작성해보기

@Data
public class Dto05 {
    private String name;
    private int age;
    private double price;
    private LocalDate birth;
    private LocalDateTime inserted;
}

@RequestMapping("link14")
public void method14(Model model) throws Exception {
    // 1.request param수집가공

    // 2.bussiness logic(crud)
    String sql = "SELECT * FROM MyTable32";
    List<Dto05> list = new ArrayList<>();
    try (Connection con = DriverManager.getConnection(url, dbId, pwd);
            PreparedStatement pstmt = con.prepareStatement(sql);
            ResultSet rs = pstmt.executeQuery();) {
        while (rs.next()) {
            Dto05 o = new Dto05();
            String name = rs.getString("name");
            int age = rs.getInt("age");
            double price = rs.getDouble("price");
            LocalDate birth = rs.getDate("birth").toLocalDate();
            LocalDateTime inserted = rs.getTimestamp("inserted").toLocalDateTime();

            o.setName(name);
            o.setAge(age);
            o.setPrice(price);
            o.setBirth(birth);
            o.setInserted(inserted);
            list.add(o);
        }
    }
    // 3.add attribute
    model.addAttribute("memberList", list);
    // 4.forward/redirect
}

28.8 읽기 - 2

/sub19/link15 MyTable33에 있는 데이터들 jsp에서 보여주기

@Data
public class Dto06 {
    private String title;
    private LocalDate published;
    private int price;
    private LocalDateTime updated;
    private double weight;
}

// /sub19/link15 MyTable33에 있는 데이터들 jsp에서 보여주기
@RequestMapping("link15")
public void method15(Model model) throws Exception {
    String sql = "SELECT * FROM MyTable33";
    List<Dto06> list = new ArrayList<>();
    try (Connection con = DriverManager.getConnection(url, dbId, pwd);
            PreparedStatement pstmt = con.prepareStatement(sql);
            ResultSet rs = pstmt.executeQuery();) {
        while (rs.next()) {
            Dto06 dto06 = new Dto06();
            dto06.setTitle(rs.getString("title"));
            dto06.setPublished(rs.getDate("published").toLocalDate());
            dto06.setPrice(rs.getInt("price"));
            dto06.setUpdated(rs.getTimestamp("updated").toLocalDateTime());
            dto06.setWeight(rs.getDouble("weight"));
            list.add(dto06);
        }
    }
    model.addAttribute("bookList", list);
}

<div align="center">
    <h1>목록</h1>
    <table border="1">
        <thead>
            <tr align="center">
                <th width="50">제목</th>
                <th width="100">출판일</th>
                <th width="100">가격</th>
                <th width="200">수정일</th>
                <th width="50">무게</th>
            </tr>
        </thead>
        <tbody>
            <c:forEach items="${bookList}" var="book">
                <tr align="center">
                    <td>${book.title }</td>
                    <td>${book.published }</td>
                    <td>${book.price }</td>
                    <td>${book.updated }</td>
                    <td>${book.weight}</td>
                </tr>
            </c:forEach>
        </tbody>
    </table>
</div>

29 마이바티스

실행은 그냥 하고싶은데 각타입이 뭔지 확인하고 빈에 넣어주고 등등
이런 코드들이 너무 지루하다.
마이바티스는 쿼리만 작성하면된다.

마이바티스 라이브러리를 추가해줘야한다.
스프링 스타터팩에 있으니 그것을 이용하자.

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>

마이바티스를 안쓰면 JPA, JDBC API, Spring에서 제공하는 JDBC등등을 사용한다.
spring.datasource.url에러 낫다는거
-> 스프링.properties와 custom에 가서 엔터만 치고 저장하기

마이바티스는 인터페이스를 만들어 놓으면 쿼리를 작성하면
쿼리기준으로 Connection, statementent, ResultSet, dto에 담는 것을 알아서 담아준다.

마이바티스에서 보통 매퍼라는 이름을 쓰고있어서 아무 이름도 괜찮지만
com.example.demo.mapper패키지를 생성하자.

학습중이니 단순한 이름으로 Mapper01인터페이스를 생성한다.
스프링부트가 마이바티스가 이것을 잘 찾아서 사용할 수 있도록
@Mapper어노테이션을 사용해서 마킹을 해야한다.

추상메소드를 만드는데 각 SQL과 관련한 @Select어노테이션을 붙이고 SQL문을 작성해주면된다.
이 쿼리문을 담고 알아서 잘 담아서 리턴해준다.
return타입을 가져오는 데이터 타입에 맞춰서 작성해줘야한다.

@Mapper
public interface Mapper01 {
    @Select("SELECT CustomerName FROM Customers WHERE CustomerID = 1")
    String method1();
}

컨트롤러에서는 이 인터페이스의 메소드를 사용하면된다.
컨트롤러는 굉장히 쉬워진다.
그런데 우리는 메소드를 사용해야해서 인터페이스를 구현해야한다.
그런데 이 구현 클래스를 마이바티스가 알아서 만들어준다.

Mapper01타입을 마이바티스가 알아서 구현해서 IoC에 넣어두었다.
이것을 꺼내서 사용하려면 DI를 해줘야하는데 이 DI는 @Autowired어노테이션을 사용해서 스프링이 알아서 해준다.
Mapper01를 구현한 객체이니 mapper의 메소드를 사용하는데 구현되어있다는게 보장되어 있으니 상관이없다.

@Controller
@RequestMapping("sub20")
public class Controller20 {

    @Autowired
    private Mapper01 mapper;

    @RequestMapping("link1")
    public void method1() {
        String name = mapper.method1();
        System.out.println(name);
    }
}

29.1 연습 - 1

Mapper01에 method3작성 1번 공급자의 공급자명 출력하기

@Mapper
public interface Mapper01 {
    @Select("SELECT SupplierName From Suppliers WHERE SupplierID = 1")
    String method3();
}

@Controller
@RequestMapping("sub20")
public class Controller20 {

    @Autowired
    private Mapper01 mapper;

    @RequestMapping("link2")
    public void method2(){
        System.out.println(mapper.method3());
    }
}

29.2 특정인원을 출력하기

?가 들어간 쿼리문 실행하는 방법을 알아보자.
쿼리에 들어간 값을 메소드가 받아야하니 메소드의 아규먼트로 넣어주면된다.
?대신 파라미터가들어가도록 #{}안에 파라미터의 이름과 같게 넣어줘야한다.
마이바티스가 쿼리문을 알아서 짜주게 되는 것이다.
컨트롤러에서는 파라미터 값을 주면서 메소드를 실행시키면된다.

@Select("SELECT CustomerName From Customers WHERE CustomerID = #{id}")
String method4(int id);

@RequestMapping("link3")
public void method3() {
    System.out.println(mapper.method4(3));
}

29.3 파라미터로 받아서 출력하기

파라미터로 받은 것을 넣어주면된다.

// /sub20/link4?ud=70
@RequestMapping("link4")
public void method4(@RequestParam("id") int customerId) {
    System.out.println(mapper.method4(customerId));
}

연습
/sub20/link5?id=2로 요청햇을때 2번직원의 lastName을 콘솔에 출력

@Select("SELECT lastName From Employees WHERE EmployeeID = #{employeeId}")
String method5(int employeeId);

// /sub20/link5?id=2
@RequestMapping("link5")
public void method5(@RequestParam("id") int employeeId) {
    String lastName = mapper.method5(employeeId);
    System.out.println(lastName);
}

29.4 다른타입 반환

String타입이 아니라 여러 데이터 타입을 받아보도록 하자.
각각의 데이터타입에 맞게 set햇던 것처럼 각각의 데이터타입에 맞는 리턴타입을 지정하면된다.

@Select("SELECT Price From MyTable33 LIMIT 1")
int method6();

@Select("SELECT Weight From MyTable33 LIMIT 1")
Double method7();

@Select("SELECT Published From MyTable33 LIMIT 1")
LocalDate method8();

@Select("SELECT updated From MyTable33 LIMIT 1")
LocalDateTime method9();

@RequestMapping("link6")
public void method6() {
    System.out.println(mapper.method6());
    // mapper01 에 method7작성 하나의 row의 weight컬럼의 값리턴

    Double weight = mapper.method7();
    System.out.println(weight);

    LocalDate published = mapper.method8();
    System.out.println(published);

    LocalDateTime updated = mapper.method9();
    System.out.println(updated);
}

29.5 javaBean

여러 컬럼을 추출해서 가져올 경우 여러 타입이 혼재해서 테이블로 가져오게된다.
여러개의 데이터를 담기 위해서는 javaBean을 사용해야한다.
컬럼명(대소문자 구분없이)과 매치되는 빈의 프로퍼티명이 있어야한다.
이 프로퍼티를 가진 dto가 필요하다.

마이바티스가 객체를 만들고 컬럼명과 매치되는 프로퍼티를 알아서 매핑해준다.

@Select("SELECT * From MyTable33 LIMIT 1")
Dto06 method10();

@RequestMapping("link7")
public void method7() {
    Dto06 res = mapper.method10();
    System.out.println(res);
}

29.6 javaBean - 2

32번테이블의 javabean을 사용해서 값을 가져와보자

@Select("SELECT * FROM MyTable32 LIMIT 1")
Dto05 method11();

@RequestMapping("link8")
public void method8() {
    Dto05 res = mapper.method11();
    System.out.println(res);
}

순서도 상관없다.
작성된 쿼리가 Dto에 없다면 넣지 않는다.
만약 한개를 조회하지 않았다면 dto의 프로퍼티가 null값이 될뿐이다.
만약 매핑할 프로퍼티와 테이블의 컬럼명을 다르게 했다면 두가지 선택이 있다.
1.dto클래스 수정하기
2.sql에 별칭을 붙여서 프로퍼티와 맞추기

@Select("SELECT CustomerID AS id, "
        + "CustomerName AS name, "
        + "contactName, "
        + "address, "
        + "city, "
        + "postalCode, "
        + "Country "
        + "From Customers WHERE customerId = 7")
Customer method12();

29.7 javaBean - 3

연습문제 별칭을 붙여 프로퍼티명과 SELECT결과의 컬럼명을 일치시키고 출력하기
-> 그런데 나는 애초에 Dto를 컬럼명과 같게 설정해두어서 그럴 필요가 없다. 알아만 두자

@Select("SELECT supplierId , "
        + "supplierName, "
        + "contactName, "
        + "address, "
        + "city, "
        + "postalCode, "
        + "phone "
        + "FROM Suppliers WHERE supplierId = 7")
Supplier method13();

@RequestMapping("link10")
public void method10() {
    Supplier res = mapper.method13();
    System.out.println(res);
}

29.8 javaBean - 4

Employee Dto를 완성하고 출력하기

@Data
public class Employee {
    private int employeeId;
    private String lastName;
    private String firstName;
    private LocalDate birth;
    private String Photo;
    private String Notes;
}

@Select("SELECT employeeId, LastName, FirstName, Birthdate birth, Photo, Notes "
        + "FROM Employees WHERE employeeId = 5")
Employee method14();

@RequestMapping("link11")
public void method11() {
    Employee res = mapper.method14();
    System.out.println(res);
}

2023.04.20

input 박스를 date로 만들면 연월일 datetime-local로 하면 연월일시분초까지 가능하다
스프링에서 받을때는 Date는 LocalDate datetime-locald은 LocalDateTime으로 받아주자.
set할땐 valueof로 변환

개뜬금없이 마이바티스 배운다. 사용법 자체는 쉬워보인다.

출력할때 sql타입으로 가져온후 toLocaldate메소드사용하자.
LocalDate published = rs.getDate("published").toLocalDate();
LocalDateTime updated = rs.getTimestamp("updated").toLocalDateTime();