2017-02-18 23 views
1

У меня есть действительно неприятное исключение StackOverflowException в моем backend-сервере, с которым мне нужна помощь. Это не будет легко решено. Я действительно надеюсь найти здесь какую-то помощь.StackOverflowException в приложении spring-data-jpa с функцией Spring-security AuditorAware

Большинство частей моей внутренней работы. Я могу запросить мой интерфейс 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 интерфейс, который загружает UserModel из БД SQL. Я бы ожидал, что этот интерфейс AuditorAware вызывается только для моделей, у которых есть поле, аннотированное @CreatedBy.

@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; 
    } 
    } 
} 

Сейчас я обновлю DelegationModel в моем UserRestController. Функциональная «Scrum User Story» здесь:

Как пользователь, я хочу иметь возможность хранить делегацию, чтобы я мог передать свое право голоса на мой прокси.

@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); 
    [...] 
    } 

    [...]   

} 

По какой-то причине, что я не могу видеть, это actualy называет реализацию AuditorAware выше. Проблема в том, что моя реализация LqiuidoAuditorAware вызывается снова и снова в бесконечном цикле. Похоже, что запрос UserModel внутри LiquidoAuditorAware.java снова вызывает LiquidoAuditorAware. (Что необычно, потому что это только операция чтения из БД.)

Вот full ThreadDump as a Gist

Весь код можно найти в разделах этой github repo

Я действительно apriciate любой помогите здесь. Я ищу в темноте :-)

ответ

3

Причина, по которой вы видите, что реализация AuditorAware вызывается из JPA @PrePersist/@PreUpdate обратного вызова. Теперь вы отправляете запрос, вызывая findByEmail(…), который снова запускает обнаружение грязных объектов, что, в свою очередь, вызывает срабатывание промывки и, следовательно, вызов обратных вызовов.

Рекомендуемый обходной путь, чтобы сохранить экземпляр UserModel внутри User реализации Spring Security (посмотрев его, когда UserDetailsService смотрит вверх экземпляр на проверку подлинности), так что вам не нужен дополнительный запрос к базе данных.

Другой (менее рекомендуется) обходной путь может быть, чтобы ввести в EntityManager в AuditorAware реализации, вызовите setFlushMode(FlushModeType.COMMIT) перед выполнением запроса и сбросить его FlushModeType.AUTO после этого, так что поток не будет срабатывать для выполнения запроса.

+0

Большое вам спасибо за быстрый ответ! Я немного доволен, что мое первоначальное предположение было правильным, что проблема внутри AuditorAware является проблемой.Но я не видел обходного пути. Я это сделаю. – Robert

+0

Только для справки: только что нашел этот очень похожий вопрос минуту назад: http://stackoverflow.com/questions/14223649/how-to-implement-auditoraware-with-spring-data-jpa-and-spring-security?rq = 1 – Robert