@Transactional(readOnly = true) 오류

원인
import jakarta.transaction.Transactional;
해결
import org.springframework.transaction.annotation.Transactional;
하면 오류가 해결됩니다.
Jsp 프론트 섹션
<div class="collapse navbar-collapse" id="collapsibleNavbar">
<c:choose>
<c:when test="${empty sessionScope.principal}">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" href="/blog/user/loginForm">로그인</a></li>
<li class="nav-item"><a class="nav-link" href="/blog/user/joinForm">회원가입</a></li>
</ul>
</c:when>
<c:otherwise>
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" href="/blog/board/writeForm">글작성</a></li>
<li class="nav-item"><a class="nav-link" href="/blog/user/userInfo">회원정보</a></li>
<li class="nav-item"><a class="nav-link" href="/blog/user/logout">로그아웃</a></li>
</ul>
</c:otherwise>
</c:choose>
</div>
================================================= form =================================================
<form action="/user/join">
<div class="form-group">
<label for="email">Email</label> <input type="email" class="form-control" placeholder="Enter email" id="email">
</div>
<div class="form-group">
<label for="Password">Password</label> <input type="password" class="form-control" placeholder="Enter password" id="password">
</div>
<div class="form-group form-check">
<label class="form-check-label"> <input class="form-check-input" type="checkbox"> Remember me
</label>
</div>
</form>
<button id="btn-login" class="btn btn-primary">login</button>
================================================ = Ajax =================================================
let index = {
init: function() {
$('#btn-save').on('click', function() { // function(){} , () => {} this를 바인딩하기 위해서!
index.save();
})
$('#btn-login').on('click', function() { // function(){} , () => {} this를 바인딩하기 위해서!
index.login();
})
},
save: function() {
let data = {
username: $('#username').val(),
email: $('#email').val(),
password: $('#password').val()
};
console.log(data);
// ajax 호출시 default가 비동기 호출
// ajax 통신을 이용해서 3개의 데이터를 json으로 변경하여 insert 요청
$.ajax({
type: "POST",
url: "/blog/api/user/join",
data: JSON.stringify(data), // HTTP body 데이터
contentType: "application/json; charset=utf-8", // body 데이터가 어떤 타입인지(MIME)
dataType: "json", // 요청을 서버로해서 응답이 왔을 때 json이라면 javascript 타입으로 변경시켜줍니다.
success: function(response) {
alert("회원가입이 완료되었습니다.");
console.log(response);
// location.href = "/blog";
},
error: function(xhr, status, error) {
alert("오류가 발생하였습니다. 상태 코드: " + xhr.status);
console.error("에러: ", error);
}
});
},
login: function() {
let data = {
email: $('#email').val(),
password: $('#password').val()
};
console.log(data);
$.ajax({
type: "POST",
url: "/blog/api/user/login",
data: JSON.stringify(data), // HTTP body 데이터
contentType: "application/json; charset=utf-8", // body 데이터가 어떤 타입인지(MIME)
dataType: "json", // 요청을 서버로해서 응답이 왔을 때 json이라면 javascript 타입으로 변경시켜줍니다.
success: function(response) {
if(response.data == 1){
alert("로그인.");
location.href = "/blog";
} else{
alert("로그인 실패.");
}
console.log(response);
},
error: function(xhr, status, error) {
alert("오류가 발생하였습니다. 상태 코드: " + xhr.status);
console.error("에러: ", error);
}
});
}
}
console.log(1111);
index.init();
================================================ = Controller =================================================
@PostMapping("/api/user/login")
public ResponseDto<Integer> login(@RequestBody User user, HttpSession session) {
log.info("login User {}", user);
User principal = userService.login(user); // principal (접근주체)
log.info("login User after service{}", user);
int result = -1;
if(principal.getEmail() != null && principal.getPassword() != null) {
session.setAttribute("principal", principal); // 세션 값 생성
result = 1;
}
return new ResponseDto<Integer>(HttpStatus.OK.value(), result);
}
================================================ = Service =================================================
@Transactional(readOnly = true) // Select 할 때 트랜잭션 시작, 서비스 종료시에 트렌잭션 종료 (정합성 높여줍니다)
public User login(User user) {
return userRepository.findByEmailAndPassword(user.getEmail(), user.getPassword());
}
================================================ = UserRepository =================================================
public interface UserRepository extends JpaRepository<User,Integer> { //JpaRepository<테이블,프라이머리 키 타입>
// JPA Naming 쿼리 전략
// Select * FROM user WHERE email = ? AND password = ?;
User findByEmailAndPassword(String email, String password);
// @Query(value="Select * FROM user WHERE email = ? AND password = ?", nativeQuery = true)
// User login(String email, String password);
}
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
태그를 추가하여 jstl 함수를 사용할 수 있게 할 수 있습니다.
로그인 하기 위해서 JPARepository를 수정해주는 것이 좋습니다. JPA Naming 쿼리 전략을 사용하여 이메일과 패스워드를 가지고 User 값을 불러와 사용할 수 있습니다.