Đệ quy vô hạn với vấn đề Jackson JSON và Hibernate JPA


412

Khi cố gắng chuyển đổi một đối tượng JPA có liên kết hai chiều thành JSON, tôi tiếp tục nhận được

org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)

Tất cả tôi tìm thấy là chủ đề này về cơ bản kết luận với khuyến nghị để tránh các hiệp hội hai chiều. Có ai có ý tưởng cho một cách giải quyết cho lỗi mùa xuân này không?

------ CHỈNH SỬA 2010-07-24 16:26:22 -------

Đoạn mã:

Đối tượng kinh doanh 1:

@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class Trainee extends BusinessObject {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(name = "id", nullable = false)
    private Integer id;

    @Column(name = "name", nullable = true)
    private String name;

    @Column(name = "surname", nullable = true)
    private String surname;

    @OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @Column(nullable = true)
    private Set<BodyStat> bodyStats;

    @OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @Column(nullable = true)
    private Set<Training> trainings;

    @OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @Column(nullable = true)
    private Set<ExerciseType> exerciseTypes;

    public Trainee() {
        super();
    }

    ... getters/setters ...

Đối tượng kinh doanh 2:

import javax.persistence.*;
import java.util.Date;

@Entity
@Table(name = "ta_bodystat", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class BodyStat extends BusinessObject {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(name = "id", nullable = false)
    private Integer id;

    @Column(name = "height", nullable = true)
    private Float height;

    @Column(name = "measuretime", nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date measureTime;

    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name="trainee_fk")
    private Trainee trainee;

Điều khiển:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Controller
@RequestMapping(value = "/trainees")
public class TraineesController {

    final Logger logger = LoggerFactory.getLogger(TraineesController.class);

    private Map<Long, Trainee> trainees = new ConcurrentHashMap<Long, Trainee>();

    @Autowired
    private ITraineeDAO traineeDAO;

    /**
     * Return json repres. of all trainees
     */
    @RequestMapping(value = "/getAllTrainees", method = RequestMethod.GET)
    @ResponseBody        
    public Collection getAllTrainees() {
        Collection allTrainees = this.traineeDAO.getAll();

        this.logger.debug("A total of " + allTrainees.size() + "  trainees was read from db");

        return allTrainees;
    }    
}

Thực hiện JPA của DAO thực tập sinh:

@Repository
@Transactional
public class TraineeDAO implements ITraineeDAO {

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public Trainee save(Trainee trainee) {
        em.persist(trainee);
        return trainee;
    }

    @Transactional(readOnly = true)
    public Collection getAll() {
        return (Collection) em.createQuery("SELECT t FROM Trainee t").getResultList();
    }
}

kiên trì

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
             version="1.0">
    <persistence-unit name="RDBMS" transaction-type="RESOURCE_LOCAL">
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="hibernate.hbm2ddl.auto" value="validate"/>
            <property name="hibernate.archive.autodetection" value="class"/>
            <property name="dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
            <!-- <property name="dialect" value="org.hibernate.dialect.HSQLDialect"/>         -->
        </properties>
    </persistence-unit>
</persistence>

Thêm @Transientvào Trainee.bodyStats.
phil294

Tính đến năm 2017, @JsonIgnorePropertieslà giải pháp sạch nhất. Kiểm tra câu trả lời của Zammel AlaaEddine để biết thêm chi tiết.
Utku

Mùa xuân này có lỗi thế nào ??
Nathan Hughes

Câu trả lời:


293

Bạn có thể sử dụng @JsonIgnoređể phá vỡ chu kỳ.


1
@Ben: Thật ra tôi không biết. Có lẽ hỗ trợ của nó không được kích hoạt: wiki.fasterxml.com/JacksonJAXBAnnotations
axtavt

40
Vì Jackson 1.6 có một giải pháp tốt hơn: bạn có thể sử dụng hai chú thích mới để giải quyết vấn đề đệ quy vô hạn mà không bỏ qua các getters / setters trong quá trình tuần tự hóa. Xem câu trả lời của tôi dưới đây để biết chi tiết.
Kurt Bourbaki

1
@axtavt Cảm ơn câu trả lời hoàn hảo. Btw, tôi đã đưa ra một giải pháp khác: bạn có thể đơn giản tránh tạo getter cho giá trị này, vì vậy Spring sẽ không thể truy cập nó trong khi tạo JSON (nhưng tôi không nghĩ rằng nó phù hợp với mọi trường hợp, vì vậy câu trả lời của bạn tốt hơn)
Semyon Danilov

11
Tất cả các giải pháp trên dường như cần thay đổi các đối tượng miền bằng cách thêm chú thích. Nếu tôi tuần tự hóa các lớp bên thứ ba mà tôi không có cách nào sửa đổi chúng. Làm thế nào tôi có thể tránh vấn đề này?
Jianwu Chen

2
giải pháp này không hoạt động trong một số tình huống. Trong cơ sở dữ liệu quan hệ với jpa, nếu bạn đặt, @JsonIgnorebạn sẽ vô hiệu hóa "khóa ngoại" khi bạn cập nhật thực thể ...
mỏng

629

JsonIgnoreProperIES [Cập nhật 2017]:

Bây giờ bạn có thể sử dụng JsonIgnoreProperies để ngăn chặn việc tuần tự hóa các thuộc tính (trong quá trình tuần tự hóa) hoặc bỏ qua việc xử lý các thuộc tính JSON được đọc (trong quá trình khử lưu huỳnh ) . Nếu đây không phải là những gì bạn đang tìm kiếm, xin vui lòng tiếp tục đọc dưới đây.

(Cảm ơn As Zammel AlaaEddine đã chỉ ra điều này).


JsonManagedReference và JsonBackReference

Vì Jackson 1.6, bạn có thể sử dụng hai chú thích để giải quyết vấn đề đệ quy vô hạn mà không bỏ qua các getters / setters trong quá trình tuần tự hóa: @JsonManagedReference@JsonBackReference.

Giải trình

Để Jackson hoạt động tốt, một trong hai mặt của mối quan hệ không nên được nối tiếp, để tránh vòng lặp vô hạn gây ra lỗi stackoverflow của bạn.

Vì vậy, Jackson lấy phần chuyển tiếp của tài liệu tham khảo ( Set<BodyStat> bodyStatstrong lớp Trainee của bạn ) và chuyển đổi nó theo định dạng lưu trữ giống như json; đây là quá trình gọi là đầm lầy . Sau đó, Jackson tìm phần phía sau của tài liệu tham khảo (tức là Trainee traineetrong lớp BodyStat) và để nguyên như vậy, không nối tiếp nó. Phần này của mối quan hệ sẽ được xây dựng lại trong quá trình khử lưu huỳnh (không sắp xếp lại ) của tham chiếu chuyển tiếp.

Bạn có thể thay đổi mã của mình như thế này (tôi bỏ qua những phần vô dụng):

Đối tượng kinh doanh 1:

@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class Trainee extends BusinessObject {

    @OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @Column(nullable = true)
    @JsonManagedReference
    private Set<BodyStat> bodyStats;

Đối tượng kinh doanh 2:

@Entity
@Table(name = "ta_bodystat", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class BodyStat extends BusinessObject {

    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name="trainee_fk")
    @JsonBackReference
    private Trainee trainee;

Bây giờ tất cả nên hoạt động đúng.

Nếu bạn muốn biết thêm thông tin, tôi đã viết một bài viết về các vấn đề của Json và Jackson Stackoverflow trên Keenformatics , blog của tôi.

BIÊN TẬP:

Một chú thích hữu ích khác mà bạn có thể kiểm tra là @JsonIdentityInfo : sử dụng nó, mỗi khi Jackson tuần tự hóa đối tượng của bạn, nó sẽ thêm một ID (hoặc một thuộc tính khác bạn chọn) vào nó, để nó hoàn toàn không "quét" lại nó mỗi lần. Điều này có thể hữu ích khi bạn có một vòng lặp chuỗi giữa các đối tượng có liên quan với nhau hơn (ví dụ: Order -> OrderLine -> User -> Order và hơn nữa).

Trong trường hợp này, bạn phải cẩn thận, vì bạn có thể cần phải đọc các thuộc tính của đối tượng nhiều lần (ví dụ: trong danh sách sản phẩm có nhiều sản phẩm có chung người bán) và chú thích này ngăn bạn làm như vậy. Tôi đề nghị luôn luôn xem các bản ghi firebird để kiểm tra phản hồi của Json và xem những gì đang diễn ra trong mã của bạn.

Nguồn:


29
Cảm ơn câu trả lời rõ ràng. Đây là một giải pháp thuận tiện hơn so với đưa @JsonIgnoretrở lại tham khảo.
Utku zdemir

3
Đây chắc chắn là cách đúng đắn để làm điều đó. Nếu bạn làm như vậy ở phía máy chủ vì bạn sử dụng Jackson ở đó, thì bạn không sử dụng trình ánh xạ json nào ở phía máy khách và bạn không phải đặt con vào sổ tay liên kết cha mẹ. Nó chỉ hoạt động. Cảm ơn Kurt
flosk8

1
Đẹp, giải thích chi tiết và chắc chắn tốt hơn và cách tiếp cận mô tả hơn @JsonIgnore.
Piotr Nowicki

2
Cảm ơn! @JsonIdentityInfo làm việc cho các tham chiếu theo chu kỳ có liên quan đến nhiều thực thể trong nhiều vòng lặp chồng chéo.
n00b

1
Tôi không thể có được điều này để làm việc cho cuộc sống của tôi. Tôi nghĩ rằng tôi có một thiết lập khá giống nhau, nhưng rõ ràng tôi đã có một cái gì đó sai vì tôi không thể nhận được bất cứ điều gì ngoại trừ lỗi tái phát vô hạn:
swv

103

Chú thích mới @JsonIgnoreProperIES giải quyết nhiều vấn đề với các tùy chọn khác.

@Entity

public class Material{
   ...    
   @JsonIgnoreProperties("costMaterials")
   private List<Supplier> costSuppliers = new ArrayList<>();
   ...
}

@Entity
public class Supplier{
   ...
   @JsonIgnoreProperties("costSuppliers")
   private List<Material> costMaterials = new ArrayList<>();
   ....
}

Kiểm tra nó ở đây. Nó hoạt động giống như trong tài liệu:
http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html


@tero - Với cách tiếp cận này, chúng tôi không nhận được dữ liệu liên quan đến thực thể.
Pra_A

@PAA HEY PAA Tôi nghĩ rằng nó được liên kết với các thực thể! tại sao bạn nói vậy
tero17

1
@ tero17 làm thế nào để bạn quản lý đệ quy vô hạn khi bạn có nhiều hơn 2 lớp? Ví dụ: Lớp A -> Lớp B -> Lớp C -> Lớp A. Tôi đã thử với JsonIgnoreProperies mà không gặp may
Villat

@Villat đây là một vấn đề khác cần giải quyết, tôi đề nghị mở một nhu cầu mới cho điều đó.
tero17

+1 cho mẫu mã, với tư cách là một người mới của Jackson, việc sử dụng @JsonIgnoreProperIES không hoàn toàn rõ ràng bằng cách đọc JavaDoc
Wecherowski

47

Ngoài ra, sử dụng Jackson 2.0+ bạn có thể sử dụng @JsonIdentityInfo. Điều này làm việc tốt hơn nhiều cho các lớp ngủ đông của tôi hơn @JsonBackReference@JsonManagedReference, có vấn đề với tôi và không giải quyết được vấn đề. Chỉ cần thêm một cái gì đó như:

@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@traineeId")
public class Trainee extends BusinessObject {

@Entity
@Table(name = "ta_bodystat", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@bodyStatId")
public class BodyStat extends BusinessObject {

và nó nên hoạt động.


Bạn có thể vui lòng giải thích "Điều này làm việc tốt hơn nhiều"? Có một vấn đề với tài liệu tham khảo được quản lý?
Utku zdemir

@ UtkuÖzdemir Tôi đã thêm chi tiết về @JsonIdentityInfocâu trả lời của tôi ở trên.
Kurt Bourbaki

1
đây là giải pháp tốt nhất mà chúng tôi tìm thấy cho đến nay, bởi vì khi chúng tôi sử dụng "@JsonManagedReference", phương thức get đã trả về thành công các giá trị mà không có bất kỳ lỗi stackoverflow nào. Nhưng, khi chúng tôi cố gắng lưu dữ liệu bằng cách sử dụng bài đăng, nó đã trả về lỗi 415 (lỗi phương tiện không được hỗ trợ)
cuser

1
Tôi đã thêm @JsonIdentityInfochú thích vào các thực thể của mình nhưng nó không giải quyết được vấn đề đệ quy. Chỉ @JsonBackReference@JsonManagedReferencegiải quyết, nhưng chúng là loại bỏ các thuộc tính được ánh xạ khỏi JSON.
Oleg Abrazhaev

19

Ngoài ra, Jackson 1.6 có hỗ trợ xử lý các tham chiếu hai chiều ... có vẻ giống như những gì bạn đang tìm kiếm ( mục blog này cũng đề cập đến tính năng này)

Và kể từ tháng 7 năm 2011, cũng có " jackson-module-hibernate " có thể giúp ích trong một số khía cạnh trong việc xử lý các đối tượng Hibernate, mặc dù không nhất thiết phải là đặc biệt này (không yêu cầu chú thích).


Liên kết đã chết, bạn có phiền cập nhật chúng hoặc chỉnh sửa câu trả lời của bạn.
GetMeARemoteJob


9

Điều này làm việc hoàn toàn tốt cho tôi. Thêm chú thích @JsonIgnore vào lớp con nơi bạn đề cập đến tham chiếu đến lớp cha.

@ManyToOne
@JoinColumn(name = "ID", nullable = false, updatable = false)
@JsonIgnore
private Member member;

2
Tôi nghĩ rằng @JsonIgnorebỏ qua thuộc tính này để được lấy về phía khách hàng. Nếu tôi cần thuộc tính này với con của nó (nếu nó có con) thì sao?
Khasan 24-7

6

Hiện tại có một mô-đun Jackson (dành cho Jackson 2) được thiết kế đặc biệt để xử lý các sự cố khởi tạo lười biếng Hibernate khi nối tiếp.

https://github.com/FasterXML/jackson-datatype-hibernate

Chỉ cần thêm phụ thuộc (lưu ý có các phụ thuộc khác nhau cho Hibernate 3 và Hibernate 4):

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-hibernate4</artifactId>
  <version>2.4.0</version>
</dependency>

và sau đó đăng ký mô-đun khi xác định đối tượng ObjectMapper của Jackson:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate4Module());

Tài liệu hiện không tuyệt vời. Xem mã Hibernate4Module để biết các tùy chọn khả dụng.


Vấn đề làm là giải quyết sau đó, bởi vì nó có vẻ thú vị. Tôi có cùng một vấn đề với OP và tất cả các thủ thuật bao gồm cả ở trên chưa hoạt động.
dùng1567291

6

Làm việc tốt với tôi Giải quyết vấn đề đệ quy vô hạn Json khi làm việc với Jackson

Đây là những gì tôi đã thực hiện trong Ánh xạ OneToMany và ManyToOne

@ManyToOne
@JoinColumn(name="Key")
@JsonBackReference
private LgcyIsp Key;


@OneToMany(mappedBy="LgcyIsp ")
@JsonManagedReference
private List<Safety> safety;

Tôi đã sử dụng ánh xạ ngủ đông trong ứng dụng khởi động mùa xuân
Mitchu M

Xin chào Tác giả, Cảm ơn các hướng dẫn tốt đẹp và bài viết tuyệt vời. Tuy nhiên tôi thấy rằng @JsonManagedReference, @JsonBackReferencekhông cung cấp cho bạn dữ liệu liên quan @OneToMany@ManyToOnekịch bản, khi sử dụng @JsonIgnorePropertiescũng bỏ qua dữ liệu thực thể liên quan. Làm thế nào để giải quyết điều này?
Pra_A

5

Đối với tôi giải pháp tốt nhất là sử dụng @JsonViewvà tạo các bộ lọc cụ thể cho từng kịch bản. Bạn cũng có thể sử dụng @JsonManagedReference@JsonBackReference , tuy nhiên, đó là một giải pháp được mã hóa cứng chỉ cho một tình huống, trong đó chủ sở hữu luôn tham chiếu phía sở hữu và không bao giờ ngược lại. Nếu bạn có một kịch bản tuần tự hóa khác, nơi bạn cần chú thích lại thuộc tính khác nhau, bạn sẽ không thể.

Vấn đề

Cho phép sử dụng hai lớp CompanyEmployeenơi bạn có sự phụ thuộc theo chu kỳ giữa chúng:

public class Company {

    private Employee employee;

    public Company(Employee employee) {
        this.employee = employee;
    }

    public Employee getEmployee() {
        return employee;
    }
}

public class Employee {

    private Company company;

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }
}

Và lớp thử nghiệm cố gắng tuần tự hóa bằng cách sử dụng ObjectMapper( Spring Boot ):

@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional
public class CompanyTest {

    @Autowired
    public ObjectMapper mapper;

    @Test
    public void shouldSaveCompany() throws JsonProcessingException {
        Employee employee = new Employee();
        Company company = new Company(employee);
        employee.setCompany(company);

        String jsonCompany = mapper.writeValueAsString(company);
        System.out.println(jsonCompany);
        assertTrue(true);
    }
}

Nếu bạn chạy mã này, bạn sẽ nhận được:

org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)

Giải pháp sử dụng `@ JsonView`

@JsonViewcho phép bạn sử dụng các bộ lọc và chọn những trường cần được đưa vào trong khi tuần tự hóa các đối tượng. Một bộ lọc chỉ là một tham chiếu lớp được sử dụng như một định danh. Vì vậy, trước tiên hãy tạo các bộ lọc:

public class Filter {

    public static interface EmployeeData {};

    public static interface CompanyData extends EmployeeData {};

} 

Hãy nhớ rằng, các bộ lọc là các lớp giả, chỉ được sử dụng để chỉ định các trường có @JsonViewchú thích, vì vậy bạn có thể tạo bao nhiêu tùy ý và cần. Chúng ta hãy xem nó hoạt động, nhưng trước tiên chúng ta cần chú thích Companylớp của chúng ta :

public class Company {

    @JsonView(Filter.CompanyData.class)
    private Employee employee;

    public Company(Employee employee) {
        this.employee = employee;
    }

    public Employee getEmployee() {
        return employee;
    }
}

và thay đổi Kiểm tra để trình tuần tự hóa sử dụng Chế độ xem:

@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional
public class CompanyTest {

    @Autowired
    public ObjectMapper mapper;

    @Test
    public void shouldSaveCompany() throws JsonProcessingException {
        Employee employee = new Employee();
        Company company = new Company(employee);
        employee.setCompany(company);

        ObjectWriter writter = mapper.writerWithView(Filter.CompanyData.class);
        String jsonCompany = writter.writeValueAsString(company);

        System.out.println(jsonCompany);
        assertTrue(true);
    }
}

Bây giờ nếu bạn chạy mã này, vấn đề đệ quy vô hạn đã được giải quyết, bởi vì bạn đã nói rõ ràng rằng bạn chỉ muốn tuần tự hóa các thuộc tính được chú thích với @JsonView(Filter.CompanyData.class) .

Khi nó đạt đến tham chiếu trở lại cho công ty trong Employee, nó sẽ kiểm tra xem nó không được chú thích và bỏ qua việc xê-ri hóa. Bạn cũng có một giải pháp mạnh mẽ và linh hoạt để chọn dữ liệu bạn muốn gửi qua API REST.

Với Spring, bạn có thể chú thích các phương thức Bộ điều khiển REST của mình với mong muốn @JsonView bộ lọc và việc tuần tự hóa được áp dụng trong suốt cho đối tượng trả về.

Dưới đây là nhập khẩu được sử dụng trong trường hợp bạn cần kiểm tra:

import static org.junit.Assert.assertTrue;

import javax.transaction.Transactional;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;

import com.fasterxml.jackson.annotation.JsonView;

1
Đây là một bài viết hay giải thích nhiều giải pháp thay thế để giải quyết các cuộc thám hiểm
Hugo Baés

5

@JsonIgnoreProperies là câu trả lời.

Sử dụng một cái gì đó như thế này ::

@OneToMany(mappedBy = "course",fetch=FetchType.EAGER)
@JsonIgnoreProperties("course")
private Set<Student> students;

Sử dụng nó một cách tự tin như tôi đã thấy Jhipster sử dụng mã này trong mã được tạo của nó
ifelse.codes

Cảm ơn câu trả lời. Tuy nhiên tôi thấy rằng @JsonManagedReference, @JsonBackReferencekhông cung cấp cho bạn dữ liệu liên quan @OneToMany@ManyToOnekịch bản, khi sử dụng @JsonIgnorePropertiescũng bỏ qua dữ liệu thực thể liên quan. Làm thế nào để giải quyết điều này?
Pra_A

4

Trong trường hợp của tôi, nó đã đủ để thay đổi quan hệ từ:

@OneToMany(mappedBy = "county")
private List<Town> towns;

đến:

@OneToMany
private List<Town> towns;

một mối quan hệ khác vẫn như cũ:

@ManyToOne
@JoinColumn(name = "county_id")
private County county;

2
Tôi nghĩ tốt hơn là sử dụng giải pháp của Kurt. Bởi vì giải pháp JoinColumn có thể kết thúc trong các xác chết dữ liệu không được ước tính.
flosk8

1
Đây thực sự là điều duy nhất giúp tôi. Không có giải pháp khác từ đầu làm việc. Tôi vẫn không chắc tại sao ...
Deniss M.

4

Hãy chắc chắn rằng bạn sử dụng com.fasterxml.jackson ở mọi nơi. Tôi đã dành nhiều thời gian để tìm ra nó.

<properties>
  <fasterxml.jackson.version>2.9.2</fasterxml.jackson.version>
</properties>

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>${fasterxml.jackson.version}</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${fasterxml.jackson.version}</version>
</dependency>

Sau đó sử dụng @JsonManagedReference@JsonBackReference.

Cuối cùng, bạn có thể tuần tự hóa mô hình của mình thành JSON:

import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(model);

4

Bạn có thể sử dụng @JsonIgnore , nhưng điều này sẽ bỏ qua dữ liệu json có thể được truy cập do mối quan hệ Khóa ngoài. Do đó, nếu bạn yêu cầu dữ liệu khóa ngoại (hầu hết thời gian chúng tôi yêu cầu), thì @JsonIgnore sẽ không giúp bạn. Trong tình huống như vậy xin vui lòng làm theo các giải pháp dưới đây.

bạn đang nhận được đệ quy vô hạn, vì lớp BodyStat lại nhắc đến đối tượng Thực tập sinh

BodyStat

@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name="trainee_fk")
private Trainee trainee;

Học viên

@OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Column(nullable = true)
private Set<BodyStat> bodyStats;

Do đó, bạn phải bình luận / bỏ qua phần trên trong Thực tập sinh


1
Trong trường hợp của tôi, nó không hoạt động. Bạn có thể vui lòng xem: github.com/JavaHelper/su-jackson-boot ?
Pra_A

3

Tôi cũng gặp vấn đề tương tự. Tôi đã từng @JsonIdentityInfo's ObjectIdGenerators.PropertyGenerator.classloại máy phát điện.

Đó là giải pháp của tôi:

@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Trainee extends BusinessObject {
...

2
Câu trả lời hay
LAGRIDA

1
Tôi sẽ trả lời điều tương tự !!! Nice;)
Felipe Desiderati

3

Bạn nên sử dụng @JsonBackReference với thực thể @ManyToOne và @JsonManagedReference với @onetomany chứa các lớp thực thể.

@OneToMany(
            mappedBy = "queue_group",fetch = FetchType.LAZY,
            cascade = CascadeType.ALL
        )
    @JsonManagedReference
    private Set<Queue> queues;



@ManyToOne(cascade=CascadeType.ALL)
        @JoinColumn(name = "qid")
       // @JsonIgnore
        @JsonBackReference
        private Queue_group queue_group;

Nếu tôi đặt chú thích @ jsonIgnore ở trẻ. Tôi không thể lấy đối tượng cha mẹ từ con. Khi tôi cố gắng đưa con đi. tại sao đối tượng cha mẹ không đến, nó bị bỏ qua bởi @ jsonignore. cho tôi biết cách để đi từ con đến cha mẹ và cha mẹ thành con.
Kumaresan Perumal

Không cần sử dụng @JsonIgnore chỉ cần sử dụng các chú thích ở trên và Để nhận được sự phản đối của cha mẹ và con cái bằng cách sử dụng Getters và setters. và Jsonignore cũng đang làm như vậy nhưng nó sẽ tạo ra đệ quy vô hạn. Nếu bạn chia sẻ mã của mình thì tôi có thể kiểm tra lý do tại sao bạn không nhận được đối tượng. Bởi vì đối với tôi cả hai đang đến.
Shubham

Ý tôi nói là. khi lấy cha mẹ. Cha mẹ nên đi kèm với đối tượng con. khi lấy đối tượng con. Đứa trẻ nên đi cùng với cha mẹ. Nó không hoạt động trong kịch bản này. Liệu bạn có thể giúp mình không?
Kumaresan Perumal

1

bạn có thể sử dụng mô hình DTO tạo lớp TraineeDTO mà không cần bất kỳ hiberbnate anotation nào và bạn có thể sử dụng trình ánh xạ jackson để chuyển Trainee thành TraineeDTO và bingo thông báo lỗi không chấp nhận :)


1

Nếu bạn không thể bỏ qua thuộc tính, hãy thử sửa đổi mức độ hiển thị của trường. Trong trường hợp của chúng tôi, chúng tôi đã có mã cũ vẫn gửi các thực thể có mối quan hệ, vì vậy trong trường hợp của tôi, đây là cách khắc phục:

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private Trainee trainee;

Nếu tôi đặt chú thích @ jsonIgnore ở trẻ. Tôi không thể lấy đối tượng cha mẹ từ con. Khi tôi cố gắng đưa con đi. tại sao đối tượng cha mẹ không đến, nó bị bỏ qua bởi @ jsonignore. cho tôi biết cách để đi từ con đến cha mẹ và cha mẹ thành con.
Kumaresan Perumal

0

Tôi đã gặp vấn đề này, nhưng tôi không muốn sử dụng chú thích trong các thực thể của mình, vì vậy tôi đã giải quyết bằng cách tạo một hàm tạo cho lớp của mình, hàm tạo này không được có tham chiếu lại cho các thực thể tham chiếu thực thể này. Hãy nói kịch bản này.

public class A{
   private int id;
   private String code;
   private String name;
   private List<B> bs;
}

public class B{
   private int id;
   private String code;
   private String name;
   private A a;
}

Nếu bạn cố gửi cho lớp xem Bhoặc Avới @ResponseBodynó có thể gây ra một vòng lặp vô hạn. Bạn có thể viết một hàm tạo trong lớp của bạn và tạo một truy vấn với entityManagernhư thế này.

"select new A(id, code, name) from A"

Đây là lớp với các nhà xây dựng.

public class A{
   private int id;
   private String code;
   private String name;
   private List<B> bs;

   public A(){
   }

   public A(int id, String code, String name){
      this.id = id;
      this.code = code;
      this.name = name;
   }

}

Tuy nhiên, có một số hạn chế về giải pháp này, như bạn có thể thấy, trong hàm tạo tôi không tạo tham chiếu đến Danh sách bs vì Hibernate không cho phép, ít nhất là trong phiên bản 3.6.10.Final , vì vậy khi tôi cần để hiển thị cả hai thực thể trong một khung nhìn tôi làm như sau.

public A getAById(int id); //THE A id

public List<B> getBsByAId(int idA); //the A id.

Vấn đề khác với giải pháp này là nếu bạn thêm hoặc xóa một thuộc tính, bạn phải cập nhật hàm tạo và tất cả các truy vấn của mình.


0

Trong trường hợp bạn đang sử dụng Spring Data Rest, vấn đề có thể được giải quyết bằng cách tạo Kho lưu trữ cho mọi Thực thể liên quan đến các tham chiếu theo chu kỳ.


0

Tôi là một người đến muộn và đó là một chủ đề dài. Nhưng tôi đã dành một vài giờ để cố gắng tìm ra điều này, và muốn đưa ra trường hợp của tôi như một ví dụ khác.

Tôi đã thử cả hai giải pháp JsonIgnore, JsonIgnoreProperies và BackReference, nhưng thật kỳ lạ là nó giống như chúng không được chọn.

Tôi đã sử dụng Lombok và nghĩ rằng có thể nó can thiệp, vì nó tạo ra các hàm tạo và ghi đè lênString (đã thấy toString trong stackoverflowerror).

Cuối cùng, đó không phải là lỗi của Lombok - Tôi đã sử dụng thế hệ thực thể JPA tự động của NetBeans từ các bảng cơ sở dữ liệu, mà không cần suy nghĩ nhiều - tốt, và một trong những chú thích được thêm vào các lớp được tạo là @XmlRootEuity. Khi tôi gỡ bỏ nó, mọi thứ bắt đầu hoạt động. Ồ tốt


0

Vấn đề là đặt @JsonIgnore trong phương thức setter như sau. trong trường hợp của tôi.

Thị trấn.java

@Access(AccessType.PROPERTY)
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name="townshipId", nullable=false ,insertable=false, updatable=false)
public List<Village> getVillages() {
    return villages;
}

@JsonIgnore
@Access(AccessType.PROPERTY)
public void setVillages(List<Village> villages) {
    this.villages = villages;
}

Village.java

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "townshipId", insertable=false, updatable=false)
Township township;

@Column(name = "townshipId", nullable=false)
Long townshipId;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.