我的Spring后端有一个非常讨厌的StackOverflowException,我需要帮助。这不容易解决。我真的希望在这里找到一些帮助。
我后端的大部分工作都在工作。我可以在我的REST界面查询模型,它们很好地通过spring-hateoas,GET,PUT和POST操作工作返回。但有一个例外:当我尝试更新现有的DelegationModel
时,我遇到了无休止的StackOverflowException。
这是我的DelegetionModel.java
课程。请注明,委托模型实际上没有任何使用@CreatedBy注释的属性!
@Entity
@Data
@NoArgsConstructor
@RequiredArgsConstructor(suppressConstructorProperties = true) //BUGFIX: https://jira.spring.io/browse/DATAREST-884
@EntityListeners(AuditingEntityListener.class) // this is necessary so that UpdatedAt and CreatedAt are handled.
@Table(name = "delegations")
public class DelegationModel {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long id;
/** Area that this delegation is in */
@NonNull
@NotNull
@ManyToOne
public AreaModel area;
/** reference to delegee that delegated his vote */
@NonNull
@NotNull
@ManyToOne
public UserModel fromUser;
/** reference to proxy that receives the delegation */
@NonNull
@NotNull
@ManyToOne
public UserModel toProxy;
@CreatedDate
@NotNull
public Date createdAt = new Date();
@LastModifiedDate
@NotNull
public Date updatedAt = new Date();
}
如Spring-data-jpa doc中所述,我实现了必要的AuditorAware接口,该接口从SQL DB加载UserModel。我原以为只有具有@CreatedBy
注释字段的模型才会调用此AuditorAware接口。
@Component
public class LiquidoAuditorAware implements AuditorAware<UserModel> {
Logger log = LoggerFactory.getLogger(this.getClass()); // Simple Logging Facade 4 Java
@Autowired
UserRepo userRepo;
@Override
public UserModel getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
log.warn("Cannot getCurrentAuditor. No one is currently authenticated");
return null;
}
User principal = (org.springframework.security.core.userdetails.User) authentication.getPrincipal();
UserModel currentlyLoggedInUser = userRepo.findByEmail(principal.getUsername()); // <<<<======= (!)
return currentlyLoggedInUser;
} catch (Exception e) {
log.error("Cannot getCurrentAuditor: "+e);
return null;
}
}
}
现在我在UserRestController
更新一个DelegationModel。功能性&#34; Scrum用户故事&#34;这是:
作为一名用户,我希望能够存储一个代表团,以便我可以将我的权利转发给我的代理人。
@RestController
@RequestMapping("/liquido/v2/users")
public class UserRestController {
[...]
@RequestMapping(value = "/saveProxy", method = PUT, consumes="application/json")
@ResponseStatus(HttpStatus.CREATED)
public @ResponseBody String saveProxy(
@RequestBody Resource<DelegationModel> delegationResource,
//PersistentEntityResourceAssembler resourceAssembler,
Principal principal) throws BindException
{
[...]
DelegationModel result = delegationRepo.save(existingDelegation);
[...]
}
[...]
}
由于某些原因,我看不到,这个实际上调用了上面的AuditorAware实现。现在的问题是,我的LqiuidoAuditorAware实现被一次又一次地调用并且无限循环。似乎LiquidoAuditorAware.java中的UserModel查询再次调用LiquidoAuditorAware。 (这是不寻常的,因为这只是来自DB的读操作。)
可以在此github repo
中找到所有代码我真的在这里给予任何帮助。我在黑暗中寻找: - )
答案 0 :(得分:3)
您看到的行为的原因是,AuditorAware
实施是在JPA @PrePersist
/ @PreUpdate
回调中调用的。您现在通过调用findByEmail(…)
来发出查询,这会再次触发脏检测,从而导致刷新被触发,从而调用回调。
建议的解决方法是将UserModel
的实例保留在Spring Security User
实现中(通过UserDetailsService
在身份验证时查找实例时查找),以便你不需要额外的数据库查询。
另一个(不太推荐)的解决方法可能是在EntityManager
实现中注入AuditorAware
,在查询执行之前调用setFlushMode(FlushModeType.COMMIT)
并在此之后将其重置为FlushModeType.AUTO
,这样就不会为查询执行触发刷新。