1

I am workin with Spring Boot 1.5.10, Spring Data JPA and Hibernate.

When I search by Id the entity Person the result is correct, but when I try to build a query with a List my request return a exception:

Failed to write HTTP message: 
org.springframework.http.converter.HttpMessageNotWritableException: 
Could not write JSON: failed to lazily initialize a collection of role: us.icitap.entities.tims.Person.otherNames, could not initialize proxy - no Session; 
nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: us.icitap.entities.tims.Person.otherNames, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->us.icitap.entities.tims.Person["otherNames"])

The code of the entity I am working on:

@Entity
@Table(name="PERSON", schema = "TIMS")
@NamedQuery(name="Person.findAll", query="SELECT p FROM Person p")
public class Person extends PersonAbstract {

    private static final long serialVersionUID = 1L;

    //bi-directional many-to-one association to PersonOtherName
    @OneToMany(mappedBy="person")
    private List<PersonOtherName> otherNames;

    //bi-directional many-to-one association to Photo
    @OneToMany(mappedBy="person")
    private List<Photo> photos;

    //bi-directional many-to-one association to TravelDocument
    @OneToMany(mappedBy="person")
    private List<TravelDocument> travelDocuments;

    public Person() {
    }

    public List<PersonOtherName> getOtherNames() {
        return this.otherNames;
    }

    public void setOtherNames(List<PersonOtherName> otherNames) {
        this.otherNames = otherNames;
    }

    public PersonOtherName addOtherName(PersonOtherName otherName) {
        getOtherNames().add(otherName);
        otherName.setPerson(this);
        return otherName;
    }

    public PersonOtherName removeOtherName(PersonOtherName otherName) {
        getOtherNames().remove(otherName);
        otherName.setPerson(null);
        return otherName;
    }

    public List<Photo> getPhotos() {
        return this.photos;
    }

    public void setPhotos(List<Photo> photos) {
        this.photos = photos;
    }

    public Photo addPhoto(Photo photo) {
        getPhotos().add(photo);
        photo.setPerson(this);
        return photo;
    }

    public Photo removePhoto(Photo photo) {
        getPhotos().remove(photo);
        photo.setPerson(null);
        return photo;
    }

    public List<TravelDocument> getTravelDocuments() {
        return this.travelDocuments;
    }

    public void setTravelDocuments(List<TravelDocument> travelDocuments) {
        this.travelDocuments = travelDocuments;
    }

    public TravelDocument addTravelDocument(TravelDocument travelDocument) {
        getTravelDocuments().add(travelDocument);
        travelDocument.setPerson(this);
        return travelDocument;
    }

    public TravelDocument removeTravelDocument(TravelDocument travelDocument) {
        getTravelDocuments().remove(travelDocument);
        travelDocument.setPerson(null);
        return travelDocument;
    }
}

The relevant part of the service:

@SuppressWarnings("unchecked")
@Override
public List<Person> searchByExpression(List<Criterion> expressions) {
    Session session = entityManager.unwrap(Session.class);
    List<Person> persons = null;
    try {
        Criteria criteria = session.createCriteria(Person.class);
        for (Criterion simpleExpression : expressions) {
            criteria.add(simpleExpression);
        }
        persons = criteria.list();                      
    }catch (Exception e) {
        e.printStackTrace();            
    }
    session.close();
    return persons;
}

@Override
public Person searchPersonById(Long id) {       
    return personRepository.findOne(id);
}

The controller:

@RestController
@RequestMapping("/tims/person")
public class PersonController {

    @Autowired
    private PersonService personService;

