Cześć, mam taki projekt gdzie encje sa powiązane z soba w wielu relacjach ManyToMany i ManyToOne..., wydaje mi sie że te relacje sa niezbedne ponieważ w bazach danych oprócz głównych tabel są te łączace, czyli mamy Contact oraz ContactGroup, ContactAddress, ContactAddress itd.
Niestety, tabele łączące i cała reszta musi zostać, bo system korzysta u klienta z tych baz danych już od kilkunastu lat. Tak więc, mamy np.
Contact:
@Entity
@Table(name="contact")
@NamedQuery(name="Contact.findAll", query="SELECT c FROM Contact c")
@Getter @Setter @NoArgsConstructor @ToString
public class Contact implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@TableGenerator(name = "default",
table = "id_table",
pkColumnName = "table_name",
valueColumnName = "next_id",
allocationSize = 10)
@GeneratedValue(strategy=GenerationType.TABLE, generator = "default")
@Column(unique=true, nullable=false)
private Long id;
@Type(type = "types.DateLongType")
private Date anniversary;
@Type(type = "types.DateLongType")
private Date birth;
@Column(name="business_email")
private String businessEmail;
@Column(name="campaign_id")
private Long campaignId;
@Column(name="children_names")
private String childrenNames;
@Column(name="children_no", nullable = false, columnDefinition = "int2")
private Integer childrenNo;
@Column(name="company_id")
private Long companyId;
@Type(type = "types.DateLongType")
private Date created;
@Column(name="created_by")
private String createdBy;
@Column(name="db_owner_ids")
private String dbOwnerIds;
@Column(name="db_owners")
private String dbOwners;
private String department;
private String education;
@Column(name="ext_symbol")
private String extSymbol;
@Column(name="family_status")
private String familyStatus;
private String fax;
@Column(columnDefinition = "int2")
private Integer grender;
@Column(name="home_phone")
private String homePhone;
private String interests;
@Column(name="last_modified")
@Type(type = "types.DateLongType")
private Date lastModified;
@Column(name="last_modified_by")
private String lastModifiedBy;
private String login;
@Column(name="mail_opt_out", columnDefinition = "int2")
@Type(type = "org.hibernate.type.NumericBooleanType")
private boolean mailOptOut;
@Column(name="mobile_phone")
private String mobilePhone;
private String name;
private String note;
private String password;
private String permissions;
@Column(columnDefinition = "int2")
@Type(type = "org.hibernate.type.NumericBooleanType")
private boolean person;
@Column(name="pref_contact")
private String prefContact;
@Column(name="primary_contact", columnDefinition = "int2")
@Type(type = "org.hibernate.type.NumericBooleanType")
private boolean primaryContact;
@Column(name="private_email")
private String privateEmail;
@Column(name="report_to")
private Long reportTo;
private String source;
@Column(name="spoken_lang")
private String spokenLang;
private String status;
@Column(name="tech_contact", columnDefinition = "int2")
@Type(type = "org.hibernate.type.NumericBooleanType")
private boolean techContact;
private String title;
private String type;
@Column(name="work_phone")
private String workPhone;
@Column(name="written_lang")
private String writtenLang;
@Column(name="www_address")
private String wwwAddress;
//bi-directional many-to-one association to ActivityContact
// @OneToMany(mappedBy="contact", fetch=FetchType.LAZY)
// private Set<ActivityContact> activityContacts;
//bi-directional many-to-one association to CongMapping
@ManyToMany(fetch=FetchType.LAZY)
@JoinTable(name = "cong_mapping",
joinColumns = @JoinColumn(name = "contact_id"),
inverseJoinColumns = @JoinColumn(name = "group_id"))
@JsonManagedReference
private Set<ContactGroup> contactGroups;
//bi-directional many-to-one association to ContactAddress
@ManyToMany(mappedBy="contact", fetch=FetchType.LAZY)
@JsonManagedReference
private Set<ContactAddress> contactAddresses;
//bi-directional many-to-one association to ContactOwner
@ManyToMany(fetch=FetchType.LAZY)
@JoinTable(name = "contact_owner",
joinColumns = @JoinColumn(name = "contact_id"),
inverseJoinColumns = @JoinColumn(name = "my_worker_id"))
@JsonManagedReference
private Set<MyWorker> contactOwners;
ContactGroup:
@Entity
@Table(name="contact_group")
@NamedQuery(name="ContactGroup.findAll", query="SELECT c FROM ContactGroup c")
public class ContactGroup implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@TableGenerator(name = "default",
table = "id_table",
pkColumnName = "table_name",
valueColumnName = "next_id",
allocationSize = 10)
@GeneratedValue(strategy=GenerationType.TABLE, generator = "default")
@Column(unique=true, nullable=false)
private Long id;
private String description;
private String name;
//bi-directional many-to-one association to CongMapping
@OneToMany(mappedBy="contactGroup", fetch=FetchType.EAGER)
private Set<CongMapping> congMappings;
//bi-directional many-to-one association to ContactGroup
@ManyToOne
@JoinColumn(name="parent_id")
private ContactGroup contactGroup;
//bi-directional many-to-one association to ContactGroup
@OneToMany(mappedBy="contactGroup", fetch=FetchType.EAGER)
private Set<ContactGroup> contactGroups;
//bi-directional many-to-one association to MyWorker
@ManyToOne
@JoinColumn(name="owner_id")
private MyWorker myWorker;
public ContactGroup() {
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Set<CongMapping> getCongMappings() {
return this.congMappings;
}
public void setCongMappings(Set<CongMapping> congMappings) {
this.congMappings = congMappings;
}
public CongMapping addCongMapping(CongMapping congMapping) {
getCongMappings().add(congMapping);
congMapping.setContactGroup(this);
return congMapping;
}
public CongMapping removeCongMapping(CongMapping congMapping) {
getCongMappings().remove(congMapping);
congMapping.setContactGroup(null);
return congMapping;
}
public ContactGroup getContactGroup() {
return this.contactGroup;
}
public void setContactGroup(ContactGroup contactGroup) {
this.contactGroup = contactGroup;
}
public Set<ContactGroup> getContactGroups() {
return this.contactGroups;
}
public void setContactGroups(Set<ContactGroup> contactGroups) {
this.contactGroups = contactGroups;
}
public ContactGroup addContactGroup(ContactGroup contactGroup) {
getContactGroups().add(contactGroup);
contactGroup.setContactGroup(this);
return contactGroup;
}
public ContactGroup removeContactGroup(ContactGroup contactGroup) {
getContactGroups().remove(contactGroup);
contactGroup.setContactGroup(null);
return contactGroup;
}
public MyWorker getMyWorker() {
return this.myWorker;
}
public void setMyWorker(MyWorker myWorker) {
this.myWorker = myWorker;
}
}
itd...
ContactController:
@RestController
@RequestMapping("/api/contact")
public class ContactController {
@Autowired
ContactRepository repository;
@GetMapping()
public ResponseEntity<List<Contact>> getAll() {
try {
List<Contact> list = new ArrayList<>();
repository.findAll().forEach(list::add);
if(list.isEmpty())
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
return new ResponseEntity<>(list, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@GetMapping("/{id}")
public ResponseEntity<Contact> getOneById(@PathVariable("id") long id)
{
Optional<Contact> data = repository.findById(id);
if(data.isPresent())
return new ResponseEntity<>(data.get(), HttpStatus.OK);
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
@PostMapping
public ResponseEntity<Contact> create(@RequestBody Contact data) {
try {
Contact created = repository.save(data);
return new ResponseEntity<>(created, HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.EXPECTATION_FAILED);
}
}
@PutMapping("/{id}")
public ResponseEntity<Contact> updateOne(@PathVariable("id") long id, @RequestBody Contact data)
{
Optional<Contact> original = repository.findById(id);
if(!original.isPresent())
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
data.setId(id);
return new ResponseEntity<>(repository.save(data), HttpStatus.OK);
}
@DeleteMapping("/{id}")
public ResponseEntity<HttpStatus> deleteOne(@PathVariable("id") long id)
{
try {
repository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.EXPECTATION_FAILED);
}
}
}
problemy zaczynaja sie gdy chcę otrzymać poprawnego JSON'a, gdy nie korzystam z @JsonManagedReference i @JsonBackReference to dostaje błedy że nie można serializować itd.
Gdy włącze customowego mappera i nie korzystam z @JsonManaged... :
@Configuration
public class WebConfig implements WebMvcConfigurer {
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter converter : converters) {
if (converter instanceof org.springframework.http.converter.json.MappingJackson2HttpMessageConverter) {
ObjectMapper mapper =((MappingJackson2HttpMessageConverter) converter).getObjectMapper();
mapper.registerModule(new Hibernate5Module());
}
}
}
}
to w odpowiedzi dostaje tabele Contact ,ale contactAddress = null i inne podobnie.
Przeczytałem sporo na stack overflow i tutaj na forum. Wiem że nie powinno sie używać relacji @ManyToMany itd. ale tutaj chyba bez tego sie nie obejdzie ?? Czy ktoś może mi podpowiedzieć jak to powinno być zrobione poprawnie, aby działało to bez ingerencji w istniejąca baze danych i jej powiązania pomiedzy tabelami ? Czy korzystać z tych adnotacji @JsonManagedReference, czy innych ?
(https://stackoverflow.com/questions/3325387/infinite-recursion-with-jackson-json-and-hibernate-jpa-issue)
(https://stackoverflow.com/questions/49960878/infinite-recursion-with-json-and-hibernate-jpa)
(https://stackoverflow.com/questions/37392733/difference-between-jsonignore-and-jsonbackreference-jsonmanagedreference/37394318#37394318)
(Spring Łączenie dwóch obiektów w relacji Many-To-Many)
(Relacja Wiele do Wielu w JPA. Problem z usuwaniem)