如何正确地将元素添加到双向关系?我正在收集被驱逐

问题描述

我有以下课程

public class User {

    @OneToMany(
            fetch = FetchType.EAGER,mappedBy = "user",cascade = CascadeType.ALL
    )
    private Set<UserSession> sessions;


    public UserSession login() {
        UserSession session = new UserSession();
        session.setUser(this);
        session.persistAndFlush();
        this.persistAndFlush();
        return session;
    }


....

public class UserSession {

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    protected User user;

如您所见,我正在从它的一侧添加会话。有时有时我会得到

caused by: org.hibernate.HibernateException: collection was evicted

如何正确操作?

解决方法

要添加双向关系中的元素,

  • 在父类中创建一个辅助方法来添加或删除子实体。
  • 创建子实体并使用辅助方法将其添加到父实体的集合中
  • 保存子实体

示例代码:

@Getter
@Setter
@Entity
class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String name;
    @OneToMany(mappedBy = "user",cascade = CascadeType.ALL,orphanRemoval = true)
    private Set<UserSession> userSessions = new HashSet<>();

    public void addNewSession(UserSession userSession) {
        this.userSessions.add(userSession);
        userSession.setUser(this);
    }

    public void removeSession(UserSession userSession) {
        this.userSessions.remove(userSession);
        userSession.setUser(null);
    }
}

完整工作代码:

package com.example.demo;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import org.hibernate.annotations.ResultCheckStyle;
import org.hibernate.annotations.SQLDelete;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.repository.CrudRepository;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

@RestController
@RequestMapping("/users")
@EnableTransactionManagement
public class UserAndSessionsController {

    private final UserService userService;

    @Autowired
    public UserAndSessionsController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public Iterable<User> list() {
        return userService.list();
    }

    @PostMapping
    public User create(@RequestBody User user) {
        return userService.save(user);
    }

    @GetMapping("/sessions")
    public Iterable<UserSession> sessions(@RequestParam("userId") Integer userId) {
        return userService.getUserSessions(userId);
    }

    @PostMapping(path = "login")
    public UserSession login(@RequestBody User user) throws Throwable {
        return userService.createNewLoginSession(user);
    }

    @PostMapping(path = "logout")
    public UserSession logout(@RequestBody UserSessionsDto userSessionsDto) throws Throwable {
        return userService.invalidateLoginSession(userSessionsDto.getUser(),userSessionsDto.getUserSession());
    }

    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }

}

@ToString
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
class UserSessionsDto {
    private User user;
    private UserSession userSession;
}

@Getter
@Setter
@Entity
class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String name;
    @OneToMany(mappedBy = "user",orphanRemoval = true)
    private Set<UserSession> userSessions = new HashSet<>();

    public void addNewSession(UserSession userSession) {
        this.userSessions.add(userSession);
        userSession.setUser(this);
    }

    public void removeSession(UserSession userSession) {
        this.userSessions.remove(userSession);
        userSession.setUser(null);
    }
}

@Getter
@Setter
@Entity
@SQLDelete(sql = "update USER_SESSION set valid = false where id = ?",check = ResultCheckStyle.COUNT)
class UserSession {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String SessionId;

    private boolean valid;

    @JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY,optional = false)
    private User user;

}

@Service
class UserService {
    private final UserRepository userRepository;
    private final UserSessionRepository userSessionRepository;

    @Autowired
    UserService(UserRepository userRepository,UserSessionRepository userSessionRepository) {
        this.userRepository = userRepository;
        this.userSessionRepository = userSessionRepository;
    }

    @Transactional
    public User save(User user) {
        return userRepository.save(user);
    }

    @Transactional(readOnly = true)
    public Iterable<User> list() {
        return userRepository.findAll();
    }

    @Transactional(readOnly = true)
    public Iterable<UserSession> getUserSessions(Integer userId) {
//        return userRepository.findById(userId)
//                .map(User::getUserSessions)
//                .orElse(Collections.emptySet());
        return userSessionRepository.findUserSessionsByUser_Id(userId);
    }

    @Transactional
    public UserSession createNewLoginSession(User user) throws Throwable {
        final User dbUser = userRepository
                .findById(user.getId())
                .orElseThrow(() -> new RuntimeException("User : " + user.getName() + " not found"));
        final UserSession userSession = new UserSession();
        userSession.setSessionId(UUID.randomUUID().toString());
        userSession.setValid(true);
        dbUser.addNewSession(userSession);
        userSessionRepository.save(userSession);
        return userSession;
    }

    @Transactional
    public UserSession invalidateLoginSession(User user,UserSession userSession) throws Throwable {
        final User dbUser = userRepository
                .findById(user.getId())
                .orElseThrow(() -> new RuntimeException("User : " + user.getName() + " not found"));
        final UserSession dbUserSession = userSessionRepository
                .findById(userSession.getId())
                .orElseThrow(() -> new RuntimeException("UserSession : " + userSession.getSessionId() + " not found"));
        dbUser.removeSession(dbUserSession);
        userSessionRepository.delete(dbUserSession);
        return dbUserSession;
    }
}


@Repository
interface UserRepository extends CrudRepository<User,Integer> {

}

@Repository
interface UserSessionRepository extends CrudRepository<UserSession,Integer> {
    Iterable<UserSession> findUserSessionsByUser_Id(Integer userId);
}

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...