स्प्रिंग बूट @ ResponseBody इकाई आईडी को क्रमबद्ध नहीं करता है


95

एक अजीब समस्या है और यह पता नहीं लगा सकते हैं कि इसके साथ कैसे निपटें। सरल पोजो है:

@Entity
@Table(name = "persons")
public class Person {

    @Id
    @GeneratedValue
    private Long id;

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

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

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

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

    @Column(name = "created")
    private Date created;

    @Column(name = "updated")
    private Date updated;

    @PrePersist
    protected void onCreate() {
        created = new Date();
    }

    @PreUpdate
    protected void onUpdate() {
        updated = new Date();
    }

    @Valid
    @OrderBy("id")
    @OneToMany(mappedBy = "person", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    private List<PhoneNumber> phoneNumbers = new ArrayList<>();

    public Long getId() {
        return id;
    }

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

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getMiddleName() {
        return middleName;
    }

    public void setMiddleName(String middleName) {
        this.middleName = middleName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public Date getCreated() {
        return created;
    }

    public Date getUpdated() {
        return updated;
    }

    public List<PhoneNumber> getPhoneNumbers() {
        return phoneNumbers;
    }

    public void addPhoneNumber(PhoneNumber number) {
        number.setPerson(this);
        phoneNumbers.add(number);
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}

@Entity
@Table(name = "phone_numbers")
public class PhoneNumber {

    public PhoneNumber() {}

    public PhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "phone_number")
    private String phoneNumber;

    @ManyToOne
    @JoinColumn(name = "person_id")
    private Person person;

    public Long getId() {
        return id;
    }

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

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}

और बाकी समापन बिंदु:

@ResponseBody
@RequestMapping(method = RequestMethod.GET)
public List<Person> listPersons() {
    return personService.findAll();
}

Json response में Id को छोड़कर सभी फ़ील्ड हैं, जो मुझे सामने वाले व्यक्ति को एडिट / डिलीट करने के लिए चाहिए। मैं आईडी के साथ ही धारावाहिक बूट को कैसे कॉन्फ़िगर कर सकता हूं?

इस तरह अब प्रतिक्रिया मिल रही है:

[{
  "firstName": "Just",
  "middleName": "Test",
  "lastName": "Name",
  "comment": "Just a comment",
  "created": 1405774380410,
  "updated": null,
  "phoneNumbers": [{
    "phoneNumber": "74575754757"
  }, {
    "phoneNumber": "575757547"
  }, {
    "phoneNumber": "57547547547"
  }]
}]

UPD में द्विदिश हाइबरनेट मैपिंग है, हो सकता है कि यह किसी तरह से संबंधित हो।


क्या आप कृपया हमें अपने स्प्रिंग सेटअप के बारे में अधिक जानकारी दे सकते हैं? आप किस मेसन मार्शल का उपयोग करते हैं? डिफ़ॉल्ट एक, जैक्सन, ...?
ओटा

वास्तव में कोई विशेष सेटअप नहीं है। वसंत बूट की कोशिश करना चाहता था :) वसंत-बूट-स्टार्टर-डेटा-आराम को पोम और @EnableAutoConfiguration का उपयोग करके जोड़ा गया। ट्यूटोरियल के कुछ पढ़ें और लगता है जैसे सभी बॉक्स से बाहर काम करना है। और यह उस ईद क्षेत्र को छोड़कर है। समापन बिंदु प्रतिक्रिया के साथ अद्यतन पोस्ट।
कॉन्सटेंटिन

1
वसंत 4 में आपको @RestControllerनियंत्रक वर्ग पर भी उपयोग करना चाहिए और @ResponseBodyविधियों से हटा देना चाहिए । इसके अलावा, मैं सुझाव दूंगा कि डोमेन वस्तुओं के बजाय json अनुरोधों / प्रतिक्रियाओं को संभालने के लिए DTO कक्षाएं हों।
18

जवाबों:


139

मुझे हाल ही में एक ही समस्या थी और यह इसलिए है क्योंकि यह spring-boot-starter-data-restडिफ़ॉल्ट रूप से काम करता है। मेरे SO प्रश्न को देखें -> स्प्रिंग बूट के लिए किसी ऐप को माइग्रेट करने के बाद स्प्रिंग डेटा रेस्ट का उपयोग करते समय, मैंने देखा है कि @Id के साथ एंटिटी प्रॉपर्टीज को JSON में नहीं रखा गया है

यह कैसे व्यवहार करता है, इसे अनुकूलित करने के लिए, आप RepositoryRestConfigurerAdapterविशिष्ट वर्गों के लिए आईडी उजागर करने के लिए बढ़ा सकते हैं ।

import org.springframework.context.annotation.Configuration;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;

@Configuration
public class RepositoryConfig extends RepositoryRestConfigurerAdapter {
    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.exposeIdsFor(Person.class);
    }
}

