2017-05-16 4 views
0

Ich arbeite an API Rest Projekt mit Spring. Ich habe einen Dienst "CreateMateriel", die als Parameterdaten JSON nimmt:Deserialize JSON mit Frühling: Ungelöste Vorwärtsreferenzen Jackson Exception

JSON von Materiel Objekt

{ 
    "agence": 1, 
    "code": "001", 
    "type": "MyType" 
} 

"Materiel" hat eine Beziehung Viele zu Eins mit "Agence". Ich habe einen @JsonIdentityInfo Tag Agence des Id zu verwenden und nicht Agence Object (Nach der Besichtigung this topic)

@JsonIdentityInfo(
     generator = ObjectIdGenerators.PropertyGenerator.class, 
     property = "idAgence") 
@JsonIdentityReference(alwaysAsId = true) 
@ManyToOne(fetch = FetchType.LAZY) 
@JoinColumn(name = "agence") 
private Agence agence; 

Aber wenn ich JSON auf POST/materiels senden habe ich diese Ausnahme:

2017-05-16 18:00:53.021 WARN 8080 --- [nio-8080-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Unresolved forward references for: 
at [Source: [email protected]; line: 5, column: 1]Object id [1] (for fr.app.domain.Agence) at [Source: [email protected]; line: 2, column: 14].; nested exception is com.fasterxml.jackson.databind.deser.UnresolvedForwardReference: Unresolved forward references for: 
at [Source: [email protected]; line: 5, column: 1]Object id [1] (for fr.app.domain.Agence) at [Source: [email protected]; line: 2, column: 14]. 

Nach vielen Forschungs Ich habe die Verwendung von ObjectIdResolver in JsonIdentityInfo gesehen ... Aber ich denke, das ist nicht die beste Lösung. Deshalb bitte ich um Ihre Hilfe bei der Erkennung der Ursache des Problems. des Thank

MaterielController.java

package fr.app.controllers; 

import fr.app.domain.Materiel; 
import fr.app.services.MaterielService; 
import org.springframework.web.bind.annotation.*; 

import javax.annotation.Resource; 
import java.util.Collection; 

@RestController @RequestMapping(value = "/materiels") 
public class MaterielController { 
    @Resource 
    private MaterielService materielService; 

    @RequestMapping(method = RequestMethod.POST) 
    public Materiel createMateriel(@RequestBody Materiel materiel) { 
     return this.materielService.createMateriel(materiel); 
    } 

    @RequestMapping(method = RequestMethod.GET) 
    public Collection<Materiel> getAllMateriels() { 
     return this.materielService.getAllMateriels(); 
    } 

    @RequestMapping(value = "/{code}", method = RequestMethod.GET) 
    public Materiel getMaterielByCode(@PathVariable(value = "code") String code) { 
     //find materiel by code 
     return this.materielService.getMaterielByCode(code); 

    } 

    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) 
    public void deleteMateriel(@PathVariable(value = "id") Long id) { 
     this.materielService.deleteMateriel(id); 

    } 

    @RequestMapping(value = "/{id}", method = RequestMethod.PUT) 
    public Materiel updateMateriel(@PathVariable(value = "id") Long id, @RequestBody 
      Materiel materiel) { 

      materiel.setIdMateriel(id); 

     return this.materielService.updateMateriel(materiel); 

    } 
} 

Materiel.java

package fr.app.domain; 

import com.fasterxml.jackson.annotation.*; 

import javax.persistence.*; 

@Entity 
public class Materiel { 
    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long idMateriel; 

    @Column(name = "type_materiel", nullable = false) 
    private String type; 

    @Column(name = "code_materiel", unique = true, nullable = false) 
    private String code; 

    @JsonIdentityInfo(
      generator = ObjectIdGenerators.PropertyGenerator.class, 
      property = "idAgence") 
    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "agence") 
    private Agence agence; 

    public Materiel() { } 

    public Materiel(String type, String code, String dateScan) { 
     this.type = type; 
     this.code = code; 
     this.dateScan = dateScan; 
    } 

    public Long getIdMateriel() { 
     return idMateriel; 
    } 

    public String getType() { 
     return type; 
    } 

    public void setType(String type) { 
     this.type = type; 
    } 

    public String getCode() { 
     return code; 
    } 

    public void setCode(String code) { 
     this.code = code; 
    } 

    public Agence getAgence() { 
     return agence; 
    } 

    public void setAgence(Agence agence) { 
     if(this.agence != null) 
      this.agence.deleteMateriel(this); 
     this.agence = agence; 
     this.agence.addMateriel(this); 
    } 
} 

MaterielService.java

package fr.app.services; 

import fr.app.domain.Materiel; 
import fr.app.repositories.MaterielRepository; 
import org.apache.commons.collections.IteratorUtils; 
import org.springframework.stereotype.Service; 

import javax.annotation.Resource; 
import java.util.Collection; 

@Service(value = "materielService") 
public class MaterielServiceImpl implements MaterielService { 
    @Resource 
    private MaterielRepository materielRepository; 

    ... 

    @Override 
    public Materiel createMateriel(Materiel materiel) { 
     return this.materielRepository.save(materiel); 
    } 

    ... 
} 

Agence.java

package fr.app.domain; 

import com.fasterxml.jackson.annotation.*; 

