jak by to zrobiliście - hibernate, jpa, relacje ManyToMany :)

0

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)

1

Problemem tutaj jest fakt, że chcesz zwracać javowe encje bezpośrednio, przez REST API. Powinieneś zwracać jakieś DTO, czyli płaskie struktury, które nie będą miały zagnieżdżonych całych kolekcji, tylko żeby miały raczej idki zamiast całych obiektów. Wówczas sobie robisz mappery entity -> DTO i odwrotnie. I problem z JSONami znika :)

0
Pinek napisał(a):

Problemem tutaj jest fakt, że chcesz zwracać javowe encje bezpośrednio, przez REST API. Powinieneś zwracać jakieś DTO, czyli płaskie struktury, które nie będą miały zagnieżdżonych całych kolekcji, tylko żeby miały raczej idki zamiast całych obiektów. Wówczas sobie robisz mappery entity -> DTO i odwrotnie. I problem z JSONami znika :)

myślałem nad tym też, tylko że jest tam kilkadziesiąt encji i trzeba by do wszystkich to napisać :) ale chyba takie podejście bedzie najbardziej właściwe, czy jeszcze ktoś może chce dodać swoją opinie ?

0

Jak już bardzo musisz, detachowanie encji?
Dopóki są zaatachowane MOŻE maja referencje na cały silnik/sesję JPA.
Wyjęte z sesji JPA "MOŻE" będą lżejsze?

Domniemanie takie, bez dowodów. Generalnie z sesją JPA w webie mam jak najgorsze wspomnienia.

2

Podstawowe pytanie - jakiej klasy jest to system? CRUD? Naprawdę musisz pchać na świat cały model bazy danych?

Odnośnie @ManyToMany - możesz użyć np. @OneToMany z obu stron wprowadzajac encje na tabele
pośrednicząca. Na poziomie bazy danych nie ma czegoś takiego jak N-N, możesz to zamapowac na co najmniej 2 sposoby, o ile w ogóle potrzebujesz tego w swoim modelu.

0

Tak, CRUD, odziedziczyłem to po koledze i chce właśnie to poprawić. Dlatego pytałem. Zastosuję sie do waszych rad, dzięki wielkie :)

0

wracając do tematu, chciałbym was zapytać jakbyście podzielili architekturę takiej aplikacji? Jest tam około 80 encji. Jak by to zrobić żeby było w miarę przejrzyście ?
Standardowo podział na warstwy:

-controller
----------ContactController
----------ContactAdressController
----------ContactGroupController
----------CompanyController
----------CompanyAdressController
----------CompanyHistoryController
----------ActivityController
----------ActivityContactController
----------ActivityDocumController
----------...
-model
---------...
-repository
---------...
-service
---------...
-dto
---------...

czy może podział na warstwy i pakiety funkcyjne:

-controller
--------contact
---------------+ContactController
---------------+ContactAdressController
---------------+ContactGroupController
--------company
---------------+CompanyController
---------------+CompanyAdressController
---------------+CompanyHistoryController
---------...
-model
------...
itd

albo może jeszcze jakiś inny podział, który byłby odpowiedni w tym przypadku? czy możecie mi doradzić ?

4

Raczej właśnie w drugą stronę:

  • contact
    --- controllers
    --- daos
    ....
  • company
    --- controllers
    --- daos
    ...
3

Wygląda że masz tam więcej odpowiedzialności w jednym miejscu, taki trochę "monolit" , więc może warto faktycznie zrobićosobne moduły podzielone "biznesowo". Ale nie wiem czy akurat kontrolery czy w ogóle rzeczy związane z jakimś frameworkiem bym też tak dzielił. Ja osobiście preferuje trochę inną opcje:

  • server -> tutaj kontrolery (które są wszystkie jednolinijkowe! a URLe są brane ze statycznego property w odpowiednim module z programowego klienta do danego endpointu)
  • contact
  • company
  • itd

