로그인 구현-hibernate,no session

이소연's avatar
Aug 05, 2024
로그인 구현-hibernate,no session
 
userNativeRepository
public User login(UserRequest.LoginDTO reqDTO){ Query query = em.createNativeQuery("select * from user_tb where email=? and password=?", User.class); query.setParameter(1, reqDTO.getEmail()); query.setParameter(2,reqDTO.getPassword()); User sessionUser = (User) query.getSingleResult(); return sessionUser; }
 
userService
public User loginByName(UserRequest.LoginDTO reqDTO){ User sessionUser = userNativeRepository.login(reqDTO); return sessionUser; }
 
usercontroller
@PostMapping("/login") public String login(UserRequest.LoginDTO reqDTO) { System.out.println("reqDTO = " + reqDTO); User sessionUser = userService.loginByName(reqDTO); System.out.println("sessionUser = " + sessionUser); session.setAttribute("sessionUser", sessionUser); return "redirect:/"; }
 
userRequest
@Data public static class LoginDTO{ private String email; private String password; }
 
notion image
당신이 겪고 있는 오류는 Hibernate에서 발생하는 LazyInitializationException입니다. 이 오류는 보통 Hibernate이 지연 로딩으로 데이터를 가져오려고 시도할 때 발생하는데, 이 때 사용된 세션이 이미 종료되었거나 더 이상 활성화되지 않은 경우 발생합니다. 오류 메시지 이해하기 오류 메시지를 다음과 같이 분석할 수 있습니다: 오류 유형: LazyInitializationException 이 예외는 Hibernate이 데이터를 지연 로딩으로 가져오려고 할 때, 이미 세션이 닫혀있거나 사용할 수 없는 경우 발생합니다. 주된 원인: could not initialize proxy - no Session Hibernate이 연결된 엔티티의 컬렉션을 초기화하려고 시도할 때, 사용된 세션이 더 이상 활성화되지 않아 초기화를 할 수 없다는 의미입니다. 해결 방법 이 문제를 해결하기 위해 몇 가지 접근 방법이 있습니다: Eager Fetching 설정: 지연 로딩 대신 즉시 로딩을 사용하도록 설정하여 이 문제를 피할 수 있습니다. 이 경우 데이터를 한 번에 모두 가져오므로 성능에 영향을 미칠 수 있습니다. 영속성 컨텍스트 관리: 영속성 컨텍스트를 수동으로 관리하거나, 필요한 데이터를 로드한 후에 세션을 종료하기 전에 모든 필요한 데이터를 초기화할 수 있도록 합니다. 트랜잭션 관리: 트랜잭션을 적절히 관리하여 세션이 닫히기 전에 필요한 데이터를 로드하고 세션을 유지하도록 합니다. 이러한 접근 방법들은 각각의 프로젝트와 상황에 따라 다를 수 있으며, 가장 적합한 해결책을 찾기 위해 코드와 Hibernate 설정을 심도 있게 검토하는 것이 중요합니다.
즉, 1번 해결 eager는 하지마! ⇒ 조별에서 lazy쓴대
2번 영속성 컨텍스트 관리 이걸 수동으로 ⇒ @transactional 달아도 오류 ⇒ 실패
3번 fetch 전략 ⇒ 실
notion image
notion image
 
4번.
notion image
그냥 봐도 아닌 거 같음..
이걸 어디에? ⇒ 실
notion image
notion image
gpt 추천
response 이용
notion image
실패
이해안돼!!!!!! 뭐야 이게!!!
 
