Служба обновления с использованием весенней загрузки Служба REST завершается с ошибкой StackOverflow в AuditAwareImpl - PullRequest
0 голосов
/ 26 февраля 2019

Краткое описание проблемы

Сбой весенней загрузки. Вызов службы PUT REST завершается с ошибкой стека потока в AuditAwareImpl.

Описание проблемы

У меня есть простое приложение с пружинной загрузкой с двумя сущностями.

Сущность 1: Пользователь | Объект 2: Проект

Пользователь может создать запись объекта проекта (или), может изменить запись объекта проекта (или) может быть руководителем проекта для записи объекта проекта.

Теперь я могу успешно использовать загрузочную систему Sprint - POST / GET-All / Get-One и видеть повторяющиеся записи.

Однако, когда я пытаюсь использовать PUTПри вызове REST для обновления любого атрибута объекта Project я вижу следующее исключение в AuditAwareImpl.

Логика, используемая в службе PUT -

  1. Получение полезной нагрузки с атрибутами дляupdate.
  2. Также идентифицируйте обновляемую запись, отправленную через RequestParam - идентификатор.
  3. Извлеките текущую запись в базе данных для данного идентификатора.
  4. Обновите атрибуты (обновляемые)из ввода в выбранной записи БД.
  5. Сохранить.

Ниже приведен снимок используемого кода.

package com.app.mycompany.AgileCenterServices.entities;

import java.util.Date;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;
import org.springframework.context.annotation.Scope;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;

@Entity
@Table(name="Project")
@EntityListeners(AuditingEntityListener.class)
@Scope("session")
public class Project {

    @Id
    @GenericGenerator(name = "sequence_project_id", strategy = "com.app.mycompany.AgileCenterServices.util.ProjectIdGenerator")
    @GeneratedValue(generator = "sequence_project_id")
    @Column(unique = true)
    private String id;

    private String name;

    private String description;

    private String template;

    private String projectKey;

    private String type;

    private String category;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn (name = "projectLead", referencedColumnName = "username")
    private User projectLead;

        @Column(nullable = false, name = "active", columnDefinition = "BOOLEAN")
    private Boolean active;

    @CreatedBy
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn (nullable = false, updatable = false, name = "createdBy", referencedColumnName = "username")
    private User createdBy;

    @CreatedDate
    @Column(nullable = false, updatable = false)
    private Date createdDate;

    @LastModifiedBy
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn (nullable = false, updatable = false, name = "modifiedBy", referencedColumnName = "username")
    private User modifiedBy;

    @LastModifiedDate
    @Column(nullable = false)
    private Date modifiedDate;

    public Project() {}

    /* Constructor using fields */

    /* getters and setters */
}

User.java

package com.app.mycompany.AgileCenterServices.entities;


import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.JoinColumn;

import org.springframework.context.annotation.Scope;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.GrantedAuthority;
//import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;

@Entity
@Table(name="User")
@Scope("session")
public class User implements UserDetails {

    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Long id;

    @Column(nullable = false, unique = true)
    private String username;

    @JsonProperty(access = Access.WRITE_ONLY)
    private String password;

    private String fullname;

    private String email;

    private Boolean active;

    private Boolean superuser;

    @JsonProperty(access = Access.WRITE_ONLY)
    @OneToMany(mappedBy="projectLead", cascade = CascadeType.PERSIST)
    private Set<Project> projectLeadList;

    @JsonProperty(access = Access.WRITE_ONLY)
    @OneToMany(mappedBy="createdBy", cascade = CascadeType.PERSIST)
    private Set<Project> createdProject;

    @JsonProperty(access = Access.WRITE_ONLY)
    @OneToMany(mappedBy="modifiedBy", cascade = CascadeType.PERSIST)
    private Set<Project> modifiedProject;

    @JsonProperty(access = Access.WRITE_ONLY)
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "UserRole", joinColumns = @JoinColumn(name = "UserId"), inverseJoinColumns = @JoinColumn(name = "RoleId"))
    private Set<Role> roles;

    public User() {}

    public User(Long id, String username, String password, String fullname, 
            String email, Boolean active, Boolean superuser, Set<Role> roles) {
        super();
        this.id = id;
        this.username = username;
        this.password = password;
        this.fullname = fullname;
        this.email = email;
        this.active = active;
        this.superuser = superuser;
        this.roles = roles;
    }

    /* getters and setters */

}

