wintertreey 님의 블로그
@MVC DB연동3 MyBatis. @Mapper. th:obejct * th:field. 본문
mybatis의장점
클래스와 sql문의 분리. 유지보수가 편하다.
Datamapininter.java
데이터연동을위한 어노테이션걸어준다.
package pack.model;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import pack.controller.FormBean;
@Mapper
public interface DataMappingInter {
@Select("select * from sangdata")
List<SangpumDto> selectAll();
@Select("select code, sang, su, dan from sangdata where sang like concat('%', #{searchValue}, '%')")//mariadb sql
// oracle : like '%'||#{searchValue}||'%'
List<SangpumDto> selectSearch(FormBean bean);
}
@Mapper
의미를 보면 @Mapper는 마커 인터페이스라는 것이다.
기능이 있는게 아니라 마커, 즉 무언가 표시를 하기 위한 인터페이스를 의미한다.
즉, @Mapper는 단순히 '이것은 매퍼입니다!!'라는 것을 표시하기 위한 어노테이션이라는 것이다.
중요한 것은 @Mapper와 같이 쓰이는 @MapperScan이지 굳이 @Mapper를 쓰지 않고 커스텀 어노테이션을 생성해서 사용해도 된다.
파일의 흐름
index ---> ListController -----> list
DataDao
DataMappingInter
@Slf4j
package pack.model;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import lombok.extern.slf4j.Slf4j;
import pack.controller.FormBean;
@Repository
@Slf4j //로그출력: 롬복이 지원해줌
public class DataDao {
private Logger logger= LoggerFactory.getLogger(this.getClass());
@Autowired
private DataMappingInter inter;
public List<SangpumDto> getDataAll(){
List<SangpumDto> list = inter.selectAll();
System.out.println("list.size:"+ list.size());
logger.info("list.size:"+ list.size());
return list;
}
System.out.println("list.size:"+ list.size());
logger.info("list.size:"+ list.size());
이 둘은 같은 의미의 문장이다. 개발자가 출력해서 확인해보고자 하는 용도..
+ 히카리 풀링이 콘솔창에 찍힌것을 확인할 수 있다.
작업 흐름
HTML > MemController > DataProcess > DataMapperInterface > HTML
@Controller @Repository @Mapper
MemDto, MemBean : 저장소
시작페이지
이번엔 시작페이지도 templates안에 위치하게 두었다.
@Controller
public class MemController {
@Autowired
private DataProcess dataProcess;
@GetMapping("/")
public String chulbal() {
return "chulbal";
}
요청명이 포트번호까지만 들어왔을때 출발페이지보여줌. 즉 시작페이지를 chulbal 로 해두는것.
<body>
메인페이지
<br>
<a href="/list">회원관리(@MVC - MyBatis : CRUD)</a>
</body>
url에 localhost를 치면 메인페이지가 뜬다.
전체자료읽기
@GetMapping("list")
public String list(Model model) {
ArrayList<MemDto> list = (ArrayList<MemDto>)dataProcess.getDataAll();
model.addAttribute("datas", list);
return "list";
}
리스트를 보여달라는 요청에 따라 전체 데이터를 보여주자.
@Repository
public class DataProcess {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private DataMapperInterface dataMapperInterface; //hikari pool이 자동지원
//전체자료읽기
public List<MemDto> getDataAll(){
List<MemDto> list = dataMapperInterface.selectAll();
logger.info("전체자료 크기: "+ list.size());//syso대신
return list;
}
private Logger logger = LoggerFactory.getLogger(this.getClass());
logger.info("전체자료 크기: "+ list.size());
Logger 객체를 생성해주었다. 이 객체는 Java의 로깅 프레임워크인 SLF4J(Simple Logging Facade for Java)를 사용하여 애플리케이션의 로깅을 관리해준다.
System.out.println();대신 logger.info 로 콘솔에 출력하고자 코드를 추가했다.
@Mapper
public interface DataMapperInterface {
@Select("select * from mem")
List<MemDto> selectAll();
Mybatis는 복수일경우 반드시 list로 반환해준다. not Arraylist.
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>templates/list.html</title>
</head>
<body>
<h2>🍇회원목록(@MVC MyBatis)🍇</h2>
<a th:href="@{/insert}">추가</a>
<table border="1">
<tr>
<th>번호</th><th>이름</th><th>주소</th><th>수정/삭제</th>
</tr>
<th:block th:if="${datas.size > 0}">
<tr th:each="data:${datas}"> <!-- datas가 가진값을 data로 하나씩 뽑아낸다 -->
<td>[[${data.num}]]</td>
<td>[[${data.name}]]</td>
<td>[[${data.addr}]]</td>
<td>
<a th:href="@{/update(num=${data.num})}">수정/</a>
<a th:href="@{/delete(num=${data.num})}">삭제</a>
</td>
</tr>
</th:block>
</table>
<br>
<a th:href="@{/}">메인으로</a>
</body>
</html>
추가하기
list.html에서 추가하기 링크로 추가해보자.
@GetMapping("insert")
public String insert() {
return "insert";
}
<title>templates/insert.html</title>
</head>
<body>
<h3>🥦자료 입력🥦</h3>
<form th:action="@{/insert}" method="post">
번호 : <input type="text" name="num"><br>
이름 : <input type="text" name="name"><br>
주소 : <input type="text" name="addr"><br>
<br>
<input type="submit" value="등록">
</form>
@PostMapping("insert")
public String insertProcess(MemBean bean) {
boolean b = dataProcess.insert(bean);
if(b)
return "redirect:/list";
else
return "redirect:/error";
}
@GetMapping("error")
public String error() {
return "error";
}
//추가하기 추가시 0아니면1
public boolean insert(MemBean bean) {
//번호중복방지, 번호자동증가 는 추가작업.
int re = dataMapperInterface.insertData(bean);
if(re >0)//즉 =1이면 성공. 아니면 false
return true;
else
return false;
}
@Insert("insert into mem values(#{num}, #{name}, #{addr})")
int insertData(MemBean bean);
<title>templates/error.html</title>
</head>
<body>
에러발생!! <hr>
<a th:href="@{/list}">회원목록 보기</a>
</body>
수정하기
@GetMapping("update")
public String update(@RequestParam("num") String num, Model model) {
MemDto dto = dataProcess.getData(num);
model.addAttribute("data", dto); //=request.setAttribute
return "update";
}
수정할 부분자료를 읽어야한다.
//부분자료읽기
public MemDto getData(String num) {
MemDto dto = dataMapperInterface.selectPart(num);
return dto;
}
@Select("select * from mem where num=#{num}")
MemDto selectPart(String num);
<h3>🥦자료 수정🥦</h3>
<form th:action="@{/update}" method="post" th:object="${data}">
<!-- th:object로 값 받아서, ${data.num}로 뿌린다. 넘길땐 field * 로 넘긴다. -->
번호 : <span th:text="${data.num}">번호</span><br>
<input type="hidden" th:field="*{num}">
<!-- 번호는 수정에서 제외. 보여만준다. 가지고 가야하기에, hidden으로 번호를 넘긴다. -->
이름 : <input type="text" th:field="*{name}"><br>
주소 : <input type="text" th:field="*{addr}"><br>
<br>
<input type="submit" value="수정">
</form>
th: object, *, th:field
입력 폼 처리
th:object, *{...}, th:field 3개를 함께 사용하면 <form>을 편리하게 작성할 수 있다.
- th:object: 커맨드 객체를 지정한다.
- 단, 해당 오브젝트 정보를 model에 담아서 넘겨주어야 한다.
- 등록 폼이기 때문에 데이터가 비어있는 빈 오브젝트를 만들어서 뷰에 전달한다.
- *{...}: th:object에서 선택한 객체에 접근한다.
- ${객체.필드}와 같다.
- th:object=${객체명}+*{필드명} 을 사용하던지, ${객체.필드}를 사용하던지 선택하면 된다.
- 하위 태그에 객체 접근 할 일이 많다면 전자가 편리할것.
- th:field: HTML 태그의 id, name, value 속성을 자동으로 만들어준다.
- input에 적용하는 필드이다.
- id와 name은 th:field에서 지정한 변수 이름과 같게 만들어진다.
- <label>등과 함께 사용시 id가 존재하지 않으면 ide측에서 오류로 간주한다. (타임리프는 렌더링시 만들어주기 때문). 따라서 필요할 시에는 id를 직접 적어야한다.
- value는 th:field에서 지정한 변수의 값(model에 담긴 값)을 사용한다.
이러면 페이지소스코드에서 볼때 자동으로 생성된다.
읽어온 자료를 수정해서 새로운 정보를 입력하자.
@PostMapping("update")
public String updateProcess(MemBean bean) {
boolean b = dataProcess.update(bean);
if(b)
return "redirect:/list";
else
return "redirect:/error";
}
@Update("update mem set name=#{name}, addr=#{addr} where num=#{num}")
int updateData(MemBean bean);
이름을 초록색 > 마이클로 변경했다.
삭제하기
@GetMapping("delete")
public String delete(@RequestParam("num") String num) {
boolean b = dataProcess.delete(num);
if(b)
return "redirect:/list";
else
return "redirect:/error";
}
//삭제하기
public boolean delete(String num) {
//번호중복방지, 번호자동증가 는 추가작업.
int re = dataMapperInterface.deleteData(num);
if(re >0)//즉 =1이면 성공. 아니면 false
return true;
else
return false;
}
@Delete("delete from mem where num=#{num}")
int deleteData(String num);
마이클 삭제 완료.
전체 소스코드는 깃허브의 sprweb11,12를 참고하자.
https://github.com/yoonah0327/spring_source.git
GitHub - yoonah0327/spring_source
Contribute to yoonah0327/spring_source development by creating an account on GitHub.
github.com
https://hanke-r.tistory.com/70
Spring boot - application.properties MySQL DB 설정
pom.xml mysql mysql-connector-java # MySQL8 설정 spring.datasource.url=jdbc:mysql://localhost:3306/스키마명?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC spring.datasource.username=아이디 spring.datasource.password=비밀번호 spring.da
hanke-r.tistory.com
타임리프(Thymeleaf) - 기본기능6
th:object, \*{...}, th:field 3개를 함께 사용하면 <form>을 편리하게 작성할 수 있다.th:object: 커맨드 객체를 지정한다.단, 해당 오브젝트 정보를 model에 담아서 넘겨주어야 한다.등록 폼이기 때문에 데이
velog.io