জ্যাকসন জেএসন এবং হাইবারনেট জেপিএ ইস্যুতে অসীম পুনরাবৃত্তি


412

একটি JPA অবজেক্টের দ্বি-দিকনির্দেশক সমিতিটি JSON এ রূপান্তর করার চেষ্টা করার সময়, আমি পেতে থাকি

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

আমি যা খুঁজে পেয়েছি তা এই থ্রেড যা দ্বি-দিকনির্দেশক সমিতিগুলি এড়ানোর পরামর্শ দিয়ে মূলত শেষ হয় conc এই বসন্ত বাগের জন্য কারও কাছে কাজের ধারণা আছে?

------ সম্পাদনা 2010-07-24 16:26:22 -------

Codesnippets:

ব্যবসায়িক বিষয় 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 ...

ব্যবসায়িক উদ্দেশ্য 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;

নিয়ন্ত্রক:

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;
    }    
}

প্রশিক্ষক ডিএওর জেপিএ-বাস্তবায়ন:

@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();
    }
}

persistence.xml

<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>

যোগ @Transientকরুন Trainee.bodyStats
ফিল্ড 294

2017 এর হিসাবে, @JsonIgnorePropertiesসবচেয়ে পরিষ্কার সমাধান। পরীক্ষা করে দেখুন Zammel AlaaEddine এর উত্তর আরো বিস্তারিত জানার জন্য।
উত্কু

এই বসন্তের দোষ কেমন ??
নাথান হিউজেস

উত্তর:


293

আপনি @JsonIgnoreচক্রটি ভাঙ্গতে ব্যবহার করতে পারেন ।


1
@ বেন: আসলে আমি জানি না। সম্ভবত এর সমর্থন সক্ষম করা যায় নি: wiki.fasterxml.com/
জ্যাকসনজেএএক্সবিএ

40
জ্যাকসন ১.6 এর থেকে আরও ভাল সমাধান রয়েছে: সিরিয়ালাইজেশনের সময় গ্রাহকরা / সেটটারগুলি উপেক্ষা না করে আপনি অসীম পুনরাবৃত্তি সমস্যা সমাধানের জন্য দুটি নতুন টিকা ব্যবহার করতে পারেন। বিস্তারিত জানার জন্য নীচে আমার উত্তর দেখুন।
কুর্ট বাউরবাাকি

1
নিখুঁত উত্তরের জন্য ধন্যবাদ বিটিডব্লিউ, আমি আরও একটি সমাধান নিয়ে এসেছি: আপনি সহজেই এই মানটির জন্য জেটর তৈরি করা এড়াতে পারেন, সুতরাং জেএসএন তৈরি করার সময় স্প্রিং এটিকে অ্যাক্সেস করতে সক্ষম হবে না (তবে আমি মনে করি না যে এটি প্রতিটি ক্ষেত্রে উপযুক্ত, তাই আপনার উত্তর আরও ভাল)
সেমিওন ডানিলভ

11
উপরের সমস্ত সমাধানগুলিতে টীকা যুক্ত করে ডোমেন অবজেক্টগুলি পরিবর্তন করা দরকার বলে মনে হচ্ছে। আমি যদি তৃতীয় পক্ষের ক্লাসিকে সিরিয়ালাইজ করি তবে এগুলি সংশোধন করার মতো আমার কোনও উপায় নেই। আমি কীভাবে এই সমস্যাটি এড়াতে পারি?
জিয়ানওয়ু চেন

2
এই সমাধানটি কিছু পরিস্থিতিতে কাজ করে না। জেপিএর সাথে সম্পর্কিত ডেটাবেজে, আপনি @JsonIgnoreসত্তা আপডেট করার সময় আপনি "বিদেশী কী" র বাতিল করে দেবেন ...
স্লিম

628

জসনআইগনোরপ্রোপার্টিস [2017 আপডেট]:

আপনি এখন বৈশিষ্ট্যগুলির সিরিয়ালাইজেশন (সিরিয়ালাইজেশন চলাকালীন) দমন করতে , বা জেএসওএন বৈশিষ্ট্যগুলি ( ডিসিরিয়ালেকরণের সময়) প্রক্রিয়াকরণ উপেক্ষা করার জন্য জসনআইগনোরপ্রেটিস ব্যবহার করতে পারেন । আপনি যা সন্ধান করছেন এটি যদি না হয় তবে দয়া করে নীচে পড়তে থাকুন।