import javax.persistence.*; 
import java.util.HashSet; 
import java.util.Set; 

@Entity 
public class Agence { 
    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long idAgence; 

    @Column(name = "nom",unique = true, nullable = false) 
    private String nom; 

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "agence") 
    private Set<Materiel> materiels = new HashSet<Materiel>(); 

    public Agence() { } 

    public Agence(String nom) { 
     this.nom = nom; 
    } 

    public Long getIdAgence() { 
     return idAgence; 
    } 

    public String getNom() { 
     return nom; 
    } 

    public void setNom(String nom) { 
     this.nom = nom; 
    } 

    public Set<Materiel> getMateriels() { 
     return materiels; 
    } 

    public void setMateriels(Set<Materiel> materiels) { 
     this.materiels = materiels; 
    } 

    public void addMateriel(Materiel materiel) { 
     this.materiels.add(materiel); 
    } 

    public void deleteMateriel(Materiel materiel) { 
     this.materiels.remove(materiel); 
    } 

} 

pom.xml

<dependencies> 
    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-data-jpa</artifactId> 
    </dependency> 
    <dependency> 
     <groupId>mysql</groupId> 
     <artifactId>mysql-connector-java</artifactId> 
     <version>5.1.6</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-test</artifactId> 
     <scope>test</scope> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-web</artifactId> 
    </dependency> 
    <dependency> 
     <groupId>commons-collections</groupId> 
     <artifactId>commons-collections</artifactId> 
     <version>3.0</version> 
    </dependency> 
    <dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-core</artifactId> 
     <version>5.2.9.Final</version> 
    </dependency> 
    <dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-entitymanager</artifactId> 
     <version>5.2.9.Final</version> 
    </dependency> 
    <dependency> 
     <groupId>io.springfox</groupId> 
     <artifactId>springfox-swagger2</artifactId> 
     <version>2.6.1</version> 
    </dependency> 
    <dependency> 
     <groupId>io.springfox</groupId> 
     <artifactId>springfox-swagger-ui</artifactId> 
     <version>2.2.2</version> 
     <scope>compile</scope> 
    </dependency> 

    <!-- Needed for JSON View --> 
    <dependency> 
     <groupId>com.fasterxml.jackson.core</groupId> 
     <artifactId>jackson-databind</artifactId> 
     <version>2.8.6</version> 
    </dependency> 
</dependencies> 
+0

'Matiriel' hat Verweise auf' Agency' und 'Agency' hat Verweise auf' Matiriel' die führen zu zirkulären Referenzen. Sie müssen '@ JsonIgnore' zu ​​einem von ihnen hinzufügen. – jny

Antwort

1

Ok ich habe das Problem gefunden.

Schließlich muss ich den Resolver hinzufügen.So finde ich here ein Beispiel implementieren ObjectIDResolver:

import com.fasterxml.jackson.annotation.ObjectIdGenerator; 
import com.fasterxml.jackson.annotation.ObjectIdResolver; 

import javax.persistence.EntityManager; 

/** 
* @author fta on 20.12.15. 
*/ 

public class EntityIdResolver 
     implements ObjectIdResolver { 

    private EntityManager entityManager; 

    public EntityIdResolver(
      final EntityManager entityManager) { 

     this.entityManager = entityManager; 

    } 

    @Override 
    public void bindItem(
      final ObjectIdGenerator.IdKey id, 
      final Object pojo) { 

    } 

    @Override 
    public Object resolveId(final ObjectIdGenerator.IdKey id) { 

     return this.entityManager.find(id.scope, id.key); 
    } 

    @Override 
    public ObjectIdResolver newForDeserialization(final Object context) { 

     return this; 
    } 

    @Override 
    public boolean canUseFor(final ObjectIdResolver resolverType) { 

     return false; 
    } 

} 

Und nächstes füge ich in @JsonIdentityInfo:

@Entity 
@JsonIdentityInfo(
     generator = ObjectIdGenerators.PropertyGenerator.class, 
     property = "idAgence", 
     resolver = EntityIdResolver.class, 
     scope=Agence.class) 
public class Agence { 
    // ... 
} 
0

Sie haben zwei Domains: Materiel und Agence. Als Jackson Material zu deserialisieren versucht, nennt er die Methode getAgence. Diese Methode gibt ein Agence-Objekt zurück, das auch von Jackson deserialisiert wird. Wenn Jackson die Methode getMeteriels (von Agence) aufruft, gibt sie eine Materialmenge zurück, die ebenfalls deserialisiert wird. Das Problem ist, dass Jackson versuchen wird, jedes Material im Set wieder zu deserialisieren. Eine gleich so Frage here.

0

Für Ihre letzte Ausnahme (utf-8), können Sie die Unterschrift des Controllers wie unten ändern:

@RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) 
public ResponseEntity<Bean> create(HttpServletRequest request) throws IOException { 
    // getting the posted value 
    String body = CharStreams.toString(request.getReader()); 
    Bean bean = new ObjectMapper().readValue(body, service.getBeanClass()); 
    bean.setId(null); 
    Bean saved = service.save(bean); 

    URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(saved.getId()).toUri(); 
    return ResponseEntity.created(location).body(saved); 
} 

Mit dieser Lösung haben Sie die gesamte Kontrolle über die Zuordnung.

Enjoy

Verwandte Themen