Czy id stringowy to bardzo zła rzecz ?

0

Cześć, w sumie dwa pytania o JPA/Hibernate.

  1. Mam sobie tabele userów, którzy mogą być logowani na 3 sposoby: fb, g+ oraz customowo. No i chciałbym w bazie przechowywać też info o tym skąd oni są. Myślałem, aby zrobić tak, że zamiast standardowego long-id zrobić klasę embeddable dla id, która miałaby w sobie dwa pola stringowe: id oraz typ, których połączenie dawało tego id usera. Np. dla usera z fb typ to byłby "FB", a id jakieś tam to, które dostane z fb i w konsekwencji jego id byłoby np. "FB:1321232313". Dla usera z g+ "GP:11122222", a dla usera zarejestrowanego customowo "CST:44433322". Czy to jest dobry pomysł ? Czy lepiej zrobić standardowo id jako long i osobne kolumny z informacją o tym czy to jest facebookowy/googleplusowy czy customowy user + info o jego id z tych serwisów ?

  2. Jaka jest różnica między @EmbeddedId a połączeniu między @Id wraz z @Embedded ?

Pozdrawiam

0

String jako ID ma sens, np. w przypadku słowników gdy jest to od razu kod.

Pytanie czy jest Ci to niezbędne? Czy jest opcja FB:1 i GP:1 i po co? Bo o ile te liczy nie pochodzą z zewnątrz i nie ma możliwości że się zdublują to wystarczy standardowy ID i kolumna określająca typ użytkownika.

Pamiętaj że jeżeli rozbijesz to na 2 kolumny, to dużo łatwiej będzie zrobić selecta dla użytkowników danego typu, a tak to sporo zabawy.

0

@krzysiek050:
Teoretycznie istnieje możliwość, że id kogoś z fb będzie tako samo jak innej osoby z gp, no ale jest to prawie zerowe prawdopodobieństwo. I jak te liczby nie pochodzą z zewnątrz ? Przecież id faceta z fb dostaje wraz z api facebooka.
A no drugi argument jest znaczący, ale nie wiem czy będę wyciągał ludzi po ich typie.
Czyli wg Ciebie powinienem mieć typową kolumnę id jako prim key generowaną przez JPA standardowo i potem jeszcze dwie kolumny : typ ( jakiś enum: fb, gp, cst) oraz service_id ( id fejsa, id google plusa lub to samo id co prim key ) ?

5

Musisz zadeklarować ktora kolumna to primary key. Id zawsze numeryczne. Dla mnie trzymanie id jako stringa to niewybaczalny blad który sie zemści raczej prędzej niz później. Generowania nie możesz zrobić z sekwencji wprost w bazie? Po co podkladac sobie nogę i pisac nadmiarowy kod skoro można to zalatwic mechanizmami bazy?

1
@kate87

Dla mnie trzymanie id jako stringa to niewybaczalny blad który sie zemści raczej prędzej niz później.

Trochę mnie zaciekawiłaś. Pomijając przypadki patologiczne, czyli klucz pełnotekstowy, to różnica w przeszukiwaniu dla typów numerycznych i stringów nie jest aż tak duża. https://www.depesz.com/2012/06/07/123-vs-depesz-what-is-faster/

Jeżeli jeszcze do tego dodamy możliwość zamiany takiego stringa we własny typ w bazie danych, to różnica będzie jeszcze mniejsza.

0

@Koziołek:
W takim razie wg Ciebie dobrym pomysłem jest po prostu zrobić coś takiego:

@Entity
class User {

	@Id
	@GeneratedValue
	private long id;

	@Embedded
	private ServiceInfo serviceInfo;
	
	// reszta jak email itd.

}

enum ServiceType { FB, GPLUS, CUSTOM }

@Embeddable
class ServiceInfo {
	private long serviceId;
	private ServiceType serviceType;

	protected  ServiceInfo() {} // for JPA

	private ServiceInfo(long serviceId, ServiceType serviceType) {
		this.serviceId = serviceId;
		this.serviceType = serviceType;
	}

	private static ServiceInfo CreateForFB(long id) {
		return new ServiceInfo(id, ServiceType.FB);
	}