ProjectController.java

package com.app.mycompany.AgileCenterServices.controller;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import com.app.mycompany.AgileCenterServices.entities.Project;
import com.app.mycompany.AgileCenterServices.entities.User;
import com.app.mycompany.AgileCenterServices.services.ProjectService;
import com.app.mycompany.AgileCenterServices.services.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/project")
public class ProjectController {

    public static final Logger logger = LoggerFactory.getLogger(ProjectController.class);

    @Autowired
    ProjectService projectService;

    @Autowired
    UserService userService;

    @CrossOrigin
    @RequestMapping(value="/{id}", method = RequestMethod.PATCH)
    public Project editProject(@RequestBody Map<String, String> project, @PathVariable("id") String id) throws Exception {

        logger.info("Inside editProject() API ");

        if(project == null) {
            return null;
        }

        for(String key: project.keySet()) {
            logger.info("Keys passed for update ==> Key(" + key + "): Value(" + project.get(key) + ")");
        }

        Project projectRec = null;

        try {

            logger.info("updateProject() :: Before save :: ");

            projectRec = projectService.updateProjectInfo(project, id);

            logger.info("updateProject() :: After save :: Saved successfully ::: ", projectRec.toString());
        }
        catch (Exception ex) {
            ex.printStackTrace();
            throw ex;
        }

        logger.info("Leaving editProject() API");

        return projectRec;
    }
}

ProjectService.java (интерфейс)

package com.app.mycompany.AgileCenterServices.services;

import java.util.Map;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import com.app.mycompany.AgileCenterServices.entities.Project;

public interface ProjectService {

    Project updateProjectInfo(Map<String, String> projectRec, String id) throws Exception;

    Project createProject(Project project);

    Project getProjectsByProjectName(String name);

    Page<Project> listProjects(Pageable pageable);

    Page<Project> searchProjects(String searchParam, Pageable pageable);

    Project getProjectsByProjectId(String id);

}

ProjectServiceImpl.java

package com.app.mycompany.AgileCenterServices.services;

import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import com.app.mycompany.AgileCenterServices.entities.Project;
import com.app.mycompany.AgileCenterServices.entities.User;
import com.app.mycompany.AgileCenterServices.repository.ProjectRepository;

@Service
public class ProjectServiceImpl implements ProjectService {

    @Autowired
    ProjectRepository projectRepository;

    private static final String nonUpdateable = "NON_UPDATEABLE";

    private static final Map<String, Set<String>> nonUpdateableKeys = new HashMap<String, Set<String>>();

    static {

        Set<String> attributeNames = new HashSet<String>();
        attributeNames.add("id");
        attributeNames.add("CreatedBy");
        attributeNames.add("CreatedDate");

        nonUpdateableKeys.put(ProjectServiceImpl.nonUpdateable, attributeNames);
    }

    public static final Logger logger = LoggerFactory.getLogger(ProjectServiceImpl.class);

    public Project updateProjectInfo(Map<String, String> projectRec, String id) throws Exception {

        logger.info("Inside updateProjectInfo() API in ProjectServiceImpl");

        Project dbRec = projectRepository.findOne(id);

        if(dbRec == null) {
            throw new Exception("No record found for the given project Id :: " + id);
        }

        logger.info("Record retrieved from db :: " + dbRec.toString());

        setAttributesfromMaptoDBRec(projectRec, dbRec);

        logger.info("DB record after updates :: " + dbRec.toString());

        Project savedRecord = null;

        try {

            logger.info("Before save :: Input Project data ::: ", dbRec.toString());

            savedRecord = projectRepository.save(dbRec);

            logger.info("After save :: Saved successfully ::: ", savedRecord.toString());
        } 

        catch (Exception ex) {
            ex.printStackTrace();
            throw ex;
        }

        logger.info(" Leaving updateProjectInfo() API in ProjectServiceImpl");

        return savedRecord;
    }

