15

I've got two classes Participant and TimeWindow. Multiple participants can register for multiple TimeWindow, hence the ManyToMany relation

@Entity
@Table
public class Participant {
    @Id
    @SequenceGenerator(
            name = "participant_sequence",
            sequenceName = "particant_sequence",
            allocationSize = 1
    )
    @GeneratedValue(
            strategy = GenerationType.SEQUENCE,
            generator = "participant_sequence"
    )
    private Long id;
    private String name;
    private String number;
    private String details;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "ParticipantCreneaux")
    private Collection<TimeWindow> registeredTimeWindow;


    public Participant() {

    }
    public Participant(String nom, String num, String details) {
        this.name = nom;
        this.number = num;
        this.details = details;
        this.registeredTimeWindow = new ArrayList<>();
    }
    public void addTimeWindow(TimeWindow c){
        registeredTimeWindow.add(c);
    }
    public void removeTimeWindow(TimeWindow c){
        registeredTimeWindow.remove(c);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public String getDetails() {
        return details;
    }

    public void setDetails(String details) {
        this.details = details;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }

    public Collection<TimeWindow> getRegisteredTimeWindow() {
        return registeredTimeWindow;
    }
}

And the TimeWindow class:

@Entity
@Table
public class TimeWindow {
    @Id
    @SequenceGenerator(
            name = "creneau_sequence",
            sequenceName = "creneau_sequence",
            allocationSize = 1
    )
    @GeneratedValue(
            strategy = GenerationType.SEQUENCE,
            generator = "creneau_sequence"
    )
    private Long id;
    private LocalTime hourStart;
    private LocalTime hourEnd;

    public Collection<Participant> getListParticipants() {
        return listParticipants;
    }

    @ManyToMany(fetch = FetchType.LAZY,cascade=CascadeType.ALL,mappedBy = "registeredTimeWindow")
    private Collection<Participant> listParticipants;

    public TimeWindow(LocalTime hourStart, LocalTime hourEnd) {
        this.hourStart = hourStart;
        this.hourEnd = hourEnd;
        this.listParticipants = new ArrayList<>();
    }

    public TimeWindow() { }

    public LocalTime getHourEnd() {
        return hourEnd;
    }

    public void setHourStart(LocalTime hourStart) {
        this.hourStart = hourStart;
    }

    public void setHourEnd(LocalTime hourEnd) {
        this.hourEnd = hourEnd;
    }

    public LocalTime getHourStart() {
        return hourStart;
    }

    public int getNbParticipants(){
        return listParticipants.size();
    }
    public void addParticipant(Participant participant){
        this.listParticipants.add(participant);
    }
    public void removeParticipant(Participant participant){
        this.listParticipants.remove(participant);
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }
}

Right now I'm still learning Spring boot, I haven't found anything about it so far or anything that helped me.

The error is when I summon my participant's TimeWindow Collection that i've gotten through the DataBase in the Config class. In the debugger my Participant looks like this

id:123
name:"hisName"
number:"321"
details:"some details"
registeredTimeWindow:{PersistentBag@10927}Unable to evaluate the expression Method threw 'org.hibernate.LazyInitializationException' exception.

At first i thought it was normal because of the Lazy option, i had to invoke the array through the getter, however it is wrong and the getter give me the exact same object.

FetchType.EAGER works fine, however I can't afford to do it. I've tried to get some help from someone a bit more experienced than I am, but without success. It should be possible to work around that issue within the JPA Repositories, but it feels like such a waste not to be able to use the getter.

Skysenzz
  • 151
  • 1
  • 1
  • 5
  • I assume your `spring.jpa.open-in-view` configuration property is set to false? I also wonder if the relation is loaded successfully in the response, or if it also fails? – Serg Vasylchak Apr 21 '21 at 14:27
  • Does this answer your question? [How to fix org.hibernate.LazyInitializationException - could not initialize proxy - no Session](https://stackoverflow.com/questions/21574236/how-to-fix-org-hibernate-lazyinitializationexception-could-not-initialize-prox) – crizzis Apr 21 '21 at 15:28

2 Answers2

24

You are trying to use lazy data after closing a transaction, and yes, one of the ways is usage EAGER. Another way - usage @Transactional on a method that using this data.

Andres Herrero
  • 281
  • 1
  • 5
  • 1
    The @Transactional is from org.springframework.transaction.annotation.Transactional; – mnagdev Jan 10 '23 at 08:57
  • @mnagdev thank you, I had to search way to long to get this clarification. there is a jakarta one with the same annotation name and I wasn't sure. – fudge Feb 17 '23 at 22:21
13

I was getting this error:

Method threw 'org.hibernate.LazyInitializationException' exception.

This is because currently there is no session present. Hibernate opens a session and closes it, but for "lazy = true" or "fetch = FetchType.LAZY" such fields are populated by proxies. When you try to find the value for such a field, it will attempt to go to the database using the active session to retrieve the data. If no such session can be found, you get this exception.

You can fix it using "lazy=false" or check whether you have used @Transcational properly (try to use this in your service layer than your data access layer), you can also use

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)

OR

@Transactional

CodingBee
  • 1,011
  • 11
  • 8