ไม่พบซีเรียลไลเซอร์สำหรับคลาส org.hibernate.proxy.pojo.javassist.Javassist?


95

ฉันทำงานในSpringMVC, Hibernate& JSONแต่ฉันได้รับข้อผิดพลาดนี้

HTTP Status 500 - Could not write JSON: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.SerializationFeature.FAIL_ON_EMPTY_BEANS) ) 

โปรดตรวจสอบเอนทิตีของฉันด้านล่าง

    @Entity
@Table(name="USERS")
public class User {

    @Id
    @GeneratedValue
    @Column(name="USER_ID")
    private Integer userId;

    @Column(name="USER_FIRST_NAME")
    private String firstName;

    @Column(name="USER_LAST_NAME")
    private String lastName;


    @Column(name="USER_MIDDLE_NAME")
    private String middleName;

    @Column(name="USER_EMAIL_ID")
    private String emailId;

    @Column(name="USER_PHONE_NO")
    private Integer phoneNo;

    @Column(name="USER_PASSWORD")
    private String password;

    @Column(name="USER_CONF_PASSWORD")
    private String  confPassword;

    @Transient
    private String token;

    @Column(name="USER_CREATED_ON")
    private Date createdOn;

    @OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL)
    @Fetch(value = FetchMode.SUBSELECT)
    @JoinTable(name = "USER_ROLES", joinColumns = { @JoinColumn(name = "USER_ID") }, inverseJoinColumns = { @JoinColumn(name = "ROLE_ID") })
    private List<ActifioRoles> userRole = new ArrayList<ActifioRoles>();


    @OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL,mappedBy="userDetails")
    @Fetch(value = FetchMode.SUBSELECT)
    private List<com.actifio.domain.Address> userAddress = new ArrayList<com.actifio.domain.Address>();

    @OneToOne(cascade=CascadeType.ALL)
    private Tenant tenantDetails;


    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getEmailId() {
        return emailId;
    }
    public void setEmailId(String emailId) {
        this.emailId = emailId;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getConfPassword() {
        return confPassword;
    }
    public void setConfPassword(String confPassword) {
        this.confPassword = confPassword;
    }
    public Date getCreatedOn() {
        return createdOn;
    }
    public void setCreatedOn(Date createdOn) {
        this.createdOn = createdOn;
    }

    public List<ActifioRoles> getUserRole() {
        return userRole;
    }

    public void setUserRole(List<ActifioRoles> userRole) {
        this.userRole = userRole;
    }
    public String getMiddleName() {
        return middleName;
    }
    public void setMiddleName(String middleName) {
        this.middleName = middleName;
    }
    public Integer getPhoneNo() {
        return phoneNo;
    }
    public void setPhoneNo(Integer phoneNo) {
        this.phoneNo = phoneNo;
    }

    public List<com.actifio.domain.Address> getUserAddress() {
        return userAddress;
    }
    public void setUserAddress(List<com.actifio.domain.Address> userAddress) {
        this.userAddress = userAddress;
    }
    public Tenant getTenantDetails() {
        return tenantDetails;
    }
    public void setTenantDetails(Tenant tenantDetails) {
        this.tenantDetails = tenantDetails;
    }
    public String getToken() {
        return token;
    }
    public void setToken(String token) {
        this.token = token;
    }

    }

ฉันจะแก้ปัญหานี้ได้อย่างไร


โปรดแสดง stacktrace และรหัสที่เกิดข้อยกเว้น
geoand

หากไม่มีความรู้เกี่ยวกับสิ่งที่โค้ดของคุณกำลังพยายามทำการดีบักก็ทำได้ยากเล็กน้อย แต่คุณอาจต้องการตรวจสอบgithub.com/FasterXML/jackson-datatype-hibernateเนื่องจากคุณใช้ Jackson และ Hibernate
geoand