	private static ServiceInfo CreateForGP(long id) {
		return new ServiceInfo(id, ServiceType.GPLUS);
	}

	private static ServiceInfo CreateForCST(long id) {
		return new ServiceInfo(id, ServiceType.CUSTOM);
	}

}

Hm ?

1

Jako ID możesz wykorzystać ServiceInfo.

@Embeddable
class ServiceInfo{
	private ServiceType type;
	private long serviceId;
} 

@Entity
class User{
	@EmbeddedId 
	private ServiceInfo id;

}
```

I tyle
0

@Koziołek:
Dzięki. A ja generować wartości serviceId jeśli użytkownik rejestruje się customowo ?
I ponawiam pytanie, jaka różnica między @EmbeddedId a @Embedded @Id ?

0

@EmbededId oznacza, że pole obiektu jest złożonym kluczem głównym. Stosujesz go jeżeli klucz główny składa się z kilku pół. Inną wersją tego podejścia jest użycie @IdClass i oznaczenie wielu pól w klasie za pomocą @Id. Obie składnie są przeznaczone do innych celów.

@Id co do zasady oznacza pole, które jest mapowane na klucz główny w bazie. Tylko jedno pole w obiekcie może mieć tą adnotację, za wyjątkiem sytuacji gdzie używamy @IdClass.

@Embedded to pole złożone, które w modelu obiektowym jest osobną klasą. Zazwyczaj reprezentuje grupę kolumn, która powtarza się w kilku tabelach i chcemy je razem mapować w modelu obiektowym.

0

No ok, ale widziałem jakieś przykłady na necie, gdzie jako id używali czegoś takiego:

@Id
@Embedded
private CustomerId customerId;

To po prostu były błędne przykłady tak ?

0

@Koziołek ad sytuacji.

Pierwsza sytuacja miała miejsce podczas konwersji walut. Mieliśmy tabelkę typu kod, kod, wartość, data. Chodziło o trzymanie wartości walut dla poszczególnych dat. I dalej przeliczanie z jednej wartości na drugą w odstępie czasowym. Początkowe założenie było takie że raz na dzień będą wpadać wartości i konwersja będzie per dzień. Indeks założony na 3 kolumny kod kod i data. kody stringowe. I o ile dla samego wyświetlania wartości w przedziale czasowym nie miało to większego znaczenia, o tyle przy konwersji konkretnych kwot w kolejnych tabelach czuć było spadek wydajności. Dodanie numerycznego id i zmiana PK na kolumnę z ID a nie na 3 połączone kolumny odsunęło konieczność realnej optymalizacji o kilka miesięcy.

Druga sytuacja zabawa z strukturą drzewiastą czyli struktura zatrudnienia i konkretna sprzedaż za kilka miesięcy,wykorzystanie connect by. Mamy w tabelce imię i nazwisko człowieka, mail i imię i nazwisko jego managera, mail managera. Łączyliśmy po emailach dla 4-5 stopniowej struktury zatrudnienia, każda osoba mogła sprzedawać, dane sprzedażowe z 3 systemów wpływały codziennie.
Dla ok 500k wpisów z systemów sprzedażowych to była totalna sieczka optymalizacyjna. Bywało że raport generował się 5 minut, w samej bazie podobnie. Tutaj niestety nie można było wrzucić pk numerycznego i jedyne co pomogło to widoki zmaterializowane. Optymalizacja odsunięta o kilka miesięcy, ale następne co będzie trzeba zrobić to partycjonowanie i podejrzewam że w ciągu najbliższego roku będzie trzeba to zrobić w systemie.
Co ciekawe klient na nas nie narzekał, o wiele wiele bardziej narzekał na system w którym ewidencjonowali sprzedaż taki na S. ;) I z tego co wiem bardzo ich cisnęli o to że działa coraz wolniej. Dodam że dało się to odczuć, w ciągu trzech miesięcy generowanie raportu sprzedażowego z tamtego systemu wydłużyło się o kilka minut. przy czym nie było aż tak dużo danych bo 500k rekordów to nie jest dużo.

0

@Koziołek: a powiedz mi, co z generowaniem wartości id jeśli użytkownik rejestruje się customowo ? Wtedy nie dostaję id z zewnętrznego serwisu

0

@Bambo: Ja na Twoim miejscu użyłbym ID typu numerycznego, generowanego automatycznie.

Co zrobisz w przypadku, gdy użytkownik zarejestrował się z Google+, a pół roku później ten sam user rejestruje się z FB? W przypadku Twojego złożonego klucza masz architekturę, która wymusza dodanie nowego usera. Myślę, że oprócz unikalnego PK np. z sekwencji, potrzebujesz jeszcze jednego pola jak adres e-mail. Jest on unikalny dla każdego użytkownika. Więc jeśli użytkownik używa tego samego maila dla Google+ i FB, powinieneś go rejestrować do tego samego konta. Również jeśli ma ustawione hasło w połączeniu z tym samym mailem, to powinien to mieć jako trzecią możliwość zalogowania do tego samego konta. W takim przypadku id z G+ i FB może być jako oddzielne pole, które zawiera nulle.

Można pójść dalej. Użytkownik zarejestrowany customowo, może w panelu użytkownika powiązać swoje konto z FB i z G+. Zapisujesz tylko id z graph api do odpowiednich pól. I tak gość jak zapomni hasła będzie mógł zalogować się na swoje konto przez FB i G+ nawet jeśli jest zarejestrowany w tych usługach na inny adres e-mail niż Ty masz w bazie.

0

@kkojot:
Dzięki, przemyśle to. A powiedz mi, bo nie do końca rozumiem ..

Jak rejestruje usera pierwszy raz z FB no to tworzę mu jego PK jakim jest service info np w taki sposób : new ServiceInfo(fbId, Type.FB);

A co jeśli rejestruje go customowo ? Skąd mam wziąć jego id ?

1

Próbowałbym robić to w ten deseń:

@Entity
    class User {

        @Id
        @GeneratedValue
        private long id;
        
        private long fbId;
        private long googleId;
        
        @Column(nullable = false)
        private String email;

Id mam zawsze generowane automatycznie i unikalne. fbId i googleId zapisuje tylko jak ktoś się rejestruje z tych usług lub wiąże swoje istniejące konto w mojej bazie z tymi usługami.
W każdym przypadku zapisuję e-mail jako unikalny identyfikator użytkownika.

0

@kkojot:
Dzięki, faktycznie chyba w taki sposób najlepiej to zrobić bo przecież user może mieć konto zarówno w g+, fb jak sobie się zrobić customowo. Tylko gorzej jak ktoś zarobi sobie konto customowo o poda maila swojego kolegi, wtedy się robi cyrk. No chyba, że zrobię aktywacje konto przez maila :)

0

Oczywiście, że przy customowej rejestracji najlepiej jest wysłać link aktywacyjny na maila, aby go potwierdzić. Jeśli tego nie zrobisz to rzeczywiście może być cyrk. Gość rejestruje usługę na gmaila kolegi. Kolega rejestruje się z G+ i wchodzi na konto tego gościa :)

Potrzebujesz mieć pewność, że klient ma dostęp do maila na który się zarejestrował customowo. Inaczej jak zapomni hasła, to jak go zresetuje?

0

@kkojot:
Jeszcze jedna sprawa. Czy na fb każdy ma maila ? Bo jak sprawdzałem to, pytając się o "/me" mogę dostać puste pole. Co wtedy ? Przecież jak się user zaloguje to ja mu na tej podstawie wygeneruje JWT(ustawię subject jako email) i wyslę to w nagłówku, żeby on potem mógł normalnie wysyłać resztę zapytań. Co jeśli nie mam jego maila bo fb mi go nie zwrócił ?

0

FB wymaga albo maila, albo nr telefonu chyba. Nie wiem dokładnie. Sprawdź czy na pewno masz odpowiednie granty do pobrania maila usera z graph api. Jeśli tak i rzeczywiście user nie ma podanego maila to hmm, nie rejestruj go :D albo kombinuj jak obsłużyć ten przypadek.

0

W sumie wtedy mogę wymagać od niego podania maila :P

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