0

There are two problems that I do not understand. First, the error message on the console. It does not give me the whole error message. Therefore I do not understand the issue at all :S The IDE is STS.

enter image description here

Second, why do I get this error, "JsonMapping failed to lazily initialize a..."

@Test
public void testUpdateCar() throws Exception {
    Car car = carRepository.findById(new Long(1)).get();
    car.setBrand("Mazda");
    car.setModel("326");

    String putJSON = objectMapper.writeValueAsString(car);
    mockMVC.perform(MockMvcRequestBuilders.put(String.format("/api/cars/%d", car.getId())).contentType(MediaType.APPLICATION_JSON_UTF8).content(putJSON))
    .andDo(MockMvcResultHandlers.print())
    .andExpect(MockMvcResultMatchers.status().isCreated())
    .andExpect(MockMvcResultMatchers.content().contentType("application/hal+json;charset=UTF-8"));
}

Car:

@Entity
public class Car {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @OneToOne
    private User owner;
    private String brand;
    private String model;
    private String color;
    private String plate;
    private String additionals;

Update 1: The error itself:

com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: me.eraytuncer.carpool.entity.User.carList, could not initialize proxy - no Session (through reference chain: me.eraytuncer.carpool.entity.Car["owner"]->me.eraytuncer.carpool.entity.User["carList"])

Update 2:

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String firstName;
    private String lastName;
    private String phone;
    private String email;

    @OneToMany(cascade = CascadeType.REMOVE, mappedBy = "owner")
    private List<Car> carList;

Update 3:

@PutMapping("/cars/{id}")
ResponseEntity<?> replaceCar(@RequestBody Car newCar, @PathVariable Long id) {
    if (repository.existsById(id)) {
        newCar.setId(id);
        Resource<Car> carResource = assembler.toResource(repository.save(newCar));
        try {
            return ResponseEntity
                    .created(new URI(carResource.getId().expand().getHref()))
                    .body(carResource);
        } catch (URISyntaxException e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }                   
    } else {
        throw new CarNotFoundException(id);
    }
}

Update 4:

Adding @JsonIgnore solved the issue somehow. Maybe it was a misleading issue caused by infinite recursion?

Eray Tuncer
  • 707
  • 2
  • 11
  • 31

3 Answers3

1

Looks like field

private List<Car> carList;

is resolved lazily (default fetch type in @OneToMany), which means it is populated from DB only when getter for this field is called. In your case it is called by Jackson serializer outside scope of the Hibernate session and property cannot be populated. Changing fetch type to EAGER in @OneToMany on carList property should help.

Also consider using DTO pattern, because returning entities from API is considered as bad practice.

akoz
  • 371
  • 3
  • 14
  • Did not help. Could you please check my Update 4? – Eray Tuncer Aug 09 '19 at 14:17
  • 1
    Update 4 seems to confirm my explanation: `@JsonIgnore` makes Jackson ignore this field thus getter is not called and car list is not fetched from DB. Depends what you want - do you want this field to be return from your API? Also @Maciej Kowalski answer is valid - you should use @ManyToOne in Car entity, but this is not the source of your problem. – akoz Aug 09 '19 at 16:51
  • I would not need @JsonIgnore, If I used DTO pattern already. I see now ;) – Eray Tuncer Aug 10 '19 at 11:15
0

You have mapped @OneToOne against @OneToMany. It should be @ManyToOne on the owning side:

    @ManyToOne
    @JoinColumn(name = "user_id") // if needed
    private User owner;
Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63
0

I faced the same issue and it got resolved by using @Transactional on the method. @Transactional help to keep open the session so that lazy collection could be fetched.