You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JPA懒加载异常求助:无法初始化代理-无Session问题

解决Hibernate懒加载导致的JSON序列化异常问题

问题描述

从数据库对象检索列表时,仅在调用mapper.writeValueAsString(pieces)将棋子序列化为JSON发送给前端时,触发failed to lazily initialize a collection of role could not initialize proxy - no Session错误,其他场景运行正常。

相关代码

事件处理方法

@Transactional
private DataListener<String> onGetPieceInfoEvent() {
    return (senderClient, data, ack) -> {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode jsonNode = mapper.readTree(data);
        String roomId = jsonNode.get("roomId").textValue();
        String color = jsonNode.get("color").textValue();
    
        Game game = this.gameService.findByRoomId(roomId);
    
        List<Piece> pieces = color.equals("white") ? game.getWhitePieces()
                : game.getBlackPieces();
    
        Collection<SocketIOClient> clientsCollection = this.server.getRoomOperations(roomId).getClients();
        ArrayList<SocketIOClient> clients = new ArrayList<>(clientsCollection);
    
        SocketIOClient client = clients.get(0).getSessionId().equals(senderClient.getSessionId()) ? clients.get(1)
                : clients.get(0);
    
        socketService.sendMessage(roomId, "colorOnMount", mapper.writeValueAsString(pieces), client);
    };
}

Game实体类

@Entity
@Table(name="game_data")
public class Game {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="game_id")
    private Integer gameId;

    @Column(name="room",nullable = false)
    private String roomId;
    
    GameState state;
    
    @ElementCollection
    @ColumnDefault("[]")
    @CollectionTable(name = "white_pieces", joinColumns = @JoinColumn(name = "game_id"))
    @Basic(fetch = FetchType.EAGER)
    private List<Piece> whitePieces = new ArrayList<>();
    
    @ElementCollection
    @ColumnDefault("[]")
    @CollectionTable(name = "black_pieces", joinColumns = @JoinColumn(name = "game_id"))
    @Basic(fetch = FetchType.EAGER)
    private List<Piece> blackPieces = new ArrayList<>();
}

Piece实体类

@Embeddable
@JsonIgnoreProperties(ignoreUnknown = true)
public class Piece {
    @JsonProperty("image")
    @Column
    private String image;

    @JsonProperty("x")
    @Column
    private Integer x;

    @JsonProperty("y")
    @Column
    private Integer y;

    @JsonProperty("type")
    @Column
    private String type;

    @JsonProperty("team")
    @Column
    private String team;

    @JsonProperty("enPassant")
    @Column
    private Boolean enPassant;
}

解决方案

  • 确保事务与Session覆盖序列化操作
    异步回调场景下,@Transactional可能无法有效维持Session到序列化阶段。在gameService.findByRoomId方法内提前初始化集合:

    // 在GameService实现中修改findByRoomId方法
    public Game findByRoomId(String roomId) {
        Game game = entityManager.createQuery("SELECT g FROM Game g WHERE g.roomId = :roomId", Game.class)
                .setParameter("roomId", roomId)
                .getSingleResult();
        // 强制初始化集合,确保Session未关闭时加载数据
        Hibernate.initialize(game.getWhitePieces());
        Hibernate.initialize(game.getBlackPieces());
        return game;
    }
    
  • 修正集合加载策略配置
    集合类型的加载策略应直接在@ElementCollection上指定,而非@Basic注解:

    @ElementCollection(fetch = FetchType.EAGER)
    @ColumnDefault("[]")
    @CollectionTable(name = "white_pieces", joinColumns = @JoinColumn(name = "game_id"))
    private List<Piece> whitePieces = new ArrayList<>();
    
    @ElementCollection(fetch = FetchType.EAGER)
    @ColumnDefault("[]")
    @CollectionTable(name = "black_pieces", joinColumns = @JoinColumn(name = "game_id"))
    private List<Piece> blackPieces = new ArrayList<>();
    
  • 使用DTO转换隔离实体与前端数据
    创建与前端需求匹配的DTO类,避免直接序列化Hibernate实体:

    public class PieceDTO {
        private String image;
        private Integer x;
        private Integer y;
        private String type;
        private String team;
        private Boolean enPassant;
    
        public PieceDTO(Piece piece) {
            this.image = piece.getImage();
            this.x = piece.getX();
            this.y = piece.getY();
            this.type = piece.getType();
            this.team = piece.getTeam();
            this.enPassant = piece.getEnPassant();
        }
    
        // 生成getter方法
    }
    

    在业务逻辑中完成转换后再序列化:

    List<PieceDTO> pieceDTOs = pieces.stream().map(PieceDTO::new).collect(Collectors.toList());
    socketService.sendMessage(roomId, "colorOnMount", mapper.writeValueAsString(pieceDTOs), client);
    
  • 配置Jackson兼容Hibernate懒加载
    添加Jackson的Hibernate模块依赖,自动处理代理对象:

    <!-- Maven依赖 -->
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-hibernate5</artifactId>
    </dependency>
    

    初始化ObjectMapper时注册该模块:

    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new Hibernate5Module());
    

内容的提问来源于stack exchange,提问作者Ankit Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 02:25:48