1
और इकाई वर्ग में आईडी के लिए अब गेट्टर और सेटर का समर्थन करना न भूलें! .. (मैं इसे भूल गया था और उस के लिए बहुत समय खोज रहा था)
फिल

3
कौडेंट में मिलता RepositoryRestConfigurerAdapter हैorg.springframework.data.rest.webmvc.config
गोविंद सिंह

2
पैदावार: The type RepositoryRestConfigurerAdapter is deprecated। इसके अलावा काम करने के लिए प्रकट नहीं होता है
GreenAsJade

2
वास्तव RepositoryRestConfigurerAdapterमें नवीनतम संस्करणों में पदावनत किया गया है, आपको RepositoryRestConfigurerसीधे लागू करना होगा ( implements RepositoryRestConfigurerइसके स्थान पर उपयोग करें extends RepositoryRestConfigurerAdapter)
Yann39

48

मामले में आपको सभी संस्थाओं के लिए पहचानकर्ता को उजागर करने की आवश्यकता है :

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;

import javax.persistence.EntityManager;
import javax.persistence.metamodel.Type;

@Configuration
public class RestConfiguration implements RepositoryRestConfigurer {

    @Autowired
    private EntityManager entityManager;

    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.exposeIdsFor(
                entityManager.getMetamodel().getEntities().stream()
                .map(Type::getJavaType)
                .toArray(Class[]::new));
    }
}

ध्यान दें कि स्प्रिंग बूट के संस्करणों में 2.1.0.RELEASEआपको सीधे लागू करने के बजाय (अब पदावनत) का विस्तार करना होगा ।org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapterRepositoryRestConfigurer


यदि आप केवल विशिष्ट सुपर क्लास या इंटरफ़ेस को बढ़ाने या लागू करने वाली संस्थाओं के पहचानकर्ताओं को उजागर करना चाहते हैं :

    ...
    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.exposeIdsFor(
                entityManager.getMetamodel().getEntities().stream()
                .map(Type::getJavaType)
                .filter(Identifiable.class::isAssignableFrom)
                .toArray(Class[]::new));
    }

यदि आप केवल विशिष्ट एनोटेशन वाली संस्थाओं के पहचानकर्ता को उजागर करना चाहते हैं :

    ...
    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.exposeIdsFor(
                entityManager.getMetamodel().getEntities().stream()
                .map(Type::getJavaType)
                .filter(c -> c.isAnnotationPresent(ExposeId.class))
                .toArray(Class[]::new));
    }

नमूना एनोटेशन:

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExposeId {}

यदि कोई कोड के पहले ब्लॉक का उपयोग करता है, तो वह किस निर्देशिका और फ़ाइल में जा सकता है?
डेविड कृडर

1
@DavidKrider: कंपोनेंट स्कैन द्वारा कवर की गई किसी भी डायरेक्टरी में इसकी अपनी फाइल होनी चाहिए । आपके मुख्य ऐप ( @SpringBootApplicationएनोटेशन के साथ ) के नीचे कोई भी उप पैकेज ठीक होना चाहिए।
lcnicolau

Field entityManager in com.myapp.api.config.context.security.RepositoryRestConfig required a bean of type 'javax.persistence.EntityManager' that could not be found.
दिमित्री कोपरीवा

मैं एक जोड़ने की कोशिश की @ConditionalOnBean(EntityManager.class)में MyRepositoryRestConfigurerAdapterहै, लेकिन विधि नहीं बुलाया जाता है और आईडी अभी भी उजागर नहीं कर रहे हैं। मैं स्प्रिंग डेटा mybatis के साथ स्प्रिंग डेटा का उपयोग करता हूं: github.com/hatunet/spring-data-mybatis
दिमित्री कोपरिवा

@DimitriKopriwa: EntityManagerJPA के विनिर्देशन का हिस्सा है और MyBatis JPA को लागू नहीं करता (एक नज़र डालिए क्या MyBatis JPA का अनुसरण करता है? )। इसलिए, मुझे लगता है कि आपको स्वीकृत उत्तरconfig.exposeIdsFor(...) में विधि का उपयोग करके एक-एक करके सभी संस्थाओं को कॉन्फ़िगर करना चाहिए ।
lcnicolau

24

@ एरिक-पेलेदान से जवाब बॉक्स से बाहर काम नहीं करता था, लेकिन बहुत करीब था, शायद जो स्प्रिंग बूट के पिछले संस्करणों के लिए काम करता था। अब यह है कि इसे कैसे कॉन्फ़िगर किया जाना चाहिए, मुझे सही करें यदि मैं गलत हूं:

import org.springframework.context.annotation.Configuration;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;

@Configuration
public class RepositoryConfiguration extends RepositoryRestConfigurerAdapter {

    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.exposeIdsFor(User.class);
        config.exposeIdsFor(Comment.class);
    }
}

3
पुष्टि की गई कि यह समाधान स्प्रिंग बूट v1.3.3 के तहत ठीक काम करता है। कृपया @ eric-peladan द्वारा प्रस्तावित एक के विपरीत।
पोलीकॉफ