(এটি নির্দেশ করার জন্য আস জামাল আলাএডাইনকে ধন্যবাদ)


JsonManagedReferences এবং JsonBackReferences

জ্যাকসন ১.6 যেহেতু সিরিয়ালাইজেশনের সময় গ্রাহকরা / সেটটারগুলি উপেক্ষা না করে আপনি অসীম পুনরাবৃত্তি সমস্যা সমাধানের জন্য দুটি টিকা ব্যবহার করতে পারেন: @JsonManagedReferenceএবং @JsonBackReference

ব্যাখ্যা

জ্যাকসন ভালভাবে কাজ করতে, সম্পর্কের দুটি দিকের একটিকে ক্রমিক করা উচিত নয়, যাতে আপনার স্ট্যাকওভারফ্লো ত্রুটির কারণ হয় এমন ইনফাইট লুপ এড়াতে পারে।

সুতরাং, জ্যাকসন রেফারেন্সের সামনের অংশটি (আপনার Set<BodyStat> bodyStatsপ্রশিক্ষণার্থী ক্লাসে) নেন এবং এটিকে জসনের মতো স্টোরেজ ফর্ম্যাটে রূপান্তর করেন; এটি তথাকথিত মার্শালিং প্রক্রিয়া। তারপরে, জ্যাকসন রেফারেন্সের পিছনের অংশটি (অর্থাত্ Trainee traineeবডিস্ট্যাট শ্রেণিতে) সন্ধান করে এবং এটি যেমনটি রেখে দেয় তেমনি এটি সিরিয়াল করে না। সম্পর্কের এই অংশটি পূর্বে উল্লেখের deserialization ( unmarshalling ) এর সময় পুনর্নির্মাণ করা হবে ।

আপনি আপনার কোডটি এভাবে পরিবর্তন করতে পারেন (আমি অকেজো অংশগুলি এড়িয়ে চলেছি):

ব্যবসায়িক বিষয় 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;

ব্যবসায়িক উদ্দেশ্য 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;

এখন এটি সব সঠিকভাবে কাজ করা উচিত।

আপনি যদি আরও তথ্য চান তবে আমি আমার ব্লগ কেইনফর্ম্যাটিকসে জসন এবং জ্যাকসন স্ট্যাকওভারফ্লো বিষয়গুলি সম্পর্কে একটি নিবন্ধ লিখেছি ।

সম্পাদনা করুন:

আর একটি দরকারী মন্তব্য যা আপনি যাচাই করতে পারেন তা হ'ল @ জসোনআইডেন্টিটি ইনফো : এটি ব্যবহার করে, প্রতিবার জ্যাকসন আপনার বস্তুকে সিরিয়াল করে দেয়, এটি এতে একটি আইডি (বা আপনার পছন্দের অন্য কোনও বৈশিষ্ট্য) যুক্ত করবে, যাতে এটি একেবারে আর পুরোপুরি "স্ক্যান" করে না। আপনি যখন আরও আন্তঃসম্পর্কিত বস্তুর (উদাহরণস্বরূপ: অর্ডার -> অর্ডারলাইন -> ব্যবহারকারী -> অর্ডার এবং তারপরে) এর মধ্যে একটি চেইন লুপ পেয়েছেন তখন এটি কার্যকর হতে পারে।

এক্ষেত্রে আপনাকে সাবধানতা অবলম্বন করতে হবে, যেহেতু আপনাকে একবারে আপনার সামগ্রীর বৈশিষ্ট্যগুলি একবারে পড়ার প্রয়োজন হতে পারে (উদাহরণস্বরূপ, একই বিক্রয়কারীকে ভাগ করে নেওয়া এমন আরও কয়েকটি পণ্য সহ একটি পণ্যের তালিকায়) এবং এই টীকাগুলি আপনাকে এটি করতে বাধা দেয়। জসন প্রতিক্রিয়া যাচাই করতে এবং আপনার কোডটিতে কী চলছে তা দেখতে আমি সর্বদা ফায়ারব্যাগ লগগুলিতে নজর দেওয়ার পরামর্শ দিই।

