67일차 2023-06-13
Spring Boot
Lombok
설치 방법
1.pom.xml에 라이브러리 추가
https://projectlombok.org/setup/maven
Maven
projectlombok.org
<!-- Lombok 라이브러리 추가 -->
pom.xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>
2.다운로드 받은 경로로 가서 java -jar lombok-1.18.16.jar 실행
spring project 한번 실행하고 경로 찾기
경로 찾기
cd: 경로 복붙
들어간 경로에서 java-jar lombok-1.18.28.jar (java-jar l쓰고 tab키 눌러주기 자동완성)
spring 재시작하기
끝
더미용
insert into onememo values(SEQ_ONEMEMO.NEXTVAL,'JSP','스프링',SYSDATE,'PARK');
select count(*)
from onememo o join member m on m.id=o.id
where 1=1 and title like '%' || '자바' || '%' and name like '%' || '김길동' || '%' and content like '%' || '안드로이드' || '%';
select title
from onememo;
select * from onememo;
commit;
insert into onememo values(SEQ_ONEMEMO.NEXTVAL,'JSP','스프링',SYSDATE,'LEE');
select no from onememo;
desc comments;
insert into comments values(SEQ_COMMENTS.NEXTVAL,'Great!',SYSDATE,2,'PARK');
insert into comments values(SEQ_COMMENTS.NEXTVAL,'Good!',SYSDATE,5,'LEE');
desc onememo;
insert into onememo VALUES(seq_onememo.nextval,'1','1',sysdate,'KIM');
commit;
select * from onememo;
select * from comments;
mybatis08/mybatis.jsp 이어서 추가
where절 일부에 사용 두번째
Mybatis.jsp
MyBatisController.java(전체)
package com.kosmo.springapp.basic.mybatis;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.kosmo.springapp.onememo.service.OneMemoDTO;
/*
1. 컨트롤러 작성
1-1.서비스 주입
1-2.서비스의 메소드 호출
2. 서비스 작성(@Service 어노테이션 부착:클래스)
2-1.생성자 인젝션으로 매퍼 인터페이스를 주입 받는다
2-2.매퍼 인터페이스의 메소드 호출
3. 매퍼 인터페이스 작성(@Mapper 어노체이션 부착:인터페이스)
어노테이션방식과 XML방식이 있다
XML방식으로 서비스시에는 반드시 마이바티스 구성을 위한 빈(MyBatisConfig.java)을 만들자
단,어노테이션 방식일때는 필수사항이 아니다
※쿼리문이 복잡할때 혹은 동적 쿼리 적용시에는 XML방식이 유리하다.
※바인딩(쿼리 결과와 자바빈의 매핑) 흐름 절차
MyBatisConfig.java에 등록한 빈이 사전에 컨테이너에 등록 되어 있어야 한다
서비스의 메소드 호출->서비스에서 매퍼 인터페이스의 메소드 호출(XML기반)->
매퍼파일(mybatis.xml)에서 매퍼 인터페이스의 메소드명과 동일한 id값을 찾는다->
해당 아이디값의 쿼리문이 실행된다
마이바티스 구성빈(MyBatisConfig) 미 코드시 어노테이션 방식은
매퍼 인터페이스의 메소드 호출시 바인딩되서(자바객체와 쿼리문) 정상적으로 실행된다
단,XML방식은 매퍼 인터페이스의 메소드 호출시 메소드와 쿼리문이 바인딩 안되서
Invalid bound statement (not found): com.kosmo.springapp.basic.mybatis.MyBatisMapper.getTimeByXml
org.apache.ibatis.binding.BindingException에러 발생
해결책-자바코드로 마이바티스 구성 빈을 작성한다
*/
@Controller
@RequestMapping("/Mybatis")
public class MyBatisController {
//서비스 호출-서비스 주입 받기
@Autowired
private MyBatisService service;
@GetMapping("/annotation")
public String annotation(Model model) {
model.addAttribute("time","ANNOTATION방식:"+service.getTimeByAnnotation());
//뷰정보 반환
return "mybatis08/Mybatis";
}///////////////
@GetMapping("/xml")
public String xml(Model model) {
model.addAttribute("time","XML방식:"+service.getTimeByXml());
//뷰정보 반환
return "mybatis08/Mybatis";
}///////////////
@GetMapping("/If1.do")
public String if1(@RequestParam Map map,Model model) {
//서비스 호출
List<Map> articles=service.if1(map);
for(Map article:articles) {
System.out.println(article.get("NO"));
}
//데이타 저장
model.addAttribute("message", "검색된 총 글 수 : "+articles.size());
//뷰정보 반환
return "mybatis08/Mybatis";
}///////////////////////
@GetMapping("/If2.do")
public String if2(@RequestParam Map map,Model model) {
//서비스 호출
List<OneMemoDTO> articles=service.if2(map);
//데이타 저장
model.addAttribute("message", "갬색된 총 글 수:"+articles.size());
//뷰정보 반환
return "mybatis08/Mybatis";
}///////////////////////
@GetMapping("/choose.do")
public String choose(@RequestParam Map map,Model model) {
//서비스 호출
List<OneMemoDTO> articles=service.choose(map);
//데이타 저장
model.addAttribute("message", "검색된 총 글 수 : "+articles.size());
//뷰정보 반환
return "mybatis08/Mybatis";
}///////////////////////
//※where절에 동적으로 SQL문 추가시 <where>태그 권장
@GetMapping("/where.do")
public String where(@RequestParam Map map,Model model) {
//서비스 호출
List<OneMemoDTO> articles=service.where(map);
for(OneMemoDTO dto:articles) {
System.out.println(dto.getNo());
}
//데이타 저장
model.addAttribute("message", "검색된 총 글 수 : "+articles.size());
//뷰정보 반환
return "mybatis08/Mybatis";
}///////////////////////
@GetMapping("/trim1.do")
public String trim1(@RequestParam Map map,Model model) {
//서비스 호출
List<OneMemoDTO> articles=service.trim1(map);
for(OneMemoDTO dto:articles) {
System.out.println(dto.getNo());
}
//데이타 저장
model.addAttribute("message", "검색된 총 글 수 : "+articles.size());
//뷰정보 반환
return "mybatis08/Mybatis";
}///////////////////////
@GetMapping("/trim2.do")
public String trim2(@RequestParam Map map,Model model) {
//서비스 호출
if(!(map.get("title")==null && map.get("content")==null)) {
int affected = service.trim2(map);
//데이타 저장
model.addAttribute("message", "수정된 총 글 수 : "+affected);
}
else {
//데이타 저장
model.addAttribute("message", "제목이나 내용 둘 중 하나는 전송하세요");
}
//뷰정보 반환
return "mybatis08/Mybatis";
}///////////////////////
@GetMapping("/set.do")
public String set(@RequestParam Map map,Model model) {
//서비스 호출
if(!(map.get("title")==null && map.get("content")==null)) {
int affected = service.set(map);
//데이타 저장
model.addAttribute("message", "수정된 총 글 수 : "+affected);
}
else {
//데이타 저장
model.addAttribute("message", "제목이나 내용 둘 중 하나는 전송하세요");
}
//뷰정보 반환
return "mybatis08/Mybatis";
}///////////////////////
@GetMapping("/foreach.do")
public String foreach(Model model) {
//[글 번호를 저장한 컬렉션]
//파라미터가 리스트 계열 컬렉션인 경우
//List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
//서비스 호출
//List items=service.foreach(numbers);
//파라미터가 맵 계열 컬렉션인 경우
Map<String,List> map = new HashMap<>();
map.put("numbers", Arrays.asList(1,2,3,4,5,6,7,8,9,10));
//서비스 호출
List items=service.foreach(map);
//데이타 저장
model.addAttribute("message", "검색된 총 글 수 : "+items.size());
//뷰정보 반환
return "mybatis08/Mybatis";
}///////////////////////
@PostMapping("/foreachExam.do")
public String foreachExam(@RequestParam("email") int[] emails,Model model) {
int affected = service.foreachExam(emails);
//데이타 저장
model.addAttribute("message", "삭제된 총 글 수 : "+affected);
//뷰정보 반환
return "mybatis08/Mybatis";
}
}
이 중
MyBatisService.java (전체)
package com.kosmo.springapp.basic.mybatis;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import com.kosmo.springapp.onememo.service.OneMemoDTO;
//※서비스의 메소드명은 업무처리 로직에 해당하는 관련있는 이름으로 주로 정의하자
@Service
public class MyBatisService {
//생성자 인젝션으로 MyBatisMapper(DAO역할)를 주입 받자
private MyBatisMapper mapper;
@Autowired//생략 가능
public MyBatisService(MyBatisMapper mapper) {
this.mapper = mapper;
}
public String getTimeByAnnotation() {
/*
매퍼 인터페이스의 getTimeByAnnotation()호출시 어노테이션에 작성한 쿼리가 실행된다
실행된 쿼리 결과를 메소드의 반환타입으로 매핑시킨다
*/
return mapper.getTimeByAnnotation();
}//////////////
public String getTimeByXml() {
/*
매퍼 인터페이스의 getTimeByXml()호출시 매퍼파일(mybatis.xml)에
메소드명과 동일한 id속성을 가진 쿼리가 실행된다.
실행된 쿼리 결과를 메소드의 반환타입으로 매핑시킨다
즉 매퍼파일의 resultType속성은 메소드의 반환타입과 일치 시키자
*/
return mapper.getTimeByXml();
}//////////////////////
public List<Map> if1(Map map) {
return mapper.findWithTitleLike(map);
}
public List<OneMemoDTO> if2(Map map) {
return mapper.findWithColumnsLike(map);
}
public List<OneMemoDTO> choose(Map map) {
return mapper.findWithColumnsLikeChoose(map);
}
public List<OneMemoDTO> where(Map map) {
// TODO Auto-generated method stub
return mapper.findWithColumnsLikeWhere(map);
}
public List<OneMemoDTO> trim1(Map map) {
return mapper.findWithColumnsLikeTrim1(map);
}
public int trim2(Map map) {
return mapper.updateWithColumnsLikeTrim2(map);
}
public int set(Map map) {
return mapper.updateWithColumnsLikeSet(map);
}
public List foreach(List<Integer> numbers) {
//파라미터가 리스트일때
return mapper.findAll(numbers);
}
public List foreach(Map<String, List> map) {
//파라미터가 맵일때
return mapper.findAll(map);
}
//아래 글 삭제 로직을 트랜잭션으로 처리하자
//즉 참조하고 있는 댓글(자식)이 있는 경우 에러발생
//이때 삭제된 모든 글을 다시 롤백하자
//트랜잭션 미 적용-댓글이 없는 글은 삭제됨
/*
public int foreachExam(int[] emails) {
return mapper.deleteEmail(emails);
}*/
//트랜잭션 적용
@Autowired
private TransactionTemplate transactionTemplate;
public int foreachExam(int[] emails) {
/*
transactionTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus status) {
// TODO Auto-generated method stub
return null;
}
});*/
int deleteCount=transactionTemplate.execute(status->{
int affected=0;
try {
affected = mapper.deleteEmail(emails);//자식이 참조시 에러
}
catch(Exception e) {
e.printStackTrace();
affected = -1;//삭제시 에러난 경우 -1.이미 삭제된 거는 롤백된다
}
return affected;
});
return deleteCount;
}
}
서비스 호출에 의해 MyBatisService.java
findWithColumnLike()를 호출
MyBatismapper.java(전체)
package com.kosmo.springapp.basic.mybatis;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import com.kosmo.springapp.onememo.service.OneMemoDTO;
//매퍼 인터페이스 : 쿼리 실행 결과와 객체를 매핑하는 추상 메소드를 정의한 인터페이스
//추상 메소드명은 CRUD작업과 관련된 명으로 주로 한다 예를들면 findBy~() 혹은 save()~등
//서비스단에서 아래 메소드 호출
@Mapper
public interface MyBatisMapper {
//어노테이션 방식(어노테이션에 쿼리 작성)
@Select("SELECT SYSDATE FROM DUAL")
String getTimeByAnnotation();
//XML방식(XML파일에 쿼리 작성)
String getTimeByXml();
//동적 SQL 연습용
List<Map> findWithTitleLike(Map map);
List<OneMemoDTO> findWithColumnsLike(Map map);
List<OneMemoDTO> findWithColumnsLikeChoose(Map map);
List<OneMemoDTO> findWithColumnsLikeWhere(Map map);
List<OneMemoDTO> findWithColumnsLikeTrim1(Map map);
int updateWithColumnsLikeTrim2(Map map);
int updateWithColumnsLikeSet(Map map);
//파라미터가 리스트 일때
List findAll(List<Integer> numbers);
//파라미터가 맵 일때
List findAll(Map<String, List> map);
//foreach 요소 응용
int deleteEmail(int[] emails);
}
MyBatismapper.java에서 찾음
이 메소드의 이름으로 id를 가지는 xml찾기 시작
mybatis.xml(전체)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 매퍼파일 -->
<mapper namespace="com.kosmo.springapp.basic.mybatis.MyBatisMapper">
<!--매퍼 인터페이스의 메소드명이 아이디(id)가 된다-->
<select id="getTimeByXml" resultType="String">
SELECT SYSDATE FROM DUAL
</select>
<!-- 동적 SQL 연습 -->
<!--
id : 매퍼 인터페이스의 메소드명
parameterType : 매퍼 인터페이스 메소드의 인자 타입
resultType : 매퍼 인터페이스 메소드의 반환타입(단,레코드 하나를 담을수 있는 타입 기준)
요소안의 쿼리문에서 값 부부은 #{키}로 객체명 부분은 ${객체명}로 작성한다
#{키}에서 키는 맵 컬렉션일때는 Key를,자바빈인 경우 필드명을 ,int나 String인 경우 임의의 키
${객체명}에서 객체명은 테이블/컬럼 혹은 뷰명이어야 한다
-->
<select id="findWithTitleLike" parameterType="Map" resultType="Map">
SELECT * FROM onememo
WHERE 1=1
<if test="title != null">
AND title LIKE '%' || #{title} || '%'
</if>
</select>
<select id="findWithColumnsLike" parameterType="Map" resultType="oneMemoDto">
SELECT o.*,name
FROM onememo o JOIN member m ON m.id=o.id
WHERE 1=1
<if test="title !=null">
AND title LIKE '%' || #{title} || '%'
</if>
<if test="name !=null">
AND name LIKE '%' || #{name} || '%'
</if>
<if test="content !=null">
AND content LIKE '%' || #{content} || '%'
</if>
</select>
<select id="findWithColumnsLikeChoose" parameterType="Map" resultType="oneMemoDto">
SELECT o.*,name
FROM onememo o JOIN member m ON m.id=o.id
WHERE 1=1
<choose>
<when test="title !=null">
AND title LIKE '%' || #{title} || '%'
</when>
<when test="name !=null">
AND name LIKE '%' || #{name} || '%'
</when>
<when test="content !=null">
AND content LIKE '%' || #{content} || '%'
</when>
</choose>
</select>
<!-- 위 구문에서 "where 1=1"을 "where"로 변경시 조건에 따라 에러 발생한다 -->
<!-- 아래 where 엘리먼트는 JSTL에 의해 컨텐츠가 리턴되면 단순히 "WHERE"만을 추가한다.
게다가 컨텐츠가 "AND"나 "OR"로 시작한다면 그 "AND"나 "OR"를 지워버린다.
조건이 만족할때 WHERE절이 붙는다
-->
<select id="findWithColumnsLikeWhere" parameterType="Map" resultType="oneMemoDto">
SELECT o.*,name
FROM onememo o JOIN member m ON m.id=o.id
<where>
<if test="title !=null">
AND title LIKE '%' || #{title} || '%'
</if>
<if test="name !=null">
AND name LIKE '%' || #{name} || '%'
</if>
<if test="content !=null">
AND content LIKE '%' || #{content} || '%'
</if>
</where>
ORDER BY no DESC
</select>
<!--
trim요소의 속성들
prefix : <trim> 문 안에 쿼리 가장 앞에 붙는 접두어
suffix : <trim> 문 안에 쿼리 가장 뒤에 붙는 접미어
prefixOverrides : <trim> 문 안에 쿼리 가장 앞에 해당하는 문자들이 있으면 자동으로 지워준다.
suffixOverrides : <trim> 문 안에 쿼리 가장 뒤에 해당하는 문자들이 있으면 자동으로 지워준다.
조건이 만족할때 접두어/접미어가 붙는다
-->
<select id="findWithColumnsLikeTrim1" parameterType="Map" resultType="oneMemoDto">
SELECT o.*,name
FROM onememo o JOIN member m ON m.id=o.id
<trim prefix="WHERE" suffix="ORDER BY no DESC" prefixOverrides="AND">
<if test="title !=null">
AND title LIKE '%' || #{title} || '%'
</if>
<if test="name !=null">
AND name LIKE '%' || #{name} || '%'
</if>
<if test="content !=null">
AND content LIKE '%' || #{content} || '%'
</if>
</trim>
</select>
<!-- trim문은 수정시에 쓰자 -->
<update id="updateWithColumnsLikeTrim2" parameterType="Map">
<!-- title이나 content가 전달되면 수정.단,둘 중의 하나는 전달되야 한다
자바코드에서 제어
-->
UPDATE onememo
<trim prefix="SET" suffixOverrides=",">
<if test="title !=null">
title=#{title},
</if>
<if test="content !=null">
content=#{content},
</if>
</trim>
WHERE no=#{no}
</update>
<!-- 수정시는 trim대신 set태그를 쓰자
역시 set요소안의 조건중 최소한 하나는 만족해야 한다
-->
<update id="updateWithColumnsLikeSet" parameterType="Map">
UPDATE onememo
<set>
<if test="title !=null">
title=#{title},
</if>
<if test="content !=null">
content=#{content},
</if>
</set>
WHERE no=#{no}
</update>
<!--
foreach요소의 속성들
collection :List 혹은 배열 타입만 가능
parameterType이 List인 경우 "list" 혹은 "collection"
만약 List<DTO계열>이면 item속성에 지정한 변수명.DTO멤버 속성명 으로 꺼낸다
예]<foreach item="item" ~
#{item.id}
</foreach>
parameterType이 배열인 경우 "array"
parameterType이 Map인 경우 key값(단,value는 List혹은 배열) 지정
item :컬렉션에서 꺼내온 객체 저장 변수
open : 시작 문자열
close : 종료 문자열
separator : 반복 되는 사이에 삽입할 문자열
index : 0부터 순차적으로 증가하는 인덱스번호
-->
<!-- 파라미터가 리스트인 경우 -->
<!--
<select id="findAll" parameterType="List" resultType="oneMemoDto">
SELECT * FROM onememo
WHERE no IN
<foreach collection="list" item="item" open="(" close=")" separator="," >
#{item}
</foreach>
</select>
-->
<!-- 파라미터가 맵인 경우 -->
<select id="findAll" parameterType="Map" resultType="oneMemoDto">
SELECT * FROM onememo
WHERE no IN
<foreach collection="numbers" item="item" open="(" close=")" separator="," >
#{item}
</foreach>
</select>
<delete id="deleteEmail" parameterType="int[]">
DELETE onememo
WHERE no IN
<foreach collection="array" item="item" open="(" close=")" separator="," >
#{item}
</foreach>
</delete>
</mapper>
그 중
결과)
(결과를 보여주기 전에 쿼리를 사용해서 대략 데이터를 넣어줌)
insert into onememo values(SEQ_ONEMEMO.NEXTVAL,'자바','스프링',SYSDATE,'KIM');
insert into onememo values(SEQ_ONEMEMO.NEXTVAL,'자바','자바스프링',SYSDATE,'KIM');
insert into onememo values(SEQ_ONEMEMO.NEXTVAL,'자바','JSP',SYSDATE,'KIM');
insert into onememo values(SEQ_ONEMEMO.NEXTVAL,'자바','부트',SYSDATE,'LEE');
id로 onememo테이블의 id를 PK로 member테이블의 id FK로 inner join하고
localhost:9090/~~ title=자바& name=김길동& content=안드로이드 결과
여기서 주의할 것으로 and앞에 where 1=1이란 true결과값이 없으면
실행될 쿼리 문장은
select o.*,name from onememo o join member m on m.id and title like '%' #title '%';
이 된다.
and 나 or 같은 논리 연산자를 사용하기 위해서는 왼쪽항이 있어야 하는 연산자인데 없다면 쿼리 실행이 안된다.
다른 태그들도 비슷한 구조로 계속 된다
MyBatisController.java
(이 구조가 반복)
Mybatis.jsp
MyBatisController.java
MyBatisService.java
MyBatisMapper.java
mybatis.xml
Mybatis.jsp
MyBatisController.java
MyBatisService.java
MyBatisMapper.java
mybatis.xml
Mybatis.jsp
MyBatisController.java
MyBatisService.java
MyBatisMapper.java
mybatis.xml
Mybatis.jsp
MyBatisController.java
MyBatisService.java
MyBatisMapper.java
mybatis.xml
Mybatis.jsp
MyBatisController.java
MyBatisService.java
MyBatisMapper.java
mybatis.xml
Mybatis.jsp
MyBatisController.java
MyBatisService.java
MyBatisMapper.java
mybatis.xml
Mybatis.jsp
MyBatisController.java
MyBatisService.java
MyBatisMapper.java
mybatis.xml
스프링으로 게시판 만들기
로그인 처리