4

यदि आप RepositoryRestMvcConfiguration का उपयोग करते हैं तो स्प्रिंग बूट के साथ आपको SpringBootRepositoryRestMvcConfiguration का विस्तार
करना होगा । कॉन्फ़िगरेशन अनुप्रयोग में परिभाषित नहीं किया जा सकता है।

@Configuration
public class MyConfiguration extends SpringBootRepositoryRestMvcConfiguration  {

@Override
protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
    config.exposeIdsFor(Project.class);
}
}

लेकिन एक अस्थायी आवश्यकता के लिए आप क्रमांकन में आईडी को शामिल करने के लिए प्रक्षेपण का उपयोग कर सकते हैं जैसे:

@Projection(name = "allparam", types = { Person.class })
public interface ProjectionPerson {
Integer getIdPerson();
String getFirstName();
String getLastName();

}


स्प्रिंग बूट 2 में, यह काम नहीं करता है। वर्ग रिपॉजिटरी RestMvcConfiguration को ओवरराइड करने के लिए configRepositoryRestConfiguration नहीं है।
पिचब्लैक 408

4

वर्ग RepositoryRestConfigurerAdapter3.1 से हटा दिया गया है, RepositoryRestConfigurerसीधे लागू करें।

@Configuration
public class RepositoryConfiguration implements RepositoryRestConfigurer  {
	@Override
	public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
		config.exposeIdsFor(YouClass.class);
		RepositoryRestConfigurer.super.configureRepositoryRestConfiguration(config);
	}
}

फ़ॉन्ट: https://docs.spring.io/spring-data/rest/docs/current-SNAPSHOT/api/org/springframework/data/rest/webmvc/config/RepositoryRestConfigurer.html


2

आसान तरीका: अपने चर private Long id;का नाम बदलेंprivate Long Id;

मेरे लिये कार्य करता है। आप इसके बारे में और अधिक यहाँ पढ़ सकते हैं


13
ओह, यार ... यह ऐसी कोड गंध है ... वास्तव में, ऐसा मत करो
इगोर डोनिन

@IgorDonin का कहना है कि जावा समुदाय जो परिवर्तनशील / वर्ग / फ़ंक्शन नामों से सूँघने और रेंगने से प्यार करता है ... हर समय, हर जगह।
EralpB

2
@Component
public class EntityExposingIdConfiguration extends RepositoryRestConfigurerAdapter {

    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        try {
            Field exposeIdsFor = RepositoryRestConfiguration.class.getDeclaredField("exposeIdsFor");
            exposeIdsFor.setAccessible(true);
            ReflectionUtils.setField(exposeIdsFor, config, new ListAlwaysContains());
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    class ListAlwaysContains extends ArrayList {

        @Override
        public boolean contains(Object o) {
            return true;
        }
    }
}

3
एसओ में आपका स्वागत है! अपने उत्तरों में कोड पोस्ट करते समय, यह बताना मददगार होता है कि आपका कोड ओपी की समस्या का हल कैसे करता है :)
जोएल

1

हम्म, ठीक लगता है जैसे मैंने समाधान पाया। पॉम फ़ाइल से स्प्रिंग-बूट-स्टार्टर-डेटा-रेस्ट को हटाकर @JsonManagedReference को फोनन्यूज़ और @JsonBackReference व्यक्ति को जोड़ने से वांछित आउटपुट मिलता है। जवाब में Json बहुत अधिक किसी भी मुद्रित नहीं है, लेकिन अब यह आईडी है। पता नहीं क्या जादू वसंत बूट इस निर्भरता के साथ हुड के तहत प्रदर्शन करता है, लेकिन मुझे यह पसंद नहीं है :)


यह आपके स्प्रिंग डेटा रिपॉजिटरी को REST अंतिम बिंदुओं के रूप में उजागर करने के लिए है। यदि आप उस सुविधा को नहीं चाहते हैं तो आप उसे छोड़ सकते हैं। आईडी बात एक जैक्सन के साथ करना Moduleहै जो एसडीआर द्वारा पंजीकृत है मुझे विश्वास है।
डेव सीर

1
रैस्टफुल एपीआई को सरोगेट कुंजी आईडी को उजागर नहीं करना चाहिए क्योंकि उनका मतलब बाहरी सिस्टम से कुछ भी नहीं है। Restful आर्किटेक्चर में ID उस संसाधन के लिए विहित URL है।
क्रिस डामोर

4
मैंने इसे पहले पढ़ा है, लेकिन ईमानदारी से मुझे समझ में नहीं आता है कि मैं ईद के बिना फ्रंट एंड बैक बैक को कैसे लिंक कर सकता हूं। डिलीट / अपडेट ऑपरेशन के लिए मुझे Id to orm पास करना होगा। हो सकता है कि आपके पास कुछ लिंक हो, यह कैसे सही तरीके से निष्पादित किया जा सकता है :)
कोन्स्टेंटिन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.