Spring REST控制器,在事务内部反序列化

时间:2016-10-19 13:26:20

标签: java spring rest jpa transactions

我有一个我正在努力解决的问题。我有一个JPA实体,它包含延迟加载的@OneToMany实体集(下面的代码)。

@Entity
@Table(name = "SKILL")
public class Skill {

    @Id
    @Column(name = "SKILL_ID")
    @GeneratedValue(generator = "increment")
    @GenericGenerator(name = "increment", strategy = "increment")
    private Long id;

    @Column(name = "NAME")
    private String name;

    @ManyToOne(fetch=FetchType.LAZY, cascade={CascadeType.PERSIST, CascadeType.MERGE})
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private Skill parent;

    @OneToMany(mappedBy="parent", cascade={CascadeType.ALL})
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private Set<Skill> children;

    public Skill() {

    }

    // getters-setters ommitted
}

这是来自Spring REST控制器的代码:

@RequestMapping(value = "/skill", method = RequestMethod.GET)
public ResponseEntity<List<Skill>> listAllSkills() {
    Iterable<Skill> skills = skillService.getAllSkills();
    return new ResponseEntity<>(Lists.newArrayList(skills), HttpStatus.OK);
}

每当我尝试从该控制器返回实体时,它都会抛出

JsonMappingException: failed to lazily initialize a collection of 
role: com.juriy.arcadia.domain.Skill.children, could not initialize 
proxy - no Session

据我了解,发生的事情是杰克逊在交易范围之外尝试实体的懒惰部分,这就是找不到Session的原因。如果我添加一个脏黑客并在事务中调用应该手动延迟加载的部分,它可以工作:

@RequestMapping(value = "/skill", method = RequestMethod.GET)
public ResponseEntity<List<Skill>> listAllSkills() {

    Iterable<Skill> skills = skillService.getAllSkills();

    // Hack here: load required items inside of session bounds
    for (Skill s : skills) {
        System.out.println("Fetched skills: "+ s.getChildren().size());
        System.out.println("Fetched parent: "+ s.getParent());
    }
    return new ResponseEntity<>(Lists.newArrayList(skills), HttpStatus.OK);
}

问题:在延迟加载和事务处理的情况下,组织反序列化的假设方法是什么。有没有办法将反序列化放在事务边界内?

相关问题:我听说制作控制器层@Transactional不是一个好习惯。在这种情况下,设计交易的最佳方式是什么?

更新:在我的情况下添加EAGER实体加载不是一个选项(有一个大的实体树,EAGER加载将加载整个树,这将完全杀死表演。)

2 个答案:

答案 0 :(得分:1)

杰克逊在JPA交易之外调用父项的getter,因此懒惰加载的实体不再可用。

更改为Fetch.EAGER或添加汇编程序层(即将实体转换为POJO的图层)。

或者在

中添加@Transaction注释
@Transaction
@RequestMapping(value = "/skill", method = RequestMethod.GET)
public ResponseEntity<List<Skill>> listAllSkills() {
    Iterable<Skill> skills = skillService.getAllSkills();
    return new ResponseEntity<>(Lists.newArrayList(skills), HttpStatus.OK);
}

这假设您已正确设置了事务管理器。

答案 1 :(得分:0)

如果您要返回所有技能列表,则必须使用FetchType.EAGER。延迟加载不会在不直接访问数据的情况下加载数据。这就是你犯这个错误的原因。