সূত্র:


29
সুস্পষ্ট উত্তরের জন্য ধন্যবাদ। @JsonIgnoreপিছনে রেফারেন্স রাখার চেয়ে এটি আরও সুবিধাজনক সমাধান ।
উত্কু dezdemir

3
এটি অবশ্যই এটি করার সঠিক উপায়। আপনি যদি এটি সার্ভার সাইডে এটি করেন কারণ আপনি সেখানে জ্যাকসন ব্যবহার করেন, আপনি ক্লায়েন্টের পক্ষে কী জেসন ম্যাপার ব্যবহার করেন তা বিবেচ্য নয় এবং আপনি সন্তানের পিতামাতার লিঙ্ক ম্যানুয়ালটিতে সেট করতে হবে না। এটা ঠিক কাজ করে। ধন্যবাদ কার্ট
flosk8

1
সুন্দর, বিস্তারিত ব্যাখ্যা এবং এর চেয়ে অবশ্যই আরও ভাল এবং আরও বর্ণনামূলক পদ্ধতির @JsonIgnore
পাইওটর নওইকি

2
ধন্যবাদ! @ জসনআইডেন্টিটি ইন্ফো চক্রীয় রেফারেন্সের জন্য কাজ করেছিল যা বহু ওভারল্যাপিং লুপগুলিতে একাধিক সত্তাকে জড়িত।
n00b

1
আমার জীবনের জন্য এটি কাজ করতে পারি না। আমার মনে হয় আমার বেশ একই রকম সেটআপ আছে তবে আমি স্পষ্টতই কিছু ভুল পেয়েছি যেহেতু আমি অসীম পুনরুক্তির ত্রুটি ছাড়া কিছুই পেতে পারি না:
swv

103

নতুন টিকা @ জসনআইগনোরপ্রটিসগুলি অন্যান্য বিকল্পগুলির সাথে অনেকগুলি সমস্যার সমাধান করে।

@Entity

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

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

এটি এখানে দেখুন। এটি ডকুমেন্টেশনের মতোই কাজ করে:
http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html


@ অ্যারো - এই পদ্ধতির পাশাপাশি আমরা সত্তার সাথে সম্পর্কিত ডেটা পাই না।
Pra_A

@ PAA হেই PAA আমি মনে করি এটি সত্তার সাথে সম্পর্কিত! কেন আপনি বলেন?
tero17

1
@ টেরো 17 যখন আপনার 2 টিরও বেশি ক্লাস থাকবে তখন আপনি কীভাবে অসীম পুনরাবৃত্তি পরিচালনা করবেন? উদাহরণস্বরূপ: Class A -> Class B -> Class C -> Class A. আমি ভাগ্য ছাড়াই JsonIgnorePererties এর সাথে চেষ্টা করেছি
Villat

@ উইলাত এটি সমাধানের আরেকটি সমস্যা, আমি এটির জন্য একটি নতুন দাবি খোলার পরামর্শ দিই।
tero17

কোড নমুনার জন্য +1, জ্যাকসন নবাগত হিসাবে @ জসনআইগনোরপ্রিয়েটির ব্যবহার পুরোপুরি জাভাডোক পড়ার মাধ্যমে পরিষ্কার ছিল না
ওয়াচেরোস্কি

47

এছাড়াও, জ্যাকসন ২.০+ ব্যবহার করে আপনি ব্যবহার করতে পারেন @JsonIdentityInfo। এটি আমার হাইবারনেট শ্রেণীর চেয়ে অনেক ভাল কাজ করেছে @JsonBackReferenceএবং @JsonManagedReferenceযা আমার জন্য সমস্যা ছিল এবং সমস্যাটি সমাধান করেনি। শুধু কিছু যুক্ত করুন:

