0

I'm working with Spring, hibernate and MySql but I have some problem with seralization of query result. First in my entity I added @JsonManagedReference on Set structure (@OneToMany side) and @JsonBackReference on single object reference (@ManyToOne side) and it works but I wasn't be able to retrieve all needed information (for example @ManyToOne reference). So i swapping @JsonBackReference on set structure and @JsonManagedReference on single object but I retrieve

No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: com.model.tablesField.TableUI["data"]->java.util.ArrayList[0]->com.domain.Car["carType"]->com.domain.CarType_$$_jvst744_f["handler"])

I tried also with @JsonIgnore on Set structure but it doesn't work for the same issues. This is my spring configuration

private Properties getHibernateProperties() {
        Properties properties = new Properties();
        properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
//      properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
        properties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
        properties.put("hibernate.enable_lazy_load_no_trans",true);
        return properties;

and this is part of one of my several entities:

   /**
 * Car generated by hbm2java
 */
@Entity
@Table(name = "car", catalog = "ATS")
public class Car implements java.io.Serializable {

        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        private Integer idCar;
        @JsonManagedReference
        private CarType carType;
        @JsonManagedReference
        private Fleet fleet;
        private String id;
        private int initialKm;
        private String carChassis;
        private String note;
        @JsonBackReference
        private Set<Acquisition> acquisitions = new HashSet<Acquisition>(0);

        public Car() {
        }

        public Car(CarType carType, Fleet fleet, int initialKm, String carChassis) {
            this.carType = carType;
            this.fleet = fleet;
            this.initialKm = initialKm;
            this.carChassis = carChassis;
        }

        public Car(CarType carType, Fleet fleet, String id, int initialKm, String carChassis, String note,
                Set<Acquisition> acquisitions) {
            this.carType = carType;
            this.fleet = fleet;
            this.id = id;
            this.initialKm = initialKm;
            this.carChassis = carChassis;
            this.note = note;
            this.acquisitions = acquisitions;
        }

        @Id
        @GeneratedValue(strategy = IDENTITY)

        @Column(name = "id_car", unique = true, nullable = false)
        public Integer getIdCar() {
            return this.idCar;
        }

        public void setIdCar(Integer idCar) {
            this.idCar = idCar;
        }

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "id_carType", nullable = false)
        public CarType getCarType() {
            return this.carType;
        }

        public void setCarType(CarType carType) {
            this.carType = carType;
        }

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "id_fleet", nullable = false)
        public Fleet getFleet() {
            return this.fleet;
        }

        public void setFleet(Fleet fleet) {
            this.fleet = fleet;
        }

        @Column(name = "id", length = 5)
        public String getId() {
            return this.id;
        }

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

        @Column(name = "initialKm", nullable = false)
        public int getInitialKm() {
            return this.initialKm;
        }

        public void setInitialKm(int initialKm) {
            this.initialKm = initialKm;
        }

        @Column(name = "carChassis", nullable = false, length = 20)
        public String getCarChassis() {
            return this.carChassis;
        }

        public void setCarChassis(String carChassis) {
            this.carChassis = carChassis;
        }

        @Column(name = "note", length = 100)
        public String getNote() {
            return this.note;
        }

        public void setNote(String note) {
            this.note = note;
        }

        @OneToMany(fetch = FetchType.LAZY, mappedBy = "car")
        public Set<Acquisition> getAcquisitions() {
            return this.acquisitions;
        }

        public void setAcquisitions(Set<Acquisition> acquisitions) {
            this.acquisitions = acquisitions;
        }

    }

one method that uses the query:

@Override
    @RequestMapping(value = { "/cars/{idFleet}"}, method = RequestMethod.GET)
    public @ResponseBody TableUI getCars(@PathVariable int idFleet) {   
        TableUI ajaxCall=new TableUI();
        try {   
            ajaxCall.setData(fleetAndCarService.findCarsByIdFleet(idFleet));
            return ajaxCall;
        } catch (QueryException e) {
            ErrorResponse errorResponse= ErrorResponseBuilder.buildErrorResponse(e);
            LOG.error("Threw exception in FleetAndCarControllerImpl::addCar :" + errorResponse.getStacktrace());
            return ajaxCall;
        }
    }