W ten sposób np. jakiegoś Springa masz tylko w jednym miejscu (w tymże server) a twoje contact czy company to czysta domena. Przy czym dla jasności, ten server to taka wydmuszka, są tam co najwyżej jakieś @Configuration i właśnie @Controller i nic więcej.
Dodatkowo albo w każdym pakiecie osobno jak pod-moduł, albo gdzieś w jednym miejscu jakieś interfaces, tak żeby pomiędzy modułami widzieć się przez interfejsy
Osobiście tego jpa/hibernate też bym zamknął w jednym module, obwarował interfejsami i w komunikacji z nim operował tymi interfejsami i obiektami domenowymi, tak zeby poza tym jednym modułem nigdzie nie było tego jpa widać, a w tych modułach domenowych zrobił sobie repository które byłoby czysto "biznesowe" i agnostyczne względem sposobu przechowywania danych.

0

dzięki za rady, a czy takie coś jest dobrym przykładem, czy przesadzam (to już chyba pod mikroserwisy podchodzi) ?
https://medium.com/slalom-build/clean-architecture-with-java-11-f78bba431041
https://github.com/carlphilipp/clean-architecture-example

albo taki przykład ?
https://github.com/jakubnabrdalik/hentai

1

https://github.com/jakubnabrdalik/hentai

Nawet mi się podoba, chociaż ja bym zrobił tam dwa osobne moduły gradlowe/mavenowe (jeden na to jego infrastructure które jest tym samym co wyżej opisałem jako server a drugi na domenę) i kontroler przerzucił do tej infrastruktury, tak zeby dostac domenę która nie zalezy w żaden sposób od jakiegoś frameoworka. A jak w domenie potrzeba by znowu coś "ciężkiego" (jak jakiś dostęp do bazy danych) to znów osobny moduł (plus moduł na interfejsy, tak zeby domena zależała tylko od interfejsów i nie wiedziała czy jest tam sqlowa baza, jakieś in memory czy cokolwiek innego).

0

czyli coś na wzór tych projektów:
https://github.com/Java-Techie-jt/spring-multi-module-application

https://reflectoring.io/spring-boot-gradle-multi-module/
https://github.com/thombergs/buckpal

tylko jak w domain potem dać te wszystkie entity (Contact, Company, Activity...) ?

czy tak myśle dobrze?

  • domain.model

    • contact
      • contact
      • contactAddress
    • company
      • company
      • companyAddress
  • domain.dto

    • contact
      • contactDto
      • contactAddressDto
    • company
      • companyDto
      • companyAddressDto

to jako jeden moduł , potem osobny moduł Repository itd , dobrze myśle ?

1

to jako jeden moduł

Moim zdaniem nie. DTO to część kontraktu serwisu i moim zdaniem powinny być w osobnym miejscu. Albo w module client gdzie będziesz miał jakiegoś programowego klienta do swojego serwisu (czyli klasę która potrafi komunikować się z twoimi restami i zwracać DTO) albo osobno w jakimś module z interfejsami. Dzięki temu ktoś kto chce korzystać z twojego serwisu nie musi pisać sobie tych klas DTO samemu, ani wciągać twojego domain jako zależności. Zamiast tego moze sobie wziąć lekki i niezależny od niczego moduł i na nim bazować.

0

mam jeszcze małą prośbę, jakbyście mogli spojrzeć i powiedzieć czy takie mapowanie encja <-> DTO jest poprawne ? spotkałem sie już z kilkoma różnymi wersjami i nie wiem które będzie w 100% poprawna do tego przypadku i żeby potem nie było problemu z JSON :)

package pl.model.activity;

import pl.model.campaign.Campaign;

import javax.persistence.*;
import java.io.Serializable;


/**
 * The persistent class for the activity_campaign database table.
 * 
 */
@Entity
@Table(name="activity_campaign")
@NamedQuery(name="ActivityCampaign.findAll", query="SELECT a FROM ActivityCampaign a")
public class ActivityCampaign 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;

	//bi-directional many-to-one association to Activity
	@ManyToOne
	@JoinColumn(name="activity_id")
	private Activity activity;

	//bi-directional many-to-one association to Campaign
	@ManyToOne
	@JoinColumn(name="campaign_id")
	private Campaign campaign;

	protected ActivityCampaign() {
	}

	public ActivityCampaign(Long id, Activity activity, Campaign campaign) {
		this.id = id;
		this.activity = activity;
		this.campaign = campaign;
	}

	public Long getId() {
		return this.id;
	}

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

	public Activity getActivity() {
		return this.activity;
	}

	public void setActivity(Activity activity) {
		this.activity = activity;
	}

	public Campaign getCampaign() {
		return this.campaign;
	}

	public void setCampaign(Campaign campaign) {
		this.campaign = campaign;
	}

}
package pl.dto.activity;

import lombok.Getter;
import pl.model.activity.Activity;
import pl.model.activity.ActivityCampaign;
import pl.model.campaign.Campaign;

import java.util.StringJoiner;

@Getter
public class ActivityCampaignDTO {

    public final long id;
    public final Activity activity;
    public final Campaign campaign;


    public ActivityCampaignDTO(long id, Activity activity, Campaign campaign) {
        this.id = id;
        this.activity = activity;
        this.campaign = campaign;
    }

    ActivityCampaign toActivityCampaign() {
        return new ActivityCampaign(
                this.getId(),
                this.getActivity(),
                this.getCampaign()
        );
    }

    ActivityCampaignDTO toActivityCampaignDTO(final ActivityCampaign activityCampaign) {
        return new ActivityCampaignDTO(
                activityCampaign.getId(),
                activityCampaign.getActivity(),
                activityCampaign.getCampaign()
        );
    }

    @Override
    public String toString() {
        return new StringJoiner(", ", ActivityCampaignDTO.class.getSimpleName() + "[", "]")
                .add("id=" + id)
                .add("activity=" + activity.getId())
                .add("campaign=" + campaign.getId())
                .toString();
    }
}

0

Niestety, ale nie do końca poprawnie. W klasie ActivityCampaignDTO zamiast pól

public final Activity activity;
public final Campaign campaign;

powinieneś mieć

public final Long activityId;
public final Long campaignId;

Pomijam już to, że te fieldy powinny być private a nie public, no i np że kolumny w bazie powinny mieć brzmieć activity_fk zamiast activity_id

0

poprawiłem:

package pl.dto.activity;

import lombok.Getter;
import pl.model.activity.ActivityCampaign;

import java.util.StringJoiner;

@Getter
public class ActivityCampaignDTO {

    private final long id;
    private final long activityId;
    private final long campaignId;


    public ActivityCampaignDTO(long id, long activityId, long campaignId) {
        this.id = id;
        this.activityId = activityId;
        this.campaignId = campaignId;
    }


    ActivityCampaignDTO toActivityCampaignDTO(final ActivityCampaign activityCampaign) {
        return new ActivityCampaignDTO(
                activityCampaign.getId(),
                activityCampaign.getActivity().getId(),
                activityCampaign.getCampaign().getId()
        );
    }

    @Override
    public String toString() {
        return new StringJoiner(", ", ActivityCampaignDTO.class.getSimpleName() + "[", "]")
                .add("id=" + id)
                .add("activity=" + activityId)
                .add("campaign=" + campaignId)
                .toString();
    }
}

metoda toActivityCampaign() jest zbędna ?

niestety nazw pól w bazie danych nie mogę zmienić, nie zależy to już ode mnie

1

We are almost there ;)
Parę uwag:

  1. metoda toActivityCampaignDTO powinna być jak już to w klasie encji lub w jakimś mapperze. Bo co za sens wołać tą metodę już na obiekcie DTO? :)

  2. Zamiast tej metody toActivityCampaignDTO, po prostu zrób drugi konstruktor

  3. Próbowałeś to (de)serializować już? Nie wiem, czy Jackson nie będzie wymagał @JsonCreator nad konstruktorem

  4. Czy activityId i campaignIdobowiązkowe? Czy jednak jest możliwa sytuacja, kiedy ich nie będzie? Wówczas lepiej użyć Long zamiast long, żeby uniknąć NPE

0

Cześć, kilka dni już mineło i można powiedzieć, że mam za dużo niewiadomych w którym kierunku iść dalej z projektem serwera backendowego. Nie mam narzuconej z góry żadnych warunków czego użyć, jak to zrobić itd, tylko żeby był dostęp do istniejącej bazy danych. A chciałbym żeby było zrobione tak jak należy. Zastosowałem się do waszych wcześniejszych rad i wskazówek, przynajmniej do części jak na razie i przygotowałem taki projekt na githubie:

https://github.com/CienZZZ/test-server

na razie rozdzieliłem go na dwa osobne moduły, potem docelowo postaram sie to lepiej ogarnąć, ale teraz moje pytanie, w domenie mam encje Contact, jak na razie tylko dla niej zrobiłem DTO, mappera, serwis. Czy w dobry sposób to zrobiłem ? Jakies rady co jeszcze zmienić, poprawić ?

W module web-server mam kontrolera do tej encji, korzystam tam z ResponeEntity wraz z objektami ContactDTO. Tylko czy korzystac z ResponseEntity ?
Mam taki inny starszy projekt, gdzie troche w inny sposób zwracałem odpowiedzi w kontrolerze:
https://github.com/CienZZZ/java-oauth2-server/blob/master/src/main/java/pl/weilandt/wms/user/UserController.java

Co radzicie ?

na pewno jeszcze dopiszę loggera dla klas kontrolerów, żeby wiecej było widać co sie dzieje niż tylko zapytania SQL.
Jeszcze mam też prośbę jakbyście mogli powiedzieć, podesłać link itp. żeby rozwiązać problem globalnego zwracania błedów przez odpowiedź.
REST Error global handling czy coś w tym stylu, może macie jakieś strony, przykłady gdzie jest to w dobry sposób zrobione ?

Bardzo dziękuje za poświęcony czas i rady :)

1

CongMappingController -

  1. getAll() - po co tutaj try catch? Czy JpaRepository nie zwraca na findAll() od razu List<T>? W takim razie po co bierzesz listę, i potem dodajesz wszystkie jej elementy do nowej?
  2. Jak pamiętam ResponseEntity jest na tyle mądre, że jeśli dostanie pustego Optional to automatycznie zmapuje go na 404 jeśli zrobisz ResponseEntity.of(Optional<T>).
if(!contactRepository.findById(contactId).isPresent()){
     throw new ResourceNotFoundException();
} else {
     this.contactRepository.deleteById(contactId);
}

Takie coś można zrobić inaczej:

contactRepository.findById(contactId)
                       .map(contact -> contactRepository.deleteById(contact.getId())
                       .orElseThrow(() -> new ResourceNotFoundException));

To z takich, które wpadły mi na pierwszy rzut oka.

0
lavoholic napisał(a):

CongMappingController -

  1. getAll() - po co tutaj try catch? Czy JpaRepository nie zwraca na findAll() od razu List<T>? W takim razie po co bierzesz listę, i potem dodajesz wszystkie jej elementy do nowej?
  2. Jak pamiętam ResponseEntity jest na tyle mądre, że jeśli dostanie pustego Optional to automatycznie zmapuje go na 404 jeśli zrobisz ResponseEntity.of(Optional<T>).
if(!contactRepository.findById(contactId).isPresent()){
     throw new ResourceNotFoundException();
} else {
     this.contactRepository.deleteById(contactId);
}

Takie coś można zrobić inaczej:

contactRepository.findById(contactId)
                       .map(contact -> contactRepository.deleteById(contact.getId())
                       .orElseThrow(() -> new ResourceNotFoundException));

To z takich, które wpadły mi na pierwszy rzut oka.

troche to .map nie działa bo deleteById daje void, trzeba by cos takiego:

this.contactRepository.findById(id)
                .map(contact -> {
                    this.contactRepository.deleteById(contact.getId());
                    return contact;
                })
                .orElseThrow(ResourceNotFoundException::new);

ale według mnie lepiej wygląda w tym przypadku if() tak jak mam

0

pytanie z innej beczki, czy użylibyście do takiego projektu JOOQ zamiast JPA i Hibernate ?

2
gandzia7 napisał(a):

pytanie z innej beczki, czy użylibyście do takiego projektu JOOQ zamiast JPA i Hibernate ?

Tak

1 użytkowników online, w tym zalogowanych: 0, gości: 1