问题
I have a stateless REST backend. So no HTML views. Just JSON and REST endpoints.
Authentication is done with Json Web Tokens. The client sends a JWT in each request.
My backend takes the user's email from the subject claim in this JWT. Then it loads the UserModel from the database in class LiquidoUserDetailsService implements UserDetailsService { ...}
Each user is part of a team. But the Team is a big entity with a lot of information in it. So teams are only loaded lazily, when necessary:
UserModel.java
@Entity
@Table(name = "users")
public class UserModel extends BaseModel {
@NotNull
@NonNull
@Column(unique = true)
public String email;
@ManyToOne(fetch = FetchType.LAZY) // only load team info (with all info) if required
public TeamModel team;
[...]
}
Now I have a service that should return the team of the current user:
TeamService.java
@PreAuthorize(HAS_ROLE_USER)
@RequestMapping("/getOwnTeam")
@Transactional // [1]
public TeamModel getOwnTeam() {
// Get currently logged in user (that was loaded with info from JWT)
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
LiquidoAuthUser authUser = (LiquidoAuthUser)authentication.getPrincipal();
// LiquidoAuthUser is the Adapter betwen spring's User and my USerModel
UserModel currentUser = authUser.getLiquidoUserModel()
TeamModel team = currentUser.getTeam() // <=== [2] throws LazyInitializationException
return team
}
Now I think I know where the problem is. But I do not yet have a clean solution for it.
My UserModel is loaded in class LiquidoUserDetailsService implements UserDetailsService
But this happens very early, in a filter, when the HTTP request is processed. As it seams the @Transaction
in my TeamService
class is not yet started at that time.
Then when the code enters the getOwnTeam()
method, a new transaction is started [1]. But in there I cannot lazy load the user's team anymore. [2]
How can I model my users and teams, so that
- The team data is only loaded when necessary
- I can load the data manually when neccessary
回答1:
If you need different load startegy you can use:
- Native sql when query
- jpql with construction like join fetch
- Entity Graph (https://www.baeldung.com/jpa-entity-graph) The main benefit when you use such way to load is single request to database. You can read more https://thorben-janssen.com/lazyinitializationexception/
Your object in deatached state - this is reason of LazyInitializationException (you cat move it to other state to load your object) for example
entityManager.merge(deatachedEntity);
来源:https://stackoverflow.com/questions/65752757/hibernate-lazy-loading-and-springs-userdetails