two class for the query:

public interface DefRdiRepository extends JpaRepository<DefRdi, Integer>{


    //@Query("SELECT CASE WHEN COUNT(c) > 0 THEN true ELSE false END FROM DefRdi c WHERE c.parName = ?1 AND c.description= ?2")
    //Boolean existsByParNameAndDescription(String parName, String description);
    //Query method of spring, I put findBy and then the key of research 
    DefRdi findByParNameAndDescription(String parName, String description);
}

public interface CarRepository extends JpaRepository<Car, Integer>, CarRepositoryCustom {

    //Query method of spring, I put findBy and then the key of research 
    List<Car> findByFleetIdFleet(int idFleet);

}

Where is my error? I don't want Set object but only the single reference. The problem is only when I serialize. Thanks

UPDATE: I use @JSonIgnore on all set collectionts and Eager instead lazy ad all works fine, but is there a way to retrieve all the information only when I want, for example having two different query? So it doesn't work

@Override
@Transactional
public List<Car> findByFleetIdFleet(int idFleet) {
    List<Car> carList= carRepository.findByFleetIdFleet(idFleet);
    for (Car car:carList){
        Hibernate.initialize(car.getCarType());
        Hibernate.initialize(car.getFleet());
    }
    return carList; 
    //      return carRepository.findByFleetIdFleet(idFleet);
}
luca
  • 3,248
  • 10
  • 66
  • 145

1 Answers1

0

All collections need to be fetched eagerly when loading them from data base, in order to get serialized by Spring. Make sure you fetch them eagerly (e.g. FetchMode.JOIN). You could also swap @JsonManagedReference from wanted fields with @JsonIgnore to black listed fields, Spring automatically serialises every field without annotation.

Update:

Changing the data repository to something like that should work, I am not sure it compiles, but I think you will get the point:

@EntityGraph(value = "some.entity.graph", type = EntityGraph.EntityGraphType.FETCH)
@Query(
        value = "SELECT c FROM Car c INNER JOIN FETCH c.acquisitions WHERE c.id = :idFleet"
)
public interface CarRepository extends JpaRepository<Car, Integer>, CarRepositoryCustom {

      //Query method of spring, I put findBy and then the key of research 
      List<Car> findByFleetIdFleet(int idFleet);

}

For more information look at this post and read the official documentation.

Workaround:

There seems to be a workaround, however fetching those collections eager like shown above should have a positive performance impact, since there is no need for loading proxies afterwards. Also no open transactions are needed at controller level.

Community
  • 1
  • 1
Journeycorner
  • 2,474
  • 3
  • 19
  • 43
  • I tried to use only @JsonIgnore on the black listed fields (all the set collections) but it doesn't work. I update the above code with the fetchType, I'm using LAZY – luca Nov 13 '15 at 10:29
  • That will be the problem. The moment Spring wants to serialize your collection, there is no collection but a proxy, due to lazy loading. You have two choices: fetch the collections eagerly on loading time where you need it (recommended), or change the mapping type to eager. – Journeycorner Nov 13 '15 at 10:32
  • How can I fetch the collections eagerly? With Hibernate.initialize on all my exernal object without @JsonIgnore? Otherwise if I use model.addAttribute("fleets",fleetAndCarService.getFleets()) no problem occurrered – luca Nov 13 '15 at 10:35
  • Depends on how you load them, Criteria, HQL, Spring Data, JDBC...? Just add your code. – Journeycorner Nov 13 '15 at 10:37
  • I updated my first post and above question, if I have to use hibernnate.initialize for all object, I can use eager , is it the same? – luca Nov 13 '15 at 10:39
  • I forgot to mention that I added a snippet. Is it working? – Journeycorner Nov 16 '15 at 20:22
  • I can try now, but why I have to fetch with acquisition and not with Fleet and CarType? With this @Query(value = "SELECT c FROM Car c INNER JOIN FETCH c.acquisitions WHERE c.id = :idFleet") List findByFleetIdFleetFetch(int idFleet); doesn't work because the query is bad written (Name for parameter binding must not be null) – luca Nov 17 '15 at 08:25