service
public UserResponse loginByName(UserRequest.LoginDTO reqDTO) { // User sessionUser = userNativeRepository.login(reqDTO.getEmail(), reqDTO.getPassword()); // return new UserResponse(sessionUser.getEmail(), sessionUser.getPassword()); //}
 

그니까 이 오류는 hibernate에서 lazy~ 오류가 뜨는 건데.. 세션이 활성화가 안되었거나 종료 되었다는 건데..

 
오늘은 때가 아닌 것 같다.. bye
조원들에게 물어보고 공부해야 할 것 같다…
하… 오류 해결은 어렵다.
 
 

문제 해결

notion image
 
을 토대로 코드를 짜니까 해결되었음 .
notion image
 
user-login
 
내가 짠 코드
// //로그인 // public User loginByName(UserRequest.LoginDTO reqDTO) { // User sessionUser = userNativeRepository.login(reqDTO); // return sessionUser; // }
 
gpt가 알려준 코드
@Transactional public User loginByName(UserRequest.LoginDTO reqDTO) { User user = userNativeRepository.login(reqDTO); user.getSpecs().size(); return user; }
 
⇒ 차이는
두 메서드의 차이점은 주로 데이터베이스 접근 방식, 초기화 방법 및 사용하는 리포지토리에서 차이가 납니다. 아래에서 차이점을 상세히 설명하겠습니다. 1. 첫 번째 메서드 (loginByName with findByName): java 코드 복사 @Service public class UserService { @Transactional public User loginByName(UserRequest.LoginDTO reqDTO) { User user = userRepository.findByName(reqDTO.getName()); // specs 컬렉션을 초기화 user.getSpecs().size(); // 컬렉션을 접근하여 초기화 return user; } } 리포지토리 사용: userRepository를 사용하여 사용자를 검색합니다. userRepository는 일반적으로 JpaRepository 또는 CrudRepository와 같은 Spring Data JPA 리포지토리일 가능성이 높습니다. 검색 기준: 사용자의 이름(reqDTO.getName())을 기반으로 사용자를 검색합니다. 초기화: user.getSpecs().size();를 통해 specs 컬렉션을 초기화합니다. 이는 Hibernate에서 지연 로딩된 컬렉션을 강제로 로딩하는 방식입니다. 트랜잭션 관리: @Transactional 어노테이션을 사용하여 메서드가 트랜잭션 내에서 실행되도록 보장합니다. 이는 데이터베이스 작업의 일관성과 원자성을 유지하는 데 도움이 됩니다. 2. 두 번째 메서드 (loginByName with userNativeRepository.login): java 코드 복사 public User loginByName(UserRequest.LoginDTO reqDTO) { User sessionUser = userNativeRepository.login(reqDTO); return sessionUser; } 리포지토리 사용: userNativeRepository를 사용하여 사용자를 검색합니다. userNativeRepository는 아마도 네이티브 쿼리를 사용하여 사용자 인증을 수행하는 리포지토리일 것입니다. 검색 기준: LoginDTO 객체(reqDTO)를 직접 받아서 이를 기반으로 사용자 인증을 수행합니다. 이 메서드는 이름뿐만 아니라 이메일, 패스워드 등 다른 필드도 포함할 수 있습니다. 초기화: 이 메서드에는 컬렉션 초기화 코드가 포함되어 있지 않습니다. 만약 specs 컬렉션이 필요하다면, 이 부분을 추가해야 할 것입니다. 트랜잭션 관리: @Transactional 어노테이션이 없습니다. 이는 해당 메서드가 트랜잭션 내에서 실행되지 않을 수 있음을 의미합니다. 데이터베이스 작업이 여러 개라면 트랜잭션으로 묶는 것이 좋습니다. 비교 요약 리포지토리 사용: 첫 번째 메서드는 userRepository를 사용하여 이름을 기반으로 사용자를 검색합니다. 두 번째 메서드는 userNativeRepository를 사용하여 LoginDTO 객체를 기반으로 사용자를 검색합니다. 검색 기준: 첫 번째 메서드는 이름(reqDTO.getName())을 사용합니다. 두 번째 메서드는 LoginDTO 객체 전체를 사용합니다. 컬렉션 초기화: 첫 번째 메서드는 specs 컬렉션을 명시적으로 초기화합니다. 두 번째 메서드는 컬렉션 초기화를 포함하지 않습니다. 트랜잭션 관리: 첫 번째 메서드는 @Transactional을 사용하여 트랜잭션 내에서 실행됩니다. 두 번째 메서드는 트랜잭션 관리가 없습니다. 언제 어떤 메서드를 사용할지 첫 번째 메서드는 간단한 JPA 리포지토리 메서드를 사용하여 이름을 기준으로 사용자를 검색하고, 필요 시 컬렉션을 초기화합니다. 기본적인 CRUD 작업에 적합합니다. 두 번째 메서드는 좀 더 복잡한 사용자 인증 로직이 필요할 때 유용합니다. 네이티브 쿼리나 복잡한 검색 로직이 필요할 때 사용할 수 있습니다. 필요한 경우 트랜잭션 관리와 컬렉션 초기화를 추가해야 합니다. 필요에 따라 적절한 방식과 추가 로직을 포함하여 사용할 수 있습니다.
specs 컬렉션이 뭔데? ChatGPT specs 컬렉션은 아마도 User 엔티티 클래스에 있는 하나의 필드로, User가 소유한 여러 Spec 객체들을 나타내는 컬렉션일 것입니다. 이는 엔티티 간의 관계를 나타내는 데 사용됩니다. 예시: UserSpec 엔티티 아래는 UserSpec 엔티티의 예시입니다. User 엔티티에는 specs라는 필드가 있고, 이는 여러 Spec 객체와의 일대다 관계를 나타냅니다. User 엔티티 java 코드 복사 package com.example.aboutme.user; import javax.persistence.*; import java.util.List; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String email; private String password; @OneToMany(fetch = FetchType.LAZY, mappedBy = "user") private List<Spec> specs; // Getters and Setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public List<Spec> getSpecs() { return specs; } public void setSpecs(List<Spec> specs) { this.specs = specs; } } Spec 엔티티 java 코드 복사 package com.example.aboutme.user; import javax.persistence.*; @Entity public class Spec { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String detail; @ManyToOne @JoinColumn(name = "user_id") private User user; // Getters and Setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } } specs 컬렉션의 역할 User 엔티티에서 specs 필드는 여러 Spec 객체와의 일대다 관계를 나타냅니다. @OneToMany 어노테이션을 사용하여 User가 여러 Spec을 가질 수 있음을 나타냅니다. fetch = FetchType.LAZY는 지연 로딩을 사용하여 User를 로드할 때 specs 컬렉션을 즉시 로드하지 않고, 실제로 접근할 때 로드합니다. 왜 초기화가 필요한가? Hibernate에서는 기본적으로 @OneToMany와 같은 컬렉션 관계를 지연 로딩(Lazy Loading)으로 설정합니다. 즉, 컬렉션을 실제로 접근하기 전까지 데이터베이스에서 로드하지 않습니다. 트랜잭션 외부에서 지연 로딩된 컬렉션에 접근하려고 하면 LazyInitializationException 예외가 발생합니다. 초기화 예시 java 코드 복사 @Service public class UserService { @Transactional public User loginByName(UserRequest.LoginDTO reqDTO) { User user = userRepository.findByName(reqDTO.getName()); // specs 컬렉션을 초기화 user.getSpecs().size(); // 컬렉션을 접근하여 초기화 return user; } } 위 예시에서 user.getSpecs().size();를 사용하여 specs 컬렉션을 강제로 초기화합니다. 이는 트랜잭션 내에서 이루어져야 하므로, @Transactional 어노테이션이 필요합니다. 트랜잭션 내에서 초기화된 컬렉션은 트랜잭션 외부에서도 접근할 수 있습니다. 결론 specs 컬렉션은 UserSpec 엔티티 간의 일대다 관계를 나타내며, 여러 Spec 객체를 포함합니다. 지연 로딩된 컬렉션을 트랜잭션 외부에서 접근하려면 초기화가 필요합니다. 초기화는 @Transactional 어노테이션을 사용하여 트랜잭션 내에서 수행됩니다.
아직 컬렉션, 지연로딩, onetomany 예외 처리 등 개념이 미흡.. 오늘은 무슨 소리인지 모르겟음…
Share article

Coding's note