คุณกำลังพยายามสร้าง JSON จากคลาสนี้หรือไม่ ในกรณีนี้ซีเรียลไลเซอร์ JSON จะพยายามเขียนคุณสมบัติทั้งหมดรวมถึง HashSet ของความสัมพันธ์แบบกลุ่มต่อกลุ่มของคุณ สิ่งนี้ทำให้เกิดข้อยกเว้นการเริ่มต้นที่ขี้เกียจ
Angelo Immediata

สามารถพบคำถามเดียวกันได้ที่stackoverflow.com/questions/4362104/…
Matrix Buster

@ user2963481 ... คำถามที่ดีและช่วยเหลือมากครับพี่
Brain

คำตอบ:


197

ฉันมีปัญหาคล้ายกันกับการโหลดแบบขี้เกียจผ่านวัตถุไฮเบอร์เนตพร็อกซี หลีกเลี่ยงได้โดยการใส่คำอธิบายประกอบคลาสที่มีคุณสมบัติส่วนตัวที่ขี้เกียจโหลดด้วย:

@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})

ฉันคิดว่าคุณสามารถเพิ่มคุณสมบัติบนวัตถุพร็อกซีของคุณที่ทำลายการทำให้อนุกรม JSON กับคำอธิบายประกอบนั้น

ปัญหาคือเอนทิตีถูกโหลดอย่างเฉื่อยชาและการทำให้เป็นอนุกรมเกิดขึ้นก่อนที่จะโหลดจนเต็ม

Hibernate.initialize(<your getter method>);

3
มันได้ผลสำหรับฉันเช่นกัน ... มีคำอธิบายเกี่ยวกับพฤติกรรมแปลก ๆ นี้หรือไม่?
วิกเตอร์

7
@ ankur-singhal คุณควรเน้นว่าคำอธิบายประกอบนี้จำเป็นในคลาสที่ซ้อนกันไม่ใช่คลาสการโทร
Darwayne

1
Yahooo ... มันได้ผล ฉันพยายามสองสามครั้งและได้ผล ฉันลองไม่กี่วิธีและ BoooM ทำงานแล้ว.
Brain

2
ขอบคุณสำหรับฉันด้วย คุณต้องเพิ่มคำอธิบายประกอบนี้ให้กับเอนทิตีที่เข้าถึงเอนทิตีอื่น ๆ ที่มีขี้เกียจโหลดถั่ว
Shafqat Shafi

3
ฉันคิดว่านี่ไม่ใช่วิธีแก้ปัญหาที่ถูกต้อง เมื่อเราทำให้เป็นอนุกรมเราคาดหวังว่าวัตถุย่อยที่สมบูรณ์หรืออย่างน้อยคีย์หลักของวัตถุย่อยจะถูกส่งกลับในผลลัพธ์ การเพิ่มคำอธิบายประกอบเหล่านี้จะช่วยระงับข้อผิดพลาด แต่จะไม่ให้ผลลัพธ์ที่คุณต้องการ
Anand Vaidya

84

เพื่อเพิ่มสิ่งนี้ในฉันพบปัญหาเดียวกันนี้ แต่คำตอบที่ให้มาไม่ได้ผล ฉันแก้ไขโดยรับข้อเสนอแนะของข้อยกเว้นและเพิ่มลงในไฟล์ application.properties ...

spring.jackson.serialization.fail-on-empty-beans=false

ฉันใช้ Spring Boot v1.3 กับ Hibernate 4.3

ตอนนี้มันทำให้วัตถุทั้งหมดเป็นอนุกรมและวัตถุที่ซ้อนกัน

แก้ไข: 2018