    @RequestMapping(value="/searchPersonById/{id}", method = RequestMethod.GET)
    public ResponseEntity<Person> searchById(@PathVariable("id") Long id) {
        try {
            Person person = this.personService.searchPersonById(id);
            if (person == null)
                return new ResponseEntity<Person>(null, null, HttpStatus.NOT_FOUND);
            else 
                return new ResponseEntity<Person>(person, null, HttpStatus.OK);
        }catch(Exception e){
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.set("Exception", e.getMessage());
            ResponseEntity<Person> respond = new ResponseEntity<Person>(null, httpHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
            return respond;
        }
    }

    @RequestMapping("/searchPersonByGenerality")
    public ResponseEntity<List<Person>> searchPersonByGenerality(String pid, String name, String surname, GenderEnum gender, String dateOfBirth){
        List<Person> persons = null;
        Date date = null;
        try {
            if(dateOfBirth != null) {
                SimpleDateFormat df = new SimpleDateFormat("dd.MM.yyyy");
                date = df.parse(dateOfBirth);
            }
        }catch (Exception e) {
            System.err.println(e.getMessage());
        }

        try {
            if (pid != null && !pid.isEmpty()) {
                persons = this.personService.searchPersons(pid, name, surname, gender, date);
                return new ResponseEntity<List<Person>>(persons, null, HttpStatus.OK);
            }
            int valid = 0;
            List<Criterion> expressions = new ArrayList<>();
            if(name != null & !name.isEmpty()) {
                name = name.toUpperCase();
                valid = valid + 5;
                if (name.contains("%") || name.contains("_")) {                 
                    expressions.add(Restrictions.sqlRestriction("translate({alias}.name, 'ËÇ', 'EC') like '" + MyString.transformAccentsLetter(name) + "'"));
                }else 
                    expressions.add(Restrictions.sqlRestriction("translate({alias}.name, 'ËÇ', 'EC') = '" + MyString.transformAccentsLetter(name) + "'"));
            }
            if(surname != null & !surname.isEmpty()) {
                surname = surname.toUpperCase();
                valid = valid + 5;
                if (surname.contains("%") || surname.contains("_")) {
                    expressions.add(Restrictions.sqlRestriction("translate({alias}.surname, 'ËÇ', 'EC') like '" + MyString.transformAccentsLetter(surname) + "'"));
                }else 
                    expressions.add(Restrictions.sqlRestriction("translate({alias}.surname, 'ËÇ', 'EC') = '" + MyString.transformAccentsLetter(surname) + "'"));
            }
            if (gender != null) {
                valid = valid + 2;
                expressions.add(Restrictions.eq("gender", gender));
            }
            if (date != null) {
                valid = valid + 3;
                expressions.add(Restrictions.between("dateOfBirth", atStartOfDay(date), atEndOfDay(date)));
            }
            persons = personService.searchByExpression(expressions);
            return new ResponseEntity<List<Person>>(persons, null, HttpStatus.OK);
        } catch (Exception e) {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.set("Exception", e.getMessage());
            ResponseEntity<List<Person>> respond = new ResponseEntity<List<Person>>(null, httpHeaders,
                    HttpStatus.INTERNAL_SERVER_ERROR);
            return respond;
        }
    }
}

Can anyone help me find what's going wrong in my code?

lcnicolau
  • 3,252
  • 4
  • 36
  • 53
  • Your methods are way to crowded. There is too much going on to understand and spot errors for sure. Try to break down your code in smaller methods. – avi.elkharrat Jun 01 '18 at 14:41
  • Did you find the solution? Do you need any more help with this? If any answer has solved your question please consider [accepting it](https://meta.stackexchange.com/a/5235). This indicates to the wider community that you've found a solution and gives some reputation to both the answerer and yourself. – lcnicolau Jul 10 '18 at 00:15

2 Answers2

3

That is the default strategy to retrieve data from the database in a @OneToMany relationship, data should be fetched lazily when it is first accessed, and in this case, you are trying to serialize the entity after closing the session. Try to establish an EAGER fetch strategy for this property (take a look at this for details):

@OneToMany(fetch = FetchType.EAGER, mappedBy="person")
private List<PersonOtherName> otherNames;

Alternatively, you can force the proxy initialization for this specific service before closing the session:

Hibernate.initialize(person.getOtherNames());

If you don't need to show this data in the interface, a more efficient solution would be not even serialize them:

@JsonIgnore
@OneToMany(mappedBy="person")
private List<PersonOtherName> otherNames;
lcnicolau
  • 3,252
  • 4
  • 36
  • 53
0

Check out your exception, it says: could not initialize proxy - no Session. That means that your session is not properly intialized.

Try to debug your app and see what happens when you run

Session session = entityManager.unwrap(Session.class);
avi.elkharrat
  • 6,100
  • 6
  • 41
  • 47