Search code examples
spring-bootlucenehibernate-searchlazy-initialization

Can't find a solution to lazy initialization exception with hibernate search


I am running a search with hibernate search in the Spring Boot environment but when executing the query I get failed to lazily initialize a collection of role.

I have tried all the variants exposed in the other similar questions. My entity has a many to many relationship with two others.

@Entity
@Indexed
public class A extends AbstractAuditableEntity<D, Long> implements 
Serializable {

  @Column
  @Field(index = Index.YES, analyze = Analyze.YES, store = 
       Store.NO)
  private String name;

  @ManyToMany(mappedBy = "a")
  @IndexedEmbedded
  private List<B> b;

  @OneToMany(mappedBy = "a")
  @IndexedEmbedded
  private List<C> c;

}

My controller class:

@RestController
public class SearchController {

@Autowired
private SearchService searchService;

@GetMapping("/search")
public ResponseEntity<?> search(@RequestParam String terms){

    List<A> aList = searchService.search(terms);

    return aList.isEmpty() ? new ResponseEntity<> 
            (HttpStatus.NO_CONTENT) : new ResponseEntity<>(aList, 
            HttpStatus.OK);
}

}

My service class:

@Service
public class SearchService {

@Autowired
private aRepository aRepository;

@Transactional
public List<A> search(String terms) {
    return aRepository.search(terms);
}

}

My repository layer:

public interface ARepository extends JpaRepository<A, Long>, 
      IndexSearchARepository {  
}

Extended interface:

@Transactional
public interface IndexSearchARepository {
   public List<A> search(String terms);
}

And the good part, the implementation:

public class IndexSearchARepositoryImpl implements 
      IndexSearchARepository {

private final EntityManager entityManager;

@Autowired
public IndexSearchARepositoryImpl(EntityManager entityManager) 
    {
    this.entityManager = entityManager;
}

@Override
@Transactional
public List<A> search(String terms) {

   FullTextEntityManager fullTextEntityManager = 
          Search.getFullTextEntityManager(entityManager);

   QueryBuilder queryBuilder = 
       fullTextEntityManager.getSearchFactory().buildQueryBuilder()
            .forEntity(A.class).get();

   Query luceneQuery = queryBuilder
              .keyword()
              .fuzzy()
              .withEditDistanceUpTo(1)
              .withPrefixLength(1)
      .onFields("name")
      .matching(terms)
              .createQuery();

    javax.persistence.Query jpaQuery = fullTextEntityManager
             .createFullTextQuery(luceneQuery, A.class);

    List<A> aList = null;

    try {
      aList = jpaQuery.getResultList();
    } catch (NoResultException nre) {
      log.debug(nre.getMessage());
    }

     return aList;
}

}

Highlight that if I put a breakpoint and go step by step it does not throw the exception. I do not know when try to access the relationship that apparently the session closes before, @Transactional should not handle this?

Output:

"message": "Could not write JSON: failed to lazily initialize a 
collection of role: com.xxxx.entity.A.b, 
could not initialize proxy - no Session

Solution

  • Hibernate Session exists within method with @Transactional.
    Passing entity outside Service class is a bad practise because session is being closed after leaving your search method. On the other hand your entity contains lazy initialised collections, which cannot be pulled once session is closed.

    The good practise is to map entity onto transport object and return those transport objects outside (not raw entities).