เนื่องจากสิ่งนี้ยังคงได้รับความคิดเห็นฉันจะชี้แจงที่นี่ นี่เป็นเพียงการซ่อนข้อผิดพลาดเท่านั้น ผลกระทบด้านประสิทธิภาพอยู่ที่นั่น ในเวลานั้นฉันต้องการบางสิ่งเพื่อส่งมอบและดำเนินการในภายหลัง (ซึ่งฉันทำโดยไม่ใช้สปริงอีกต่อไป) ใช่ฟังคนอื่นถ้าคุณต้องการแก้ปัญหาจริงๆ หากคุณต้องการให้มันหายไปในตอนนี้ให้ใช้คำตอบนี้ มันเป็นความคิดที่แย่มาก แต่อาจเหมาะกับคุณ สำหรับบันทึกจะไม่เกิดข้อขัดข้องหรือปัญหาอีกเลยหลังจากนี้ แต่มันน่าจะเป็นที่มาของสิ่งที่กลายเป็นฝันร้ายของประสิทธิภาพ SQL


8
แก้ปัญหาได้ แต่ json จะมีคุณสมบัติพิเศษสองอย่างที่ไม่จำเป็น"handler":{},"hibernateLazyInitializer":{}
prettyvoid

เยี่ยมมาก! แก้ไขที่นี่ด้วยนี่คือคำตอบ
ฮิโนโทริ

@prettyvoid ทำไมคุณสมบัติพิเศษจึงมีอยู่?
Robert Moon

12
@RobertMoon ถ้าคุณต้องการกำจัดคุณสามารถใส่คำอธิบายประกอบเอนทิตีของคุณด้วย@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
prettyvoid

@prettyvoid ขอบคุณ ฉันพบทางออกอื่นคือเปลี่ยน LAZY เป็น EAGER มีผลต่อการแสดงหรือไม่?
Robert Moon

69

ตามที่แนะนำไว้อย่างถูกต้องในคำตอบก่อนหน้านี้การโหลดแบบขี้เกียจหมายความว่าเมื่อคุณดึงวัตถุจากฐานข้อมูลวัตถุที่ซ้อนกันจะไม่ถูกดึงออกมา (และอาจดึงข้อมูลได้ในภายหลังเมื่อจำเป็น)

ตอนนี้ Jackson พยายามทำให้เป็นอนุกรมของวัตถุที่ซ้อนกัน (== สร้าง JSON จากมัน) แต่ล้มเหลวเนื่องจากพบ JavassistLazyInitializer แทนที่จะเป็นวัตถุปกติ นี่คือข้อผิดพลาดที่คุณเห็น ทีนี้จะแก้ยังไง?

ตามที่แนะนำโดย CP510 ก่อนหน้านี้ทางเลือกหนึ่งคือการระงับข้อผิดพลาดโดยการกำหนดค่าบรรทัดนี้:

spring.jackson.serialization.fail-on-empty-beans=false

แต่นี่คือการจัดการกับอาการที่ไม่ก่อให้เกิด ในการแก้ปัญหาอย่างหรูหราคุณต้องตัดสินใจว่าคุณต้องการวัตถุนี้ใน JSON หรือไม่?

  1. หากคุณต้องการอ็อบเจ็กต์ใน JSON ให้ลบFetchType.LAZYอ็อพชันออกจากฟิลด์ที่เป็นสาเหตุ (อาจเป็นฟิลด์ในอ็อบเจ็กต์ที่ซ้อนกันบางตัวไม่ใช่เฉพาะในเอนทิตีรูทที่คุณกำลังดึงข้อมูล)

  2. หากไม่ต้องการออบเจ็กต์ใน JSON ให้ใส่คำอธิบายประกอบ getter ของฟิลด์นี้ (หรือฟิลด์เองถ้าคุณไม่จำเป็นต้องยอมรับค่าที่เข้ามา) ด้วย@JsonIgnoreตัวอย่างเช่น:

    // this field will not be serialized to/from JSON @JsonIgnore private NestedType secret;

หากคุณมีความต้องการที่ซับซ้อนมากขึ้น (เช่นกฎที่แตกต่างกันสำหรับตัวควบคุม REST ที่แตกต่างกันโดยใช้เอนทิตีเดียวกัน) คุณสามารถใช้มุมมองแจ็คสันหรือการกรองหรือสำหรับกรณีการใช้งานที่ง่ายมากเรียกวัตถุที่ซ้อนกันแยกกัน