    private void setAttributesfromMaptoDBRec(Map<String, String> updatedProjectRecMap, Project updatedProjectRec) {

        logger.info(" Inside setAttributesfromMaptoDBRec() API in ProjectServiceImpl");

        Set<String> nonUpdateableAttributes = ProjectServiceImpl.nonUpdateableKeys.get(ProjectServiceImpl.nonUpdateable);

        if(updatedProjectRecMap == null) {
            logger.warn(" Input Map to service is null in ProjectServiceImpl ===> " + (updatedProjectRecMap == null));
            return;
        }

        for(String key: updatedProjectRecMap.keySet()) {

            logger.info(" Project property ===>  " + key);

            logger.info(" nonUpdateableAttributes contains key  ===> " + nonUpdateableAttributes.contains(key));

            if(!nonUpdateableAttributes.contains(key)) {
                try {

                    if(key != null && "name".equalsIgnoreCase(key)) {
                        updatedProjectRec.setName(updatedProjectRecMap.get(key));
                    }

                    if(key != null && "description".equalsIgnoreCase(key)) {
                        updatedProjectRec.setDescription(updatedProjectRecMap.get(key));
                    }

                    if(key != null && "template".equalsIgnoreCase(key)) {
                        updatedProjectRec.setTemplate(updatedProjectRecMap.get(key));
                    }

                    if(key != null && "projectKey".equalsIgnoreCase(key)) {
                        updatedProjectRec.setProjectKey(updatedProjectRecMap.get(key));
                    }

                    if(key != null && "type".equalsIgnoreCase(key)) {
                        updatedProjectRec.setType(updatedProjectRecMap.get(key));
                    }

                    if(key != null && "category".equalsIgnoreCase(key)) {
                        updatedProjectRec.setCategory(updatedProjectRecMap.get(key));
                    }

                } catch(Exception ex) {
                    logger.error("No key was found or key was null");
                    ex.printStackTrace();
                }

            }
        }


    }

AuditAwareImpl.java

package com.app.mycompany.AgileCenterServices.audit;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.AuditorAware;
import org.springframework.security.core.context.SecurityContextHolder;

import org.springframework.security.core.userdetails.User;

import javax.persistence.EntityManager;

import com.app.mycompany.AgileCenterServices.repository.UserRepository;


public class AuditorAwareImpl implements AuditorAware<com.app.mycompany.AgileCenterServices.entities.User> {

    public static final Logger logger = LoggerFactory.getLogger(AuditorAwareImpl.class);

    @Autowired
    EntityManager entityManager;

