0

I came across a org.hibernate.LazyInitializationException which the cause is very well explained in this question. My code has, I think, the same problem as in the question referenced in the link. Here's the code:

Contract class:

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor

public class Contract implements Serializable {

    @Id
    @Column(name = "uuid")
    private UUID uuid;

    @Column(name = "settlement_date")
    private LocalDate settlementDate;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "contract")
    private Set<ContractFile> files;
    
}

ContractFile class:

@Entity
@NoArgsConstructor
@Getter
@Setter
public class ContractFile implements Serializable {

    @Id
    @Column(name = "uuid")
    private UUID uuid;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "contract_uuid")
    private Contract contract;

    public ContractFile(final Contract contract) {
        uuid = UUID.randomUUID();
        this.contract = contract;
    }

}

And here's where I'm having trouble. This is a DTO, in which I need to get a settlementDate from a Contract by retrieving the Contract from a ContractFile object.

@Getter
public class DTO {
    private String reference;

    public DTO(final ContractFile contractFile) {
        final Contract contract = contractFile.getContract();
        this.reference = "reference_" + receivable.getSettlementDate();
    }
}

It is my understandment that I'm getting org.hibernate.LazyInitializationException on the file this.reference = "reference_" + receivable.getSettlementDate(); because the Contract objects wasn't properly initialized to begin with, due to the lack of a Spring Context. If this assumption is correct, how do I make it so that when I do contractFile.getContract() inside this DTO I have proper proxy opened? I've seen many explanation as to why my code won't work and the environment in which it should, but no sample code and I cannot think of how accomplish this without having a repository object retrieving the contract directly from database.

EDIT 1 This is how DTO is used, in another class, responsible for endpoint communication:

public String report(Set<DTO> dto) {
        return executePost(properties.getUri(), new DTOList(dto), String.class);
    }

EDIT 2 What did the trick for me was to set @Transactional to the method that gets the ContractFiles from database.

//this transactional opens the context for all
//methods called from reportAll.
@Transactional 
public void reportAll() {
    final Set<ContractFile> contracts = contractFileService
            .findAll();
    contracts.forEach(this::report);
}


public void report(final ContractFile contractFile) {
    final String response = infraService.report(contractFile);
    log.info("Reported successfully");
}
Pelicer
  • 1,348
  • 4
  • 23
  • 54
  • Where is the DTO used? – Vipulkumar Gorasiya Jun 14 '21 at 11:45
  • In another class, responsible for endpoint communication, as editted in the question. – Pelicer Jun 14 '21 at 11:48
  • `LazyInitializationException` occurs if you try to access `Lazy` property at a place where the property can not be retrieved( i.e. no Session available to retrieve the property by asking DB/Persistence). Your problem should be solved by using 'OpenSessionInViewFilter'. How this should be done would depend on your project setup. – Vipulkumar Gorasiya Jun 14 '21 at 12:00

2 Answers2

1

This is happening because there is no Transaction opened in DTO(object become detached). Wherever you have fetched the object from the DB call contractFile.getContract() so that the ORM framework loads the lazy-loaded object.

1

As the linked question explains the error is most probably because you fetch the instance of ContractEntity in a separate transaction and while contract is not needed it is not populated because lazy.

You can still force contract to be populated if not willing to use eager but you need to do it in a context of the transaction where entity is fetched.

You might have a code that retrieves this entity and finally passes it to a DTO constructor:

ContractFile cf = repo.findById(...);
// tx ends
...
DTO dto = new DTO(cf); // fails

Now, before tx ends ofr right after repo call invoke contract getter, like

ContractFile cf = repo.findById(...);
Contract contract = contractFile.getContract();
// maybe do something else with it
// tx ends
DTO dto = new DTO(cf); // should now work    

However, first of all you might need to check the flow of your code because it might be able to be refactoer inside same transaction to avoid this kind of a hack.

pirho
  • 11,565
  • 12
  • 43
  • 70