16
นี่คือคำตอบที่ดีที่สุด จัดการกับต้นตอของปัญหาอย่าเพียงแค่ปกปิดอาการเพื่อให้โปรแกรมเมอร์คนต่อไปต้องจัดการ
Andrew

2
คุณคือฮีโร่ของฉัน
Lay Leangsros

ขอบคุณมากสำหรับการอธิบายเหตุผลเบื้องหลังข้อผิดพลาดและให้สองวิธีแก้ไขที่สมเหตุสมผล
Mauricio Poppe

ขอบคุณสำหรับสิ่งนี้ แต่ฉันเห็นว่า springfox-swagger ยังคงให้ข้อผิดพลาดแก่ฉันRangeError: Maximum call stack size exceeded
Pra_A

18

คุณสามารถใช้โมดูลเสริมสำหรับ Jackson ซึ่งจัดการกับ Hibernate lazy-loading

ข้อมูลเพิ่มเติมเกี่ยวกับhttps://github.com/FasterXML/jackson-datatype-hibernate ซึ่งรองรับโหมดไฮเบอร์เนต 3 และ 4 แยกกัน


7
นี่น่าจะเป็นคำตอบที่ถูกต้องจริงๆ คำตอบอื่น ๆ ทั้งหมดกำลังซ่อนปัญหาแทนที่จะแก้ปัญหา
อาเหม็ดฮัสซาเนียน

แต่จะใช้โมดูลนี้อย่างไร? มีคำแนะนำหรือไม่
อานันท์วิดยา

วิธีกำหนดค่า jackson-datatype-hibernate: stackoverflow.com/q/33727017
Chase

13

ฉันคิดว่าปัญหาคือวิธีที่คุณเรียกข้อมูลเอนทิตี

บางทีคุณอาจกำลังทำสิ่งนี้:

Person p = (Person) session.load(Person.class, new Integer(id));

ลองใช้วิธีgetแทนload

Person p = (Person) session.get(Person.class, new Integer(id));

ปัญหาคือด้วยวิธีการโหลดคุณจะได้รับเพียงพร็อกซี แต่ไม่ใช่วัตถุจริง วัตถุพร็อกซีไม่มีคุณสมบัติที่โหลดไว้แล้วดังนั้นเมื่อการทำให้เป็นอนุกรมเกิดขึ้นจะไม่มีคุณสมบัติที่จะทำให้เป็นอนุกรม ด้วยเมธอด get คุณจะได้รับวัตถุจริงในความเป็นจริงวัตถุนี้สามารถต่ออนุกรมกันได้


2
เกิดข้อผิดพลาดที่คล้ายกันมากกับฉันเมื่อฉันใช้ getOne แทน findOne ในกรณีของฉันที่เก็บอินเทอร์เฟซของฉันกำลังขยาย JpaRepository findOne ทำงานได้ดีโดยไม่มีคำอธิบายประกอบ Json หรือการเปลี่ยนแปลงใด ๆ ใน application.properties
James Freitas

เช่นเดียวกับ James Freitas ฉันพบว่า getOne นำไปสู่ปัญหาและใช้แทนเช่น findById - แก้ไขปัญหาโดยไม่จำเป็นต้องใช้คำอธิบายประกอบหรือบรรทัดที่ระบุข้างต้นในคุณสมบัติของแอปพลิเคชัน
MY

1
เพื่อนร่วมงานของฉันชี้แจงฉันและบอกว่า: "ข้อแตกต่างพื้นฐานคือ getOne ขี้เกียจโหลดและ findOne ไม่ใช่" - หมายความว่าที่ผ่านมาไม่ได้รับแถวข้อมูลจากฐานข้อมูล แต่สร้างการอ้างอิงเท่านั้น อันหลังได้รับแถวแทนซึ่งเป็นกรณีที่มักต้องการ
MY

@JamesFreitas ใช่! ขอบคุณ! และขอบคุณคาร์ลอสที่นำเจมส์ไปสู่บทสรุปที่ดีนี้!
Filip Savic