    @Override
    public com.app.mycompany.AgileCenterServices.entities.User getCurrentAuditor() {

        logger.info("Inside getCurrentAuditor() API");

        String user = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();

        logger.info("Inside getCurrentAuditor() :: Username :: " + user);

        String userInfoQuery = 
                "select u.id, u.username, u.password, u.superuser, u.fullname, u.email, u.active from User u " + 
                "where upper(u.username) = upper('" + user + "')";

        com.app.mycompany.AgileCenterServices.entities.User userResponse = null;

        try {

//          userResponse = userRepository.findOneByUsername(user);

            userResponse = (com.app.mycompany.AgileCenterServices.entities.User) entityManager.createNativeQuery(userInfoQuery.toString(), com.app.mycompany.AgileCenterServices.entities.User.class)
                                .getSingleResult();

            logger.info("Response after fetching complete userInformation" + userResponse.toString());

        } catch(NoResultException e) {
            System.out.println("No records found");
            e.printStackTrace();
            throw new NoResultException();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return userResponse;

    }

}

Исключение трассировки стека

2019-02-26 13:56:38.878  INFO 12432 --- [http-nio-8080-exec-6] c.a.m.A.controller.ProjectController     : Inside editProject() API 
2019-02-26 13:56:38.879  INFO 12432 --- [http-nio-8080-exec-6] c.a.m.A.controller.ProjectController     : Keys passed for update ==> Key(type): Value(Business)
2019-02-26 13:56:38.879  INFO 12432 --- [http-nio-8080-exec-6] c.a.m.A.controller.ProjectController     : updateProject() :: Before save :: 
2019-02-26 13:56:38.879  INFO 12432 --- [http-nio-8080-exec-6] c.a.m.A.services.ProjectServiceImpl      : Inside updateProjectInfo() API in ProjectServiceImpl
2019-02-26 13:56:38.905  INFO 12432 --- [http-nio-8080-exec-6] c.a.m.A.services.ProjectServiceImpl      : Record retrieved from db :: Project [id=1000, name=Content Server Platform, description= Enterprise document repository, template=Scrum, projectKey=OCCN, type=Software, category=Platform, projectLead=com.app.mycompany.AgileCenterServices.entities.User@76274998, active=true, createdBy=com.app.mycompany.AgileCenterServices.entities.User@76274998, createdDate=2019-02-26 13:54:34.0, modifiedBy=com.app.mycompany.AgileCenterServices.entities.User@76274998, modifiedDate=2019-02-26 13:54:34.0]
2019-02-26 13:56:38.905  INFO 12432 --- [http-nio-8080-exec-6] c.a.m.A.services.ProjectServiceImpl      :  Inside setAttributesfromMaptoDBRec() API in ProjectServiceImpl
2019-02-26 13:56:38.905  INFO 12432 --- [http-nio-8080-exec-6] c.a.m.A.services.ProjectServiceImpl      :  Project property ===>  type
2019-02-26 13:56:38.906  INFO 12432 --- [http-nio-8080-exec-6] c.a.m.A.services.ProjectServiceImpl      :  nonUpdateableAttributes contains key  ===> false
2019-02-26 13:56:38.906  INFO 12432 --- [http-nio-8080-exec-6] c.a.m.A.services.ProjectServiceImpl      : DB record after updates :: Project [id=1000, name=Content Server Platform, description= Enterprise document repository, template=Scrum, projectKey=OCCN, type=Business, category=Platform, projectLead=com.app.mycompany.AgileCenterServices.entities.User@76274998, active=true, createdBy=com.app.mycompany.AgileCenterServices.entities.User@76274998, createdDate=2019-02-26 13:54:34.0, modifiedBy=com.app.mycompany.AgileCenterServices.entities.User@76274998, modifiedDate=2019-02-26 13:54:34.0]
2019-02-26 13:56:38.906  INFO 12432 --- [http-nio-8080-exec-6] c.a.m.A.services.ProjectServiceImpl      : Before save :: Input Project data ::: 
2019-02-26 13:56:38.912  INFO 12432 --- [http-nio-8080-exec-6] c.a.m.A.audit.AuditorAwareImpl           : Inside getCurrentAuditor() API
2019-02-26 13:56:38.922  INFO 12432 --- [http-nio-8080-exec-6] c.a.m.A.audit.AuditorAwareImpl           : Inside getCurrentAuditor() :: Username :: dyoung
c.a.m.A.audit.AuditorAwareImpl           : Inside getCurrentAuditor() :: Username :: dyoung
c.a.m.A.audit.AuditorAwareImpl           : Response after fetching complete userInformationcom.app.mycompany.AgileCenterServices.entities.User@76274998

В дополнительных примечаниях по отладке показано следующее исключение

javax.persistence.PersistenceException: org.hibernate.HibernateException: найдены общие ссылки на коллекцию: com.app.mycompany.AgileCenterServices.entities.User.createdProject в org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert (AbstractEntityManagerImpl.java:1692) в org.hibernate.jpa.spi.AbstractEntityManagerInt.nt.nt.nt.nt.nt.nt.nt.nt1.tmphibernate.jpa.spi.AbstractEntityManagerImpl.convert (AbstractEntityManagerImpl.java:1608) в org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush (AbstractEntityManagerImpl.java:1mpery.j.Imp.Ibe.QuI.Java: 518) в org.hibernate.jpa.internal.QueryImpl.getSingleResult (QueryImpl.java:527) в com.app.mycompany.AgileCenterServices.audit.AuditorAwareImpl.getCurrentAuditor (AuditorAwareImpl.java:49) в com.app.mycompany.AgileCenterServices.audit.AuditorAwareImpl.getCurrentAuditor (AuditorAwareImpl.java:18) 1066 *

...