spring:
datasource:
classpath:/mappers/**/*.xml
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
2022-11-30 18:17:24.813 ERROR 6919 --- [nio-8088-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for bookdetail.getbookVo
### Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for bookdetail.getbookVo] with root cause
java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for bookdetail.getbookVo
:mapper id가 다를 경우
🖍️ 오라클의 경우에는 서브쿼리에 Alias를 주지 않아도 정상적으로 동작하지만
MySQL의 경우에는 무조건 에러
<insert id="addNewPlaylist" parameterType="com.javaex.dto.main.AddPlayListRequest" useGeneratedKeys="true">
<selectKey keyProperty="playlistNo" resultType="Long" order="BEFORE">
SELECT LAST_INSERT_ID()
</selectKey>
<![CDATA[
insert into playlist
values(#{playlistNo}, #{userNo}, now(), #{playlistName}, #{emoNo})
]]>
</insert>
; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: Unknown column 'seq_review_user_no.nextval' in 'field list'] with root cause
mapper namespace="com.javaex.dao.PlaylistFolderDao">
implementation 'javax.xml.bind:jaxb-api:2.1'
package com.javaex.config;
import com.javaex.dto.user.UserDto;
import com.javaex.service.UserService;
import com.javaex.util.JwtUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@Slf4j
@RequiredArgsConstructor
public class JwtTokenFilter extends OncePerRequestFilter {//입장할 때 마다(요청할 떄마다) 매번 불특정한 사람들이 요청 티켓을가지고 체크
private final String secretKey;
private final UserService userService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//권한을 주거나 주지 않는다 ( 입장 ex) 티켓 확인)
//개찰구 역할
//현재는 모두 닫혀 있습니다
final String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
log.info("authorizationHeader:{}", authorizationHeader);
if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
//토큰만 분리해야한다
String token;
try {
token = authorizationHeader.split(" ")[1];
} catch (Exception e) {//요청시 token이 없으면
log.error("token 추출에 실패 했습니다");
filterChain.doFilter(request, response);
return;
}
//JWTTokenUtil.
if (JwtUtil.isExpired(token, secretKey)) {
filterChain.doFilter(request, response);
return;
}
// Token에서 userName 꺼내기
String userName = JwtUtil.getUserEmail(token, secretKey);
log.info("username:{}", userName);
//userDetail 가져오기
UserDto user = userService.findByUsername(userName);
log.info("userEmail:{}", user.getEmail());
//문 열어주기, role 바인딩
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getEmail(), null, List.of(new SimpleGrantedAuthority(user.getEmail()))); //권한을 여러개 줄 수 있다
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);//권한 부여 (이 문을 통과할 수 있다는 것)
filterChain.doFilter(request, response);
}
}
package com.javaex.config;
import com.javaex.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
public class SecurityConfig {
private final UserService userService;
@Value("${jwt.token.secret}")
private String secretKey;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.httpBasic().disable()
.csrf().disable()
.cors().and()
.authorizeRequests()
.antMatchers("/boggle/user/join", "/boggle/user/login").permitAll() // join, login은 언제나 가능
.antMatchers("/boggle/review/**").permitAll()
.antMatchers(HttpMethod.POST, "/api/v1/**").authenticated()//문 만들기(인증된 사용자의 접근 허용)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // jwt사용하는 경우 씀 -> 프런트에서 처리
.and()
.addFilterBefore(new JwtTokenFilter(secretKey, userService), UsernamePasswordAuthenticationFilter.class) //UserNamePasswordAuthenticationFilter적용하기 전에 JWTTokenFilter를 적용 하라는 뜻 입니다.
//로그인을 한 다음에 받은 토큰
.build();
}
}
@AuthenticationPrincipal User user로 받던 것을 삭제하고 하위 코드로 변경
📌https://sillutt.tistory.com/entry/Spring-Security-AuthenticationPrincipal%EA%B3%BC-ArgumentResolver
JPA는 count return 값이 Long, mybatis는 int이다
Long으로 xml 파일에 resultType을 Long으로 지정해줘서 타입이 맞지 않아서 일어난 에러
<resultMap id="reviewNoAndUserNo" type="map">
<result property="userNo" column="user_no"/>
<result property="reviewNo" column="review_no"/>
</resultMap>
<!-- reviewByUser -->
<select id="reviewByUser" resultMap="reviewNoAndUserNo" parameterType="Long">
<![CDATA[
select user_no userNo,
review_no reviewNo
from review
where review_no = #{reviewNo}
]]>
</select>
@Getter
@AllArgsConstructor
@NoArgsConstructor
https://codinghero.tistory.com/24
이건 말 그대로 내가 Insert 나 Update하기 위한 값이 Primary 값이라 동일한 값으로 쿼리문을 실행 할 수 없다는 의미입니다.
keyProperty="reviewUserNo" -> keyProperty="review_user_no"
테이블 컬럼명과 맞춰줘야한다
<insert id="reviewLike" parameterType="map">
<selectKey keyProperty="review_user_no" resultType="Long" order="BEFORE">
SELECT LAST_INSERT_ID()
</selectKey>
<![CDATA[
insert into review_user
values (#{reviewUserNo}, #{userNo}, #{reviewNo}, now())
]]>
</insert>