9

มันได้ผลสำหรับฉัน

@JsonIgnoreProperties({"hibernateLazyInitializer","handler"})

เช่น

@Entity
@Table(name = "user")
@Data
@NoArgsConstructor
@JsonIgnoreProperties({"hibernateLazyInitializer","handler"})
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private Date created;

}

5

มีสองวิธีในการแก้ไขปัญหา

วิธีที่ 1 :

เพิ่ม spring.jackson.serialization.fail-on-empty-beans=falseลงในapplication.properties

วิธีที่ 2 :

ใช้join fetchในแบบสอบถาม JPQL เพื่อดึงข้อมูลออบเจ็กต์หลักดูด้านล่าง:

@Query(value = "select child from Child child join fetch child.parent Parent ",
           countQuery = "select count(*) from Child child join child.parent parent ")
public Page<Parent> findAll(Pageable pageable); 

ขอบคุณ! ทางออกแรกดีมาก!
Lucas Moyano Angelini

วิธีที่ 1. เยี่ยมมาก ขอบคุณ.
Lay Leangsros

3

เพิ่มคำอธิบายประกอบนี้ในคลาสเอนทิตี (รุ่น) ที่เหมาะกับฉันซึ่งทำให้เกิดการโหลดขี้เกียจผ่านวัตถุพร็อกซีไฮเบอร์เนต

@JsonIgnoreProperties ({"hibernateLazyInitializer", "handler"})


2

ข้อยกเว้นนี้

org.springframework.http.converter.HttpMessageNotWritableException

รับเพราะฉันหวังว่าอย่างนั้นคุณกำลังส่งเอาต์พุตการตอบสนองเป็นวัตถุต่อเนื่อง
นี่คือปัญหาที่เกิดขึ้นในฤดูใบไม้ผลิ เมื่อต้องการแก้ไขปัญหานี้ให้ส่งวัตถุ POJO เป็นเอาต์พุตการตอบสนอง

ตัวอย่าง:

    @Entity
    @Table(name="user_details")
    public class User implements Serializable{

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

        @Column(name="user_name")
        private String userName;

        @Column(name="email_id")
        private String emailId;

        @Column(name="phone_no")
        private String phone;

//setter and getters

ชั้น POJO:

public class UserVO {

    private int Id;
    private String userName;
    private String emailId;
    private String phone;
    private Integer active;

//setter and getters

ในคอนโทรลเลอร์แปลงฟิลด์อ็อบเจ็กต์ที่สามารถใช้งานได้เป็นฟิลด์คลาส POJO และส่งคืนคลาส pojo เป็นเอาต์พุต

         User u= userService.getdetials(); // get data from database

        UserVO userVo= new UserVO();  // created pojo class object

        userVo.setId(u.getId());
        userVo.setEmailId(u.getEmailId());
        userVo.setActive(u.getActive());
        userVo.setPhone(u.getPhone());
        userVo.setUserName(u.getUserName());
       retunr userVo;  //finally send pojo object as output.

ใช่ฉันทำแบบเดียวกัน แต่ฉันก็ยังได้รับข้อผิดพลาดเดียวกันเนื่องจากฉันกำลังส่งข้อมูลที่เกี่ยวข้องกับวัตถุซึ่งไม่มีอะไรนอกจากการตั้งค่าคลาสเอนทิตี
Pra_A

2

ใน Hibernate 5.2 ขึ้นไปคุณสามารถลบ hibernate proxy ได้ดังต่อไปนี้ซึ่งจะให้วัตถุจริงแก่คุณเพื่อให้คุณสามารถทำให้เป็นอนุกรมได้อย่างถูกต้อง:

Object unproxiedEntity = Hibernate.unproxy( proxy );

มันจะโทรHibernate.initializeมาก่อนโดยอัตโนมัติเช่นกัน
Sam YC

@ ทิมควรเพิ่มก่อนการทำให้อนุกรม json ปัญหาเกิดขึ้นเนื่องจากมีวัตถุไฮเบอร์เนตพร็อกซีที่พบในระหว่างการทำให้เป็นอนุกรมดังนั้นเมื่อลบออกแล้วการทำให้เป็นอนุกรมจะใช้ได้ หากคุณใช้ตัวควบคุมสปริงคุณต้องทำก่อนที่ตัวควบคุมจะสิ้นสุด
Sam YC

1

สำหรับ Hibernate คุณสามารถใช้jackson-datatype-hibernateโปรเจ็เพื่อรองรับ JSON serialization / deserialization กับวัตถุที่โหลดแบบ lazy

ตัวอย่างเช่น,

import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonDatatypeHibernate5Configuration {

    // Register Jackson Hibernate5 Module to handle JSON serialization of lazy-loaded entities
    // Any beans of type com.fasterxml.jackson.databind.Module are automatically
    // registered with the auto-configured Jackson2ObjectMapperBuilder
    // https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper
    @Bean
    public Module hibernate5Module() {
        Hibernate5Module hibernate5Module = new Hibernate5Module();
        hibernate5Module.enable( Hibernate5Module.Feature.FORCE_LAZY_LOADING );
        hibernate5Module.disable( Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION );
        return hibernate5Module;
    }
}

can't parse JSON. Raw result:ถ้าผมใช้วิธีนี้แล้วฉันจะได้รับ ความช่วยเหลือใด ๆ
Pra_A

1

โซลูชันนี้ได้รับแรงบันดาลใจจากโซลูชันด้านล่างโดย @marco ฉันได้อัปเดตคำตอบของเขาด้วยข้อมูลเหล่านี้ด้วย

ปัญหาที่นี่เกี่ยวกับการโหลดวัตถุย่อยที่ขี้เกียจโดยที่แจ็คสันพบเฉพาะพร็อกซีไฮเบอร์เนตแทนที่จะเป็นวัตถุที่ถูกเป่าเต็ม

ดังนั้นเราจึงเหลือสองตัวเลือก - ปราบปรามข้อยกเว้นเช่นเดียวกับที่ทำข้างต้นในคำตอบที่ได้รับการโหวตมากที่สุดที่นี่หรือตรวจสอบให้แน่ใจว่าโหลดวัตถุ LazyLoad แล้ว

หากคุณเลือกที่จะใช้ตัวเลือกหลังวิธีแก้ปัญหาก็คือการใช้ jackson-datatypeไลบรารีและกำหนดค่าไลบรารีเพื่อเริ่มต้นการอ้างอิงแบบ lazy-load ก่อนการจัดลำดับ

ฉันเพิ่มคลาส config ใหม่เพื่อทำสิ่งนั้น

@Configuration
public class JacksonConfig extends WebMvcConfigurerAdapter {

@Bean
@Primary
public MappingJackson2HttpMessageConverter jacksonMessageConverter(){
    MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
    ObjectMapper mapper = new ObjectMapper();
    Hibernate5Module module = new Hibernate5Module();
    module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING);
    mapper.registerModule(module);
    messageConverter.setObjectMapper(mapper);
    return messageConverter;
}

}

@Primary ตรวจสอบให้แน่ใจว่าไม่มีการใช้การกำหนดค่า Jackson อื่นสำหรับการเริ่มต้นถั่วอื่น ๆ @Beanเป็นไปตามปกติmodule.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING);คือการเปิดใช้งาน Lazy loading ของการอ้างอิง

ข้อควรระวัง - โปรดดูผลกระทบด้านประสิทธิภาพ บางครั้งการดึงข้อมูล EAGER ช่วยได้ แต่แม้ว่าคุณจะทำให้มันกระตือรือร้นคุณก็ยังต้องใช้รหัสนี้เนื่องจากวัตถุพร็อกซียังคงมีอยู่สำหรับการแม็พอื่น ๆ ทั้งหมดยกเว้น@OneToOne

PS: ตามความคิดเห็นทั่วไปฉันจะไม่แนะนำให้ส่งวัตถุข้อมูลทั้งหมดกลับในการตอบสนองของ Json หนึ่งควรใช้ Dto สำหรับการสื่อสารนี้และใช้ผู้ทำแผนที่บางอย่างเช่น mapstruct เพื่อทำแผนที่ วิธีนี้ช่วยให้คุณประหยัดจากช่องโหว่ด้านความปลอดภัยโดยไม่ได้ตั้งใจเช่นเดียวกับข้อยกเว้นข้างต้น


1

ฉันมีปัญหาเดียวกันตอนนี้ ตรวจสอบว่าคุณแก้ไขการดึงข้อมูลด้วยความขี้เกียจด้วย @jsonIQgnore หรือไม่

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="teachers")
@JsonIgnoreProperties("course")
Teacher teach;

เพียงลบ "(fetch = ... )" หรือคำอธิบายประกอบ "@jsonIgnore" ก็จะใช้งานได้

@ManyToOne
@JoinColumn(name="teachers")
@JsonIgnoreProperties("course")
Teacher teach;

0

หรือคุณสามารถกำหนดค่าผู้ทำแผนที่เป็น:

// การกำหนดค่าที่กำหนดเองสำหรับการโหลดแบบขี้เกียจ

public static class HibernateLazyInitializerSerializer extends JsonSerializer<JavassistLazyInitializer> {

    @Override
    public void serialize(JavassistLazyInitializer initializer, JsonGenerator jsonGenerator,
            SerializerProvider serializerProvider)
            throws IOException, JsonProcessingException {
        jsonGenerator.writeNull();
    }
}

และกำหนดค่าผู้ทำแผนที่:

    mapper = new JacksonMapper();
    SimpleModule simpleModule = new SimpleModule(
            "SimpleModule", new Version(1,0,0,null)
    );
    simpleModule.addSerializer(
            JavassistLazyInitializer.class,
            new HibernateLazyInitializerSerializer()
    );
    mapper.registerModule(simpleModule);

0

อาจเป็นความสัมพันธ์ของเอนทิตีไฮเบอร์เนตที่ทำให้เกิดปัญหา ... เพียงแค่หยุดการโหลดเอนทิตีที่เกี่ยวข้องนั้นอย่างขี้เกียจ ... ตัวอย่างเช่น ... ฉันแก้ไขด้านล่างโดยการตั้งค่า lazy = "false" สำหรับ customerType

<class name="Customer" table="CUSTOMER">
        <id name="custId" type="long">
            <column name="CUSTID" />
            <generator class="assigned" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <property name="phone" type="java.lang.String">
            <column name="PHONE" />
        </property>
        <property name="pan" type="java.lang.String">
            <column name="PAN" />
        </property>

        <many-to-one name="customerType" not-null="true" lazy="false"></many-to-one>
    </class>
</hibernate-mapping>

1
โปรดแจ้งในโพสต์ของคุณว่าอาจเป็นทางออกที่อันตรายมากในการใช้งานในชีวิตจริง
panurg

0

ฉันเปลี่ยน (ในคลาสโมเดลคำอธิบายประกอบ)

เรียก = FetchType.LAZY

ถึง

fetch = FetchType.EAGER

และทำงานได้ดี ...

รักมัน.


5
แม้ว่าวิธีนี้จะช่วยแก้ปัญหาได้ แต่ก็เป็นเรื่องที่อันตรายมาก การเปลี่ยนfetchกลยุทธ์ในแบบจำลองมีผลหลายประการในด้านประสิทธิภาพ ด้วยการเปลี่ยนแปลงนี้คุณกำลังนำข้อมูลมาจากฐานข้อมูลมากขึ้น อาจเป็นวิธีแก้ปัญหาที่ถูกต้อง แต่ก่อนอื่นจำเป็นต้องมีการศึกษาประสิทธิภาพ
João Menighin

1
หากคุณรู้ว่าคุณเป็นนางแบบและไม่มีเอนทิตีจำนวนมากที่เกี่ยวข้องคุณสามารถใช้ mem และ perfomance เล็กน้อยเพื่อให้ได้เอนทิตีที่สมบูรณ์ แต่เป็นสิ่งที่ดีที่จะรู้ว่าดีที่สุดที่คุณใช้ LAZY fetch
Sham Fiorin

0

นี่เป็นปัญหากับแจ็คสัน เพื่อป้องกันสิ่งนี้สั่งให้แจ็คสันอย่าจัดลำดับความสัมพันธ์แบบซ้อนหรือคลาสที่ซ้อนกัน

ดูตัวอย่างต่อไปนี้ ที่อยู่ชั้นแมปไปเมือง , รัฐและประเทศชั้นเรียนและรัฐเองจะชี้ไปที่ประเทศและประเทศที่ชี้ไปยังภาค เมื่อรับค่าที่อยู่ของคุณผ่าน Spring boot REST API คุณจะได้รับข้อผิดพลาดข้างต้น เพื่อป้องกันไม่ให้ชั้นเพียงแค่แมปเป็นอันดับ (ซึ่งสะท้อนให้เห็นถึงระดับหนึ่ง JSON) และไม่สนใจความสัมพันธ์ซ้อนกันด้วย @JsonIgnoreProperties(value = {"state"}),@JsonIgnoreProperties(value = {"country"})และ @JsonIgnoreProperties(value = {"region"})

สิ่งนี้จะป้องกันข้อยกเว้น Lazyload พร้อมกับข้อผิดพลาดข้างต้น ใช้โค้ดด้านล่างเป็นตัวอย่างและเปลี่ยนคลาสโมเดลของคุณ

Address.java

@Entity
public class Address extends AbstractAuditingEntity
{
    private static final long serialVersionUID = 4203344613880544060L;

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

    @Column(name = "street_name")
    private String streetName;

    @Column(name = "apartment")
    private String apartment;

    @ManyToOne
    @JoinColumn(name = "city_id")
    @JsonIgnoreProperties(value = {"state"})
    private City city;

    @ManyToOne
    @JoinColumn(name = "state_id")
    @JsonIgnoreProperties(value = {"country"})
    private State state;

    @ManyToOne
    @JoinColumn(name = "country_id")
    @JsonIgnoreProperties(value = {"region"})
    private Country country;

    @ManyToOne
    @JoinColumn(name = "region_id")
    private Region region;

    @Column(name = "zip_code")
    private String zipCode;

    @ManyToOne
    @JoinColumn(name = "address_type_id", referencedColumnName = "id")
    private AddressType addressType;

}

City.java

@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = "city")
@Cache(region = "cityCache",usage = CacheConcurrencyStrategy.READ_WRITE)
@Data
public class City extends AbstractAuditingEntity
{
    private static final long serialVersionUID = -8825045541258851493L;

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

    @Column(name = "name")
    //@Length(max = 100,min = 2)
    private String name;


    @ManyToOne
    @JoinColumn(name = "state_id")
    private State state;
}

State.java

@Entity
@Table(name = "state")
@Data
@EqualsAndHashCode(callSuper = true)
public class State extends AbstractAuditingEntity
{
    private static final long serialVersionUID = 5553856435782266275L;

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

    @Column(name = "code")
    private String code;

    @Column(name = "name")
    @Length(max = 200, min = 2)
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "country_id")
    private Country country;

}

Country.java

@Entity
@Table(name = "country")
@Data
@EqualsAndHashCode(callSuper = true)
public class Country extends AbstractAuditingEntity
{
    private static final long serialVersionUID = 6396100319470393108L;

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

    @Column(name = "name")
    @Length(max = 200, min = 2)
    private String name;

    @Column(name = "code")
    @Length(max = 3, min = 2)
    private String code;

    @Column(name = "iso_code")
    @Length(max = 3, min = 2)
    private String isoCode;

    @ManyToOne
    @JoinColumn(name = "region_id")
    private Region region;
}


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.