1

I have an odd error with a spring boot controller, not returning a recently created object.

I have a controller, with 2 methods (see below). One simply retrieves an Object of the class "OrderPay" and returns it as the payload of a response entity. This works fine, the object is therefore okay.

The other one creates and persists a new instance of "OrderPay" and is then supposed to return that newly created object. The creation of the new object and its persistence work fine. However, when I try to return it, I get the error message below.

Now I would understand that error message if it occured consistently. However, when returning this newly created object using the first function ("getPaymentByIdTest"), it returns it without problems, even though I retrieve it in the exact same way from the database and return it in the same way, with the same return type of the method.

Now I know that executing the code in a HTTP-GET method is not best practise, however it is quicker and more convenient for testing.

Can anyone see where I need to adjust the code?

2020-04-13 21:37:57.507 ERROR 26796 --- [nio-8081-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.brownbag_api.model.OrderPay["posSend"]->com.brownbag_api.model.Pos$HibernateProxy$7l7MDMEi["hibernateLazyInitializer"])] with root cause

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.brownbag_api.model.OrderPay["posSend"]->com.brownbag_api.model.Pos$HibernateProxy$7l7MDMEi["hibernateLazyInitializer"])

  1. The Controller
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/pay")
public class PaymentController {

    @Autowired
    private OrderPayRepo orderPayRepo;

    @Autowired
    private OrderPaySvc orderPaySvc;

    @GetMapping("/{id}")
    public ResponseEntity<?> getPaymentByIdTest(@PathVariable Long id) {
        Optional<OrderPay> orderPay = orderPayRepo.findById(id);
        return ResponseEntity.ok(orderPay);
    }

    @GetMapping("/exec/from/{from}/to/{to}/amount/{amount}")
    public ResponseEntity<?> execPayment(@PathVariable Long from, @PathVariable Long to, @PathVariable double amount) {
        Pos posFrom = posRepo.getOne(from);
        Pos posTo = posRepo.getOne(to);
        OrderPay pay = orderPaySvc.createPay(amount, posFrom, posTo);
        pay = orderPaySvc.execPay(pay);

        if (pay == null) {
            return ResponseEntity.ok("Payment could not be executed. Please see log for more details!");
        } else {
            System.err.println("Payment executed: " + pay.getPosRcv().getParty().getName());
            Long payId = pay.getId();
            System.err.println("Payment executed: " + payId);
            // payId returns the expected value here, the object is therefore saved in the database (verified).
            Optional<OrderPay> orderPay = orderPayRepo.findById(payId);
            return ResponseEntity.ok(pay);
        }

    }
}
  1. Order.java
@Entity
@Table(name = "order_base")
@Inheritance(strategy = InheritanceType.JOINED)
public class Order implements Serializable {

    private static final long serialVersionUID = -3458221490393509305L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    @NotNull
    @Column(name = "QTY")
    private double qty;

    public Order() {
    }

    public Order(@NotNull double qty) {
        super();
        this.qty = qty;
    }
}
  1. OrderPay
@Entity
@Table(name = "order_pay")
public class OrderPay extends Order implements Serializable {

    private static final long serialVersionUID = 4643589803146964779L;

    @NotNull
    @OneToOne(targetEntity = Pos.class)
    @JoinColumn(name = "POS_SEND_ID")
    private Pos posSend;

    @NotNull
    @OneToOne(targetEntity = Pos.class)
    @JoinColumn(name = "POS_RCV_ID")
    private Pos posRcv;

    public OrderPay() {
        super();
    }

    public OrderPay(@NotNull double qty, @NotNull Pos posSend, @NotNull Pos posRcv) {

        super(qty);
        this.posSend = posSend;
        this.posRcv = posRcv;

    }
}
  1. Pos.java
@Entity
@Table(name = "POS")
public class Pos implements Serializable {

    private static final long serialVersionUID = 1530699992135610397L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    @NotNull
    @Column(name = "QTY")
    private double qty;

    @NotNull
    @ManyToOne(targetEntity = Party.class)
    @JoinColumn(name = "PARTY_ID")
    @JsonBackReference
    private Party party;

    public Pos() {
    }

    public Pos(@NotNull double qty, @NotNull Party owner) {
        super();
        this.qty = qty;
        this.party = owner;
    }
}
  1. JSON
{
"id":7,
"qty":33000.0,
"posSend":
  {
  "id":1,
  "qty":-266010.0,
  "hibernateLazyInitializer":{}
  },
"posRcv":
  {
  "id":2,
  "qty":66000.0,
  "hibernateLazyInitializer":{}
  }
}
Community
  • 1
  • 1
Daniel Methner
  • 509
  • 1
  • 6
  • 18

1 Answers1

6

If you are using Spring Boot, you can set the following property in application.properties file. That should solve the issue according to you stacktrace (see: "to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS")

spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false

Tom Cools
  • 1,098
  • 8
  • 23
  • This fixes the error, but adds a new line to the JSON file. Is there an alternative without that side effect? – Daniel Methner Apr 13 '20 at 14:21
  • 1
    What extra line did it add for you? Could you add the JSON to your original problem statement? – Tom Cools Apr 13 '20 at 14:26
  • I added the JSON with the "hibernateLazyInitializer" addendum. It only occurs upon the initial creation and disappears when getting the object using the first controller method (i.e. by calling it not immediately after its creation) – Daniel Methner Apr 13 '20 at 14:46
  • I normally never use the same objects I use for JPA/Hibernate and to expose them as JSON objects. Usually I have a mapping in between to make sure they are not the same object, so i never have this issue. – Tom Cools Apr 13 '20 at 14:48
  • I see. I feel like this might be an overkill for my app, unless that will save me a lot of time down the road. May I ask how You map them? Do You have a constructor in the JSON object that receives the JPA object as a parameter and maps it field by field in the constructor of the JSON object? – Daniel Methner Apr 13 '20 at 15:12
  • A workaround to avoid extra line is to add @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) to the class, but I seach for a cleaner way. – Ekaterina Mar 14 '21 at 14:26
  • This is somewhat addressed here: https://stackoverflow.com/questions/52656517/no-serializer-found-for-class-org-hibernate-proxy-pojo-bytebuddy-bytebuddyinterc however it's dealing with SELECT operations. I also face this issue when updating an entity and attempting to serialize it for return to front end. I'm guessing the 2 are related, but when inserting/updating, you don't have control over lazy loading. – Half_Duplex Nov 12 '21 at 00:17