@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 {

এবং এটি কাজ করা উচিত।


আপনি কি "এটি আরও ভাল কাজ করেছে" ব্যাখ্যা করতে পারেন? পরিচালিত রেফারেন্সে কোনও সমস্যা আছে?
উত্কু dezdemir

@ উত্কুজেডেমির আমি @JsonIdentityInfoউপরে আমার উত্তর সম্পর্কে বিশদ যুক্ত করেছি।
কর্ট বাউরবাাকি

1
এটি এখন অবধি পাওয়া সেরা সমাধান, কারণ আমরা যখন "@JsonManagedReferences" ব্যবহার করতাম তখন get পদ্ধতিটি কোনও স্ট্যাকওভারফ্লো ত্রুটি ছাড়াই সাফল্যের সাথে মানগুলি ফিরিয়ে দেয়। তবে, যখন আমরা পোস্টটি ব্যবহার করে ডেটা সংরক্ষণ করার চেষ্টা করেছি, তখন এটি 415 এর একটি ত্রুটি ফিরে
পেয়েছে

1
আমি @JsonIdentityInfoআমার সত্তাগুলিতে টিকা যুক্ত করেছি তবে এটি পুনরাবৃত্তি সমস্যা সমাধান করে না। কেবলমাত্র @JsonBackReferenceএবং @JsonManagedReferenceসমাধান করে তবে তারা JSON থেকে ম্যাপযুক্ত বৈশিষ্ট্যগুলি সরিয়ে ফেলছে।
ওলেগ আব্রাজাইভ

19

এছাড়াও, জ্যাকসন 1.6 এর সমর্থন রয়েছে -এর দ্বি-দিকনির্দেশক রেফারেন্সগুলি পরিচালনা করার ... যা আপনি যা খুঁজছেন তা দেখে মনে হচ্ছে ( এই ব্লগ এন্ট্রিতেও বৈশিষ্ট্যটি উল্লেখ করা হয়েছে)

এবং জুলাই ২০১১ পর্যন্ত, " জ্যাকসন-মডিউল-হাইবারনেট " রয়েছে যা হাইবারনেট অবজেক্টগুলির সাথে ডিল করার কিছু দিকগুলিতে সহায়তা করতে পারে, যদিও এই নির্দিষ্ট কোনওটির প্রয়োজন নেই (যার জন্য এনোটেশন প্রয়োজন)।


লিঙ্কগুলি মারা গেছে, সেগুলি আপডেট করার বা আপনার উত্তরটি সম্পাদনা করতে আপনি কি আপত্তি করেন?
getMeARemoteJob


9

এটি আমার পক্ষে পুরোপুরি ঠিকঠাক কাজ করেছে। আপনি পিতামাত্ত শ্রেণীর রেফারেন্সটি উল্লেখ করেছেন এমন শিশু ক্লাসে @JsonIgnore টিকা যোগ করুন।

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

2
আমি মনে করি @JsonIgnoreক্লায়েন্ট পক্ষ থেকে পুনরুদ্ধার করা থেকে এই বৈশিষ্ট্যটিকে উপেক্ষা করে। যদি আমার তার সন্তানের সাথে এই বৈশিষ্ট্যটির প্রয়োজন হয় (যদি এটির সন্তান থাকে) তবে?
খাসন 24-7

6

সিরিয়ালাইজেশনের সময় হাইবারনেট অলস প্রারম্ভিকরণের সমস্যাগুলি হ্যান্ডেল করার জন্য বিশেষভাবে ডিজাইন করা একটি জ্যাকসন মডিউল রয়েছে (জ্যাকসন 2 এর জন্য)।

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

কেবল নির্ভরতা যুক্ত করুন (দ্রষ্টব্য হাইবারনেট 3 এবং হাইবারনেট 4 এর জন্য পৃথক নির্ভরতা রয়েছে নোট করুন):

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

এবং তারপরে জ্যাকসনের অবজেক্টম্যাপারকে অন্তর্নিহিত করার সময় মডিউলটি নিবন্ধ করুন:

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

ডকুমেন্টেশন বর্তমানে দুর্দান্ত নয়। উপলভ্য বিকল্পগুলির জন্য হাইবারনেট 4 মড্যুল কোডটি দেখুন ।


কোন সমস্যাটি তখন সমাধান হয় কারণ এটি আকর্ষণীয় দেখাচ্ছে। আমার যেমন ওপি হিসাবে একই সমস্যা আছে এবং উপরোক্ত সমস্ত কৌতুক কাজ করে নি।
ব্যবহারকারী 1567291

6

আমার পক্ষে ভাল কাজ করা জ্যাকসনের সাথে কাজ করার সময় জসন অসীম পুনরাবৃত্তি সমস্যাটি সমাধান করুন

এটিই আমি ওয়ান টোমনি এবং ম্যান্টিটোওন ম্যাপিংয়ে করেছি

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


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

আমি বসন্ত বুট অ্যাপ্লিকেশনটিতে হাইবারনেট ম্যাপিং ব্যবহার করেছি
প্রু এম এম

হাই লেখক, সুন্দর টিউটোরিয়াল এবং দুর্দান্ত পোস্টের জন্য ধন্যবাদ। তবে আমি দেখতে পেয়েছি যে @JsonManagedReference, @JsonBackReferenceআপনাকে সম্পর্কিত ডেটা @OneToManyএবং দৃশ্যাবলী দেয় না, @ManyToOneব্যবহার করার সময়ও @JsonIgnorePropertiesসংশ্লিষ্ট সত্তা ডেটা এড়িয়ে যায়। কীভাবে সমাধান করবেন?
Pra_A

5

আমার জন্য সর্বোত্তম সমাধান হ'ল @JsonViewপ্রতিটি দৃশ্যের জন্য নির্দিষ্ট ফিল্টার ব্যবহার এবং তৈরি করা। আপনি এটিও ব্যবহার করতে পারেন @JsonManagedReferenceএবং @JsonBackReferenceতবে এটি কেবলমাত্র একটি পরিস্থিতিতে একটি হার্ডকোডযুক্ত সমাধান, যেখানে মালিক সর্বদা নিজস্ব পক্ষের উল্লেখ করে এবং এর বিপরীতে কখনও নয়। আপনার যদি আর একটি সিরিয়ালাইজেশন দৃশ্য থাকে যেখানে আপনাকে বৈশিষ্ট্যটিকে আলাদাভাবে নতুন করে বানাতে হবে, আপনি সক্ষম হবেন না।

সমস্যা

দুটি ক্লাস ব্যবহার করা যাক Companyএবং Employeeযেখানে আপনি তাদের মধ্যে একটি চক্রীয় নির্ভরতা রেখেছেন:

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;
    }
}

এবং পরীক্ষামূলক শ্রেণি যা ObjectMapper( স্প্রিং বুট ) ব্যবহার করে সিরিয়ালাইজ করার চেষ্টা করে :

@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);
    }
}

আপনি যদি এই কোডটি চালনা করেন তবে আপনি এটি পাবেন:

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

সমাধান `@ জসনভিউ` ব্যবহার করে`

@JsonViewফিল্টারগুলি ব্যবহার করতে এবং অবজেক্টগুলিকে সিরিয়ালকরণ করার সময় কোন ক্ষেত্রগুলি অন্তর্ভুক্ত করা উচিত তা চয়ন করতে আপনাকে সক্ষম করে। একটি ফিল্টার শনাক্তকারী হিসাবে ব্যবহৃত কেবলমাত্র একটি শ্রেণীর রেফারেন্স। সুতরাং প্রথমে ফিল্টারগুলি তৈরি করা যাক:

public class Filter {

    public static interface EmployeeData {};

    public static interface CompanyData extends EmployeeData {};

} 

মনে রাখবেন, ফিল্টারগুলি ডামি ক্লাস, কেবলমাত্র @JsonViewটীকা সহ ক্ষেত্রগুলি নির্দিষ্ট করার জন্য ব্যবহৃত হয় , যাতে আপনি যতগুলি চান এবং যতগুলি প্রয়োজন তৈরি করতে পারেন। আসুন এটি কার্যকরভাবে দেখা যাক, তবে প্রথমে আমাদের Companyক্লাসটি টিকিয়ে দেওয়া দরকার :

public class Company {

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

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

    public Employee getEmployee() {
        return employee;
    }
}

এবং সিরিয়ালাইজারের ভিউটি ব্যবহারের জন্য টেস্টটি পরিবর্তন করুন:

@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);
    }
}

এখন আপনি যদি এই কোডটি চালনা করেন তবে অসীম পুনরাবৃত্তি সমস্যাটি সমাধান হয়ে গেছে, কারণ আপনি স্পষ্টভাবে বলেছেন যে আপনি যে বৈশিষ্ট্যগুলির সাথে বর্ণিত হয়েছিল সেগুলি ক্রমিকায়িত করতে চান @JsonView(Filter.CompanyData.class)

এটি যখন কোম্পানির জন্য পিছনের রেফারেন্সে পৌঁছায় Employee, এটি পরীক্ষা করে যে এটি বর্ণিত নয় এবং সিরিয়ালাইজেশনটিকে উপেক্ষা করে। আপনি আপনার REST এপিআইগুলির মাধ্যমে কোন ডেটা প্রেরণ করতে চান তা চয়ন করার জন্য আপনার কাছে একটি শক্তিশালী এবং নমনীয় সমাধান রয়েছে।

বসন্তের সাথে আপনি পছন্দসই @JsonViewফিল্টারটি দিয়ে আপনার REST কন্ট্রোলারগুলির পদ্ধতিগুলি টীকাতে পারেন এবং সিরিয়ালাইজেশনটি স্বচ্ছভাবে প্রত্যাবর্তিত অবজেক্টে প্রয়োগ করা হয়।

আপনার যাচাই করা দরকার হলে এখানে আমদানিগুলি ব্যবহৃত হয়:

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
পুনরাবৃত্তিগুলি সমাধান করার জন্য অনেক বিকল্প সমাধান ব্যাখ্যা করে এটি একটি দুর্দান্ত নিবন্ধ: baeldung.com/…
হুগো বাশ

5

@ JsonIgnoreProperties- এর উত্তর।

এরকম কিছু ব্যবহার করুন ::

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

এটিকে আত্মবিশ্বাসের সাথে ব্যবহার করুন যেমন আমি দেখেছি জিপস্টার তার উত্পন্ন কোডে এটি ব্যবহার করে
ifelse.codes

উত্তরের জন্য ধন্যবাদ. তবে আমি দেখতে পেয়েছি যে @JsonManagedReference, @JsonBackReferenceআপনাকে সম্পর্কিত ডেটা @OneToManyএবং দৃশ্যাবলী দেয় না, @ManyToOneব্যবহার করার সময়ও @JsonIgnorePropertiesসংশ্লিষ্ট সত্তা ডেটা এড়িয়ে যায়। কীভাবে সমাধান করবেন?
Pra_A

4

আমার ক্ষেত্রে এটি থেকে সম্পর্ক পরিবর্তন করার পক্ষে যথেষ্ট ছিল:

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

প্রতি:

@OneToMany
private List<Town> towns;

অন্য সম্পর্কটি যেমন ছিল তেমন রয়ে গেল:

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

2
আমি মনে করি কার্টের সমাধানটি ব্যবহার করা আরও ভাল। কারণ জয়লোকলম সমাধানটি অবাস্তব ডেটা মৃতদেহগুলিতে শেষ হতে পারে।
flosk8

1
আসলে এটিই আমাকে সাহায্য করেছিল। শীর্ষ থেকে অন্য কোনও সমাধান কাজ করে না। আমি এখনও নিশ্চিত নই কেন ...
ডেনিস এম

4

আপনি যে কোনও জায়গায় com.fasterxml.jackson ব্যবহার করেছেন তা নিশ্চিত হন । আমি এটি খুঁজে বের করতে অনেক সময় ব্যয় করেছি।

<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>

তারপরে @JsonManagedReferenceএবং ব্যবহার করুন @JsonBackReference

শেষ অবধি, আপনি আপনার মডেলটিকে JSON এ ক্রমিক করতে পারেন:

import com.fasterxml.jackson.databind.ObjectMapper;

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

4

আপনি @ জসনআইগনর ব্যবহার করতে পারেন , তবে এটি বিদেশী কী সম্পর্কের কারণে অ্যাক্সেস করা যায় এমন জসন ডেটা উপেক্ষা করবে। সুতরাং আপনি যদি বিদেশী কী ডেটা (আমাদের বেশিরভাগ সময় প্রয়োজন) পুনর্বার করেন তবে @ জসনআইগনোর আপনাকে সাহায্য করবে না। এই পরিস্থিতিতে নীচের সমাধান অনুসরণ করুন।

তোমার কারনে অসীম recursion পাচ্ছেন BodyStat বর্গ আবার উল্লেখ ট্রেইনি বস্তুর

BodyStat

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

শাগরেদ

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

অতএব, আপনি প্রশিক্ষণার্থী উপরের অংশ মন্তব্য / বাদ দিতে হবে


1
আমার ক্ষেত্রে এটি কাজ করছে না। আপনি কি দয়া করে একবার দেখে নিতে পারেন: github.com/ জাভাহেল্পার / বিস্মিত-জ্যাকসন- বুট ?
Pra_A

3

আমিও একই সমস্যা পূরণ করেছি। আমি ব্যবহৃত @JsonIdentityInfo'র ObjectIdGenerators.PropertyGenerator.classজেনারেটর প্রকার।

এটাই আমার সমাধান:

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

2
ভাল উত্তর
ল্যাগ্রিডা

1
আমি একই জিনিস উত্তর হবে !!! চমৎকার;)
ফিলিপ ডিজিডারেটি

3

আপনার @ জেসনব্যাকের রেফারেন্সটি @ মানি টোওন সত্তার সাথে এবং @ জেসনম্যানেজড রেফারেন্সটি ব্যবহার করতে হবে অ্যান্টিটি ক্লাস সমেত @ অনেটোম্যানির সাথে।

@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;

যদি আমি @ json আইনে রাখি তবে শিশুটিতে টীকাটি উপেক্ষা করুন। আমি সন্তানের কাছ থেকে পিতামাতাকে পেতে পারি না w যখন আমি শিশুটিকে নেওয়ার চেষ্টা করি। প্যারেন্ট অবজেক্ট কেন আসছে না, এটি @ jsonignore দ্বারা উপেক্ষা করা হবে। আমাকে সন্তানের কাছ থেকে পিতা-মাতার কাছে এবং অভিভাবকের কাছে সন্তানের কাছে যাওয়ার উপায় বলুন।
কুমারসান পেরুমাল

@ জেসনআইগনোর ব্যবহারের প্রয়োজন নেই কেবল উপরের টীকাগুলি এবং পিতামাতা এবং সন্তানের আপত্তি পাওয়ার জন্য গেটরস এবং সেটার ব্যবহার করে প্রয়োজন নেই। এবং জসিনগনোরও একই কাজ করছে তবে এটি অসীম পুনরাবৃত্তি তৈরি করবে। আপনি যদি নিজের কোডটি ভাগ করেন তবে আমি কেন পরীক্ষা করতে পারি আপনি কেন বস্তু পাচ্ছেন না। কারণ আমার জন্য দুজনেই আসছেন।
শুভম

আমি বলতে চাই. যখন একটি পিতামাতার গ্রহণ। পিতা-মাতার উচিত শিশু অবজেক্ট নিয়ে আসা উচিত। সন্তানের আপত্তি যখন। সন্তানের পিতামাতার সাথে আসা উচিত। এটি এই পরিস্থিতিতে কাজ করছে না। দয়া করে আমাকে একটু সাহায্য করবেন?
কুমারসান পেরুমাল

1

আপনি ডিটিও প্যাটার্নটি কোনও বেনিফিট হাইবারবনেট ছাড়াই ক্লাস ট্রেনিডিটিটিও ব্যবহার করতে পারেন এবং আপনি জ্যাকসন ম্যাপার ব্যবহার করে ট্রেইনিকে ট্রেনিডিটিওতে রূপান্তর করতে এবং ত্রুটি বার্তাটি বিলোপ করতে পারেন বিঙ্গো :)


1

আপনি যদি সম্পত্তিটিকে উপেক্ষা করতে না পারেন তবে ক্ষেত্রটির দৃশ্যমানতা সংশোধন করার চেষ্টা করুন। আমাদের ক্ষেত্রে, আমাদের পুরানো কোডটি এখনও সম্পর্কের সাথে সত্তা জমা দিচ্ছিল, তাই আমার ক্ষেত্রে এটি স্থির ছিল:

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

যদি আমি @ json আইনে রাখি তবে শিশুটিতে টীকাটি উপেক্ষা করুন। আমি সন্তানের কাছ থেকে পিতামাতাকে পেতে পারি না w যখন আমি শিশুটিকে নেওয়ার চেষ্টা করি। প্যারেন্ট অবজেক্ট কেন আসছে না, এটি @ jsonignore দ্বারা উপেক্ষা করা হবে। আমাকে সন্তানের কাছ থেকে পিতা-মাতার কাছে এবং অভিভাবকের কাছে সন্তানের কাছে যাওয়ার উপায় বলুন।
কুমারসান পেরুমাল

0

আমার এই সমস্যাটি ছিল, তবে আমি আমার সত্তাগুলিতে টীকাটি ব্যবহার করতে চাইনি, তাই আমি আমার ক্লাসের জন্য একজন কনস্ট্রাক্টর তৈরি করে সমাধান করেছি, এই স্রষ্টার সাথে এই কনস্ট্রাক্টরের অবশ্যই রেফারেন্স থাকতে হবে না। এই দৃশ্যটি বলা যাক।

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;
}

যদি আপনি ক্লাসে ভিউতে পাঠানোর চেষ্টা করেন Bবা এর Aসাথে @ResponseBodyএটি অসীম লুপের কারণ হতে পারে। আপনি আপনার ক্লাসে একজন কনস্ট্রাক্টর লিখতে পারেন এবং আপনার entityManagerমতো এটিকে নিয়ে একটি কোয়েরি তৈরি করতে পারেন।

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

এটি কনস্ট্রাক্টর সহ ক্লাস।

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;
   }

}

তবে এই সমাধান সম্পর্কে কিছু বাধা আছে, যেমন আপনি দেখতে পাচ্ছেন, কনস্ট্রাক্টরে আমি লিস্ট বিএস-এর জন্য কোনও রেফারেন্স তৈরি করি নি কারণ হায়বারনেট এটি কমপক্ষে ৩.6.১০ সংস্করণে অনুমতি দেয় না , তাই আমার যখন প্রয়োজন তখন উভয় সত্তাকে একটি দৃশ্যে দেখানোর জন্য আমি নিম্নলিখিতটি করছি।

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

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

এই সমাধানটির সাথে অন্য সমস্যাটি হ'ল আপনি যদি কোনও সম্পত্তি যুক্ত করেন বা সরিয়ে থাকেন তবে আপনাকে অবশ্যই আপনার নির্মাতা এবং আপনার সমস্ত ক্যোয়ারী আপডেট করতে হবে।


0

আপনি যদি স্প্রিং ডেটা রেস্ট ব্যবহার করছেন তবে চক্রীয় রেফারেন্সের সাথে জড়িত প্রতিটি সত্তার জন্য সংগ্রহস্থল তৈরি করে সমস্যার সমাধান করা যেতে পারে।


0

আমি দেরীতে এসেছি এবং এটি ইতিমধ্যে এত দীর্ঘ সুতো। তবে আমি এটির বিষয়টি জানার চেষ্টা করতে কয়েক ঘন্টা ব্যয় করেছি এবং আমার কেসটিকে অন্য উদাহরণ হিসাবে দিতে চাই।

আমি জসনআইগনোর, জসনআইগনোরপ্রিট এবং ব্যাকরেফারেন্স সমাধান উভয়ই চেষ্টা করেছিলাম, তবে আশ্চর্যের বিষয় এটি যথেষ্ট ছিল যে সেগুলি তোলা হয়নি।

আমি লম্বোককে ব্যবহার করেছি এবং ভেবেছিলাম যে এটি হস্তক্ষেপ করে, যেহেতু এটি নির্মাণকারী এবং টু স্ট্রিংকে ওভাররাইড করে (

পরিশেষে এটি লম্বোকের দোষ ছিল না - আমি খুব বেশি চিন্তাভাবনা না করেই ডাটাবেস টেবিলগুলি থেকে জেপিএ সত্তাগুলির নেটবিন জেনারেশনটি স্বয়ংক্রিয়ভাবে ব্যবহার করেছি - ভাল, এবং উত্পন্ন শ্রেণিতে যুক্ত করা একটি টিকা ছিল @ এক্সএমএল রুটএলিমেন্ট E একবার আমি এটি সরিয়ে ফেললে সবকিছু কাজ শুরু করে। আচ্ছা ভালো.


0

মুল্যটি হল @ জসনআইগনোর স্থাপন করা হিসাবে অনুসরণ সেটার পদ্ধতিতে। আমার ক্ষেত্রে.

Township.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;
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.