简体   繁体   中英

HIbernate - "could not initialize proxy - no Session" error when trying to fetch data

I am using spring boot and hibernate with MySql and I am trying to figure out the best way to handle the error

nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: "some class field" could not initialize proxy - no Session".

I saw couple of solutions but I couldn't make them work, and also I do not understand the repercussions of implementing them.

I have the following Entities:

@Entity
@Table(name="machine_groups_to_versions")
@Getter
@Setter
//@JsonIgnoreProperties(value= {"machineGroup", "version"})
public class MachineGroupToVersion {
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO, generator = "machine_groups_to_versions_seq")
    @SequenceGenerator(name = "machine_groups_to_versions_seq", allocationSize = 1)
    @Column(name = "id")
    private long id;
    @ManyToOne
    @JoinColumn(name = "machine_group_id", nullable = false)
    private MachineGroup machineGroup;
    @ManyToOne
    @JoinColumn(name = "version_id", nullable = false)
    private Version version;
    @Column(name = "state")
    private String state;
    @Column(name = "tested_time")
    private Date testedTime;
    @Column(name = "creation_time")
    private Date creationTime;
}
@Entity
@Table(name="versions")
@Getter
@Setter
public class Version {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "versions_seq")
    @SequenceGenerator(name = "versions_seq", allocationSize = 1)
    @Column(name = "id")
    private long id;
    @Column(name = "name")
    private String name;
    @Column(name = "creation_time")
    private Date creationTime;
    @Column(name = "exe_file")
    @Lob
    private Blob exeFile;
    @ManyToMany(mappedBy = "versions", cascade = CascadeType.MERGE)
    private Set<MachineGroup> machineGroups = new HashSet<>();
}
@Entity
@Table(name="machine_groups")
@Getter
@Setter
@AllArgsConstructor(access = AccessLevel.PUBLIC)
@NoArgsConstructor
public class MachineGroup {
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO, generator = "machine_groups_seq")
    @SequenceGenerator(name = "machine_groups_seq", allocationSize = 1, initialValue = 2)
    @Column(name = "id")
    private long id;
    @Column(name = "name")
    private String name;
    @Column(name = "creation_time")
    private Date creationTime;
    @Column(name = "is_official")
    private boolean official;
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "machine_properties_id", nullable = false)
    private ContinuousIntegrationProperties defaultContinuousIntegrationProperties;
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "machine_groups_to_users",
            joinColumns = @JoinColumn(name = "machine_group_id"),
            inverseJoinColumns = @JoinColumn(name = "user_id"))
    private Set<User> users = new HashSet<>();
    @ManyToMany(cascade = CascadeType.MERGE)
    @JoinTable(name = "machine_groups_to_versions",
            joinColumns = @JoinColumn(name = "machine_group_id"),
            inverseJoinColumns = @JoinColumn(name = "version_id"))
    private Set<Version> versions = new HashSet<>();
}

My Controller:

   @GetMapping("/getByMachineGroupName/{machineGroupName}")
    public ResponseEntity<List<MachineGroupToVersionDTO>> getAllVersions(@PathVariable String machineGroupName) {
        logger.info("Incoming GetMachineGroupToVersionReport Request. Machine Group Name: {}", machineGroupName);

        List<MachineGroupToVersionDTO> omgtv = machineGroupToVersionService.getByMachineGroupName(machineGroupName).stream()
     .map(this::convertToDto).collect(Collectors.toList());

        return new ResponseEntity<>(omgtv, HttpStatus.OK);
    }

When I debug I can see that my omgtv has all the data I need but for some reason it is unable to return it.

As you can see in my MachineGroupToVersion class I extract MachineGroup and Version, the issue I see is:

  1. MachineGroup is referencing a set of versions and each version references a set of MachineGroup
  2. Same for the Version class

Looks like a cyclic issue with how the tables are created. I have tried to use the @JsonIgnoreProperties annotation but it just removes it from the response which is not the desired outcome.

I see the trace that shows what the error is but how can I fix this?

through reference chain: java.util.ArrayList[0]->entities.machinegrouptoversion.MachineGroupToVersionDTO[\"machineGroup\"]->entities.machinegroup.MachineGroup[\"users\"]->org.hibernate.collection.internal.PersistentSet[0]->entities.user.User[\"machineGroups\"]

I don't want to make everything eager unless I specifically call the get function.

How can I resolve this without changing the structure of my classes and DB?

UPDATE:

Service

  • List item
 @Transactional(transactionManager = "primaryTransactionManager", propagation= Propagation.REQUIRED, readOnly=true, noRollbackFor=Exception.class)
    public List<MachineGroupToVersionDTO> getByMachineGroupName(String mgName) {
        List<MachineGroupToVersionDTO> mgtvl = new ArrayList<>();
        Optional<MachineGroup> mg = machineGroupService.getByName(mgName);
        if(mg.isPresent())
            mgtvl = machineGroupToVersionRepository.findByMachineGroup(mg.get()).stream()
                    .map(this::convertToDto).collect(Collectors.toList());
        return mgtvl;
    }

    private MachineGroupToVersionDTO convertToDto(MachineGroupToVersion mgtv) {
        MachineGroupToVersionDTO machineGroupToVersionDTO = new MachineGroupToVersionDTO();
        machineGroupToVersionDTO.setMachineGroup(mgtv.getMachineGroup());
        machineGroupToVersionDTO.setVersion(mgtv.getVersion());
        machineGroupToVersionDTO.setCreationTime(mgtv.getCreationTime());
        machineGroupToVersionDTO.setState(mgtv.getState());
        machineGroupToVersionDTO.setTestedTime(mgtv.getTestedTime());
        return machineGroupToVersionDTO;

    }

Thank to @Shadov I was able to find the issue. In my case the issue was a mistake made by me when creating the DTO.

I mistakenly created the DTO with fields which are Entities and not other DTOs. Once I changed them from entities to DTOs I was able to get the response I needed.

Changed from this:

public class MachineGroupToVersionDTO {
    private MachineGroup machineGroup;
    private Version version;
    private String state;
    private Date testedTime;
    private Date creationTime;
}

To this:

public class MachineGroupToVersionDTO {
    private MachineGroupSimpleDTO machineGroup;
    private VersionDTO version;
    private String state;
    private Date testedTime;
    private Date creationTime;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM