Programistyczne WTF jakie Was spotkały

3

@cerrato: w angielskiej wersji jest tak: screenshot-20180622203100.png

7

Szlachetna wydajności,
nikt się nie dowie,
jako smakujesz,
aż się zepsujesz*.

* - nie dowie się też debil, który napisał coś takiego:

sb.append("\n" + getClass().getSimpleName() + ".idNb OldVal: [" + this.getAvtrNb() + "], NewVal: [" + newValues.getidNb() + "]");

i powtórzył to ponad 250 razy w tylko jednym pakiecie.

1

W końcu udało mi się stworzyć bardziej złożone zapytanie, w EF Core, w debugerze pokazują mi się wyniki, teraz ładnie powinny się wyświetlać na frontendzie a tam pustka... tylko w konsoli czasami pojawia się:

"Http failure response for (unknown url): 0 Unknown Error" >

Noż ręce mi opadają... liczyłem, że problem zajmie mi wczoraj 30 minut... mam dziś godzinę prawie 20tą.

7

WTF? Ktoś nazwał funkcję symbolem euro? :O A nie... ta funkcja jest po prostu @deprecated.
Zrzut ekranu 2018-07-05 o 17.34.00.png

4

Jednak są rzeczy na które nas nie stać
screenshot-www.otomoto.pl-2018-07-13-12-21-31.png

5

Powrót z krótkiego urlopu zawsze dostarcza moc wrażeń na review.

  1. Sąsiedni team zaczął używać BigDecimal jako klucza głównego w domenie. Teraz są naprawdę przygotowani na wielką skalę rozwoju naszego biznesu. Większą niż galaktyczną. Ba!, ponad nawet lokalną gromadę galaktyk (tzw. biznes lokalny), bo BigInteger by na to wystarczył. Są przygotowani na fundamentalne zmiany w strukturze wszechświata i przejście na osbługę niepoliczalnej liczby kredytów ( Japoni, czy Włochom może się przydać...),... albo raczej tak im się wydaje. Muszę ich uświadomić, że komputerowy BigDecimal nadal jest ograniczony przez to fatalne Alef Zero.
  2. Nie ma to jak uświadomić kolejnych programistów o Optionalach.
    Teraz mam takie kwiatki :-)
 Optional.ofNullable(  remoteObject.getReference()).ifPresent( r -> target.setReferenceId(r.getId()));

Aczkolwiek pierwsza lekcja zaliczona, isPresent i get() nie jest użyty - zawsze to jakiś postęp...
Z drugiej strony problem to oczywiście remoteObject, który ma nulle i nic na to nie poradzimy.

EDIT:
W przypadku drugim
podany kod jest równoważny

if (remoteObject.getReference() != null) {
   target.setReferenceId(remoteObject.getReference().getId());
}

Tylko, niepotrzebnie bawimy się w Optionale, bo modne. Jeszcze tworzymy sobie jednego w locie (żeby zaraz się go pozbyć, ale to żaden problem, akurat cykle procka nie są u nas problemem, mamy dużo na zbyciu, razem z RAMem).

Dobre wyjście bez null checka istnieje, ale wymagałoby zmiany pola referenceId na Option, w ogóle wywalenie settera i przejście immutables i w ogóle...
ale to taka sobie kilkuletnia aplikacjia, gdzie taka zmiana IMO nie ma sensu. I tak porypane frameworki nie zrozumieją Optiona w domenie.
A może ktoś ma jakieś lepsze rozwiązanie?

4

Jankes programista (mid) napisał parę testów automatycznych (nie jednostkowych). Pewien mock działał w innym wątku udając zewnętrzny system. Problem, że mock dynamicznie dostawał polecenia czego ma się spodziewać z głównego wątku. Synchronizacji brak, nic co by niejawnie wstawiło barierę pamięci. I dziwnym trafem jego testy zaczęły zawodzić. O out-of-order execution, memory ordering i instruction scheduling pewnie nie słyszał. Otworzył mi błąd, że w systemie, coś nie działa ;). Poprawiłem mu kod, dodałem synchroznizację, to się oburzył, że wolno będzie działać, podczas gdy sam nawstawiała pół sekundowych sleepów ;D.

7

Post @nalik o Jankesie przypomniał mi sytuację, gdy trzeba było dostosować "framework testowy" do nowej platformy. Przychodzi kolega w poniedziałek i mówi mi, że nie jest pewien co pozmieniał w piątek, ale "nie ma już błędów" i te testy, które dostał do analizy już przechodzą. No to niewiele myśląc powiedziałem "Dodaj do repozytorium i daj do review", patrzę na commita, a tam funkcjonalność wywołania testów z jakiegoś powodu wykomentowana, na co kolega "o k**a! na chwilę zakomentowałem w piątek i zapomniałem odkomentować". Rzecz jasna po odkomentowaniu nastąpił powrót do dalszej analizy :-)

4

Testy jednostkowe to nieskończone źródło różnego typu WTF-ków.

for (Assent assent : resp.getYYYYResp().getRspn().getAssnt()) {

	if ("40".equals(assent.getXXXRcpt().getValue())) {
		assertEquals("2", assent.getXXXSts());
	}

	if ("39".equals(assent.getXXXRcpt().getValue())) {
		assertEquals("3", assent.getXXXSts());
	}

	if ("1".equals(assent.getXXXRcpt().getValue())) {
		assertEquals("3", assent.getXXXSts());
	}

}

Tu mamy podwójny WTF. Pierwszy to sama konstrukcja, która sprawdza nie wiadomo co, bo nie wiadomo co tak naprawdę znajduje się w liście. No ale... drugi WTF leży w assertEquals. Otóż gettery zwracają elementy JAXBElement<>, które nie za bardzo można porównywać ze zwykłymi stringami. A później zdziwienie, że testy nie działają.

10

Jak myślicie, co w php zrobi taka linijka?

rename('/home/user/Desktop/file123.jpg', 'wallpaper.jpg');

Tak! Przeniesie ten plik do current working directory. Why? Bo pod spodem rename to jest mv

$ mv /home/user/Desktop/file123.jpg wallpaper.jpg
13

robię sobie kalendarz w JavaScript więc rozgryzam / przypominam sobie szczegóły dotyczące dat. Obiekt Date w JS jest przesiąknięty złą sławą, bo jest on inspirowany obiektem Date z Javy (w końcu skądś JavaScript ma swoją nazwę, nie jest nazywany JavaScript bez powodu), więc są WTFy.

I tak wg JavaScript mamy 118 rok:

var date = new Date();
console.log(date); // 2018-07-29T17:20:44.724Z> 
console.log(date.getYear()); // 118 (WTF)

(dobra, już znalazłem na MDN, że getYear jest przestarzałe i żeby używać getFullYear ale i tak WTF. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getFullYear

Nie mówiąc już o sztuczkach tego typu, że żeby się dowiedzieć, ile dany miesiąc ma dni, trzeba utworzyć datę, która będzie reprezentowała zerowy dzień kolejnego miesiąca (czyli jeśli chcesz wiedzieć, ile dni ma marzec, to tworzysz datę "zerowego kwietnia" https://stackoverflow.com/a/1184359
Trochę jak na tym memie z Parks & Rec: https://imgur.com/gallery/nfbpv )

Co to do tworzenia dat, też trzeba pamiętać, że miesiące są numerowane od zera i można stosować sztuczki, np. MDN nas uczy, że:
, if values are greater than their logical range (e.g. 13 is provided as the month value or 70 for the minute value), the adjacent value will be adjusted. E.g. new Date(2013, 13, 1) is equivalent to new Date(2014, 1, 1), both create a date for 2014-02-01 (note that the month is 0-based
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date

No ale rezultat jest taki, że ciężko tym operować. Nie jestem w stanie utworzyć obiektu daty dla danego dnia :/ Chcę utworzyć datę pierwszego kwietnia, to wrzucam 2018, wrzucam 3 (0 - styczeń, 1 - luty, 2 - marzec, 3 - kwiecień) i wrzucam i 1 (pierwszy), a się wcale nie robi i wyskakuje mi 31 marca. Ale to już kwestia chyba strefy czasowej.

new Date(2018, 3 /* numeracja od zera */, 1);
2018-03-31T22:00:00.000Z

Więc jak skonwertować to do UTC? Hmm, jest coś takiego jak metoda getUTCDate! Czy to to? Hmmm, zobaczmy:

new Date(2018, 3 /* numeracja od zera */, 1).getUTCDate(); 
31 // WTF!!!?

Co pisze MDN?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCDate

The getUTCDate() method returns the day (date) of the month in the specified date according to universal time.

WTF. Przecież date to date a day to powinien być day. Co ciekawe funkcja getUTCDay (której i tak nie potrzebuję, bo chciałem całą datę) też istnieje, ale nie zwraca dnia miesiąca, tylko dzień tygodnia: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCDay

WTF WTFem pogania. Ciekawe czy w Javie też tak jest.

4

Nie wiem czy to dobry temat, ale klikajac w odnosniki jednego z tematow na 4p trafilem na to:

screenshot-20180729204338.png

1

Docker... a raczej Docker CLI na windowsa

  1. uzyj do logowania swojego maila
  2. odpal komende ktora dzialala Ci wczesniej na linuxie
  3. dostajesz Unable to find image 'postgres:latest' locally, zaraz po tym dostajesz unauthorized: incorrect username or password
  4. https://github.com/docker/hub-feedback/issues/935#issuecomment-300361781
  5. robisz reloga na ID
  6. Dziala
  7. ???????
4

W nawiązaniu do postu @LukeJL. Mamy sobie taki oto test:

@Test
public void floodGenerator() {
    Date db = new Date(1984, 0, 21);
    Set<String> generated = new HashSet<String>();
    for (int i = 0; i < 1000; i++) {
        generated.add(
                sut.generate(db, "M")
        );
    }
    System.out.println(generated);
    Assert.assertEquals(1000, generated.size());
}

Ma on za zadanie sprawdzić ile czasu zajmie wygenerowanie wszystkich numerów PESEL dla danej płci i daty urodzenia. Ma to nam podpowiedzieć jak dużo kolizji będziemy mieć w testach wydajnościowych. Trochę dupa-debug, ale nieważne.

Sam generator PESEL ma taki oto fragment:

Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(birthDt.getTime());
int yyyy = cal.get(Calendar.YEAR);
int MM = cal.get(Calendar.MONTH) + 1;
int dd = cal.get(Calendar.DAY_OF_MONTH);

if (yyyy < 1800 || yyyy >= 2400) {
    return "";
}

Pytanie ile numerów PESEL zostanie wygenerowanych?


Oczywiście 0. Bo Date w konstruktorze dodaje sobie 1900 do daty.

2

BYŁO W INNYM WĄTKU, ALE TO BYŁ ZŁY WĄTEK

Z serii spłodziłem potwora.

Importy statyczne w Javie są jakie są. Niektórzy lubią inni nienawidzą. W połączeniu z klasami wewnętrznymi można sobie za ich pomocą spaść z rowerka albo bardzo ułatwić pisanie czytelnego kodu. czy to opcja pierwsza czy druga sami oceńcie:

import static pl.firma.produkt.generator.loadui.configuration.ComposedRunConfigurationProvider.MergedRunConfiguration.MergedRunConfigurationBuilder.aMergedRunConfiguration;

Co ciekawe w samym docelowym kodzie:

static RunConfiguration merge(RunConfiguration left, RunConfiguration right) {
    int order = left.compareTo(right);
    return aMergedRunConfiguration()
            .withActive(pickOne(order, left.isActive(), right.isActive(), def.isActive()))
            // .... 
            .build();
}

Nazewnictwo jest jeszcze do poprawy, ale to pikuś.

#spłodziłempotwora

2

Raben - wielka, poważna, bogata, międzynarodowa firma spedycyjna.
Ale przycisków na stronie jakoś ogarnąć nie umieją ;)

screenshot-20180807163243.png

3

Zmieniłem pracę.
jenkins master
WTF1: ostatni zielony build 2017-09-01 22:00
WTF2: ostatni zielony ma numer #51 a najświeższy #117
liczba buildów na develop 511 (bardzo mało)

I tak sobie myślę albo zostanę tutaj zbawcą, albo zmienię pracę po okresie próbnym.

3

Bardzo pracowitych ludzi mam w dziale:

IF {ID} = 1 THEN "ZC"
ELSE IF {ID} = 2 THEN "ZK"
ELSE IF {ID} = 3 THEN "SC"
ELSE IF {ID} = 4 THEN "SCF"
ELSE IF {ID} = 5 THEN "SCL"
ELSE IF {ID} = 6 THEN "R-m P"
ELSE IF {ID} = 7 THEN "TN"
ELSE IF {ID} = 8 THEN "UM"
ELSE IF {ID} = 9 THEN "JC"
ELSE IF {ID} = 10 THEN "TP"
ELSE IF {ID} = 11 THEN "ZLM"
ELSE IF {ID} = 12 THEN "LA"
ELSE IF {ID} = 13 THEN "PT"
ELSE IF {ID} = 14 THEN "PZ"
ELSE IF {ID} = 15 THEN "ZB"
ELSE IF {ID} = 16 THEN "R-M PF"
ELSE IF {ID} = 17 THEN "UM"
ELSE IF {ID} = 18 THEN "F+U"
ELSE IF {ID} = 19 THEN "ZC"
ELSE IF {ID} = 20 THEN "ZK"
ELSE IF {ID} = 21 THEN "SC
ELSE IF {ID} = 22 THEN "SCF"
ELSE IF {ID} = 23 THEN "SCL"
ELSE IF {ID} = 24 THEN "R-m P"
ELSE IF {ID} = 25 THEN "TN"
ELSE IF {ID} = 26 THEN "UM"
ELSE IF {ID} = 27 THEN "JC"
ELSE IF {ID} = 28 THEN "TP"
ELSE IF {ID} = 29 THEN "ZLM"
ELSE IF {ID} = 30 THEN "LA"
ELSE IF {ID} = 31 THEN "PT"
ELSE IF {ID} = 32 THEN "PZ"
ELSE IF {ID} = 33 THEN "ZB"
ELSE IF {ID} = 34 THEN "R-M F"
ELSE IF {ID} = 35 THEN "UM"
ELSE IF {ID} = 36 THEN "F+U"
ELSE IF {ID} = 37 THEN "Europe"
ELSE IF {ID} = 38 THEN "BU"
ELSE IF {ID} = 39 THEN "FI"
21

screenshot-20180810164500.png

2

GOG -> nieopatrznie na wakacjach zalogowalem sie na swoje konto z innego kraju. Teraz nie jestem w stanie wybrac PLN jako waluty bo mam do wyboru tylko EUR i USD bo strona "mysli" ze jestem za granica chociaz wrocilem...

6

Może nie do końca programistyczny, ale WTF.

Aplikacja, którą obecnie rozwijamy korzysta z urządzeń mobilnych w roli pośrednika pomiędzy backendem a urządzeniem Bluetooth LE. Swego czasu biznes sobie zażyczył, żeby była możliwość monitorowania nieporządanych zachowań - jeżeli ktoś wchodzi w interakcję z tym urządzeniem bez udziału backendu, to powinniśmy mieć możliwość identyfikacji takich zdarzeń. Wymaganie rozsądne, więc zabraliśmy się za opracowanie rozwiązania.

Jak się okazało, zadanie wymagało dużo głębszych zmian i nie było ówcześnie do zrealizowania. Musieliśmy zatem opracować nowy protokół komunikacyjny, który by nam na to pozwalał. W skrócie korzystaliśmy z AESa wynegocjowanego z udziałem backendu i urządzenia BLE. Do detekcji złośliwych zachowań chcieliśmy użyć pewnych metadanych, które są generowane po każdej akcji i są dla niej unikalne w czasie.

Mimo że nie miało to sensu, to niemieccy spece od bezpieczeństwa rzekli "Schmetterlinge und Pfannkuchen dürfen nicht skaten", co mniej więcej tłumaczy się na "Metadane muszę być szyfrowane, bo tak". To nam trochę zabiło ćwieka, bo co prawda nowy protokoł szyfrował istotną komunikację po BLE, ale backend nie był przygotowany pod odszyfrowanie wiadomości. Poinformowaliśmy o tym dział bezpieczeństwa. W odpowiedzi otrzymaliśmy sugestię, która rozwiała wszelkie wątpliwości. Mamy szyfrować metadane i wysyłać je na backend razem z kluczem szyfrującym.

Rozwiązania oczywiście nie zaimplementowaliśmy, tylko zaktualizowaśmy naszą propozycję, żeby się jednak dało odszyfrować. Niemniej, niemiecka myśl techniczna wiecznie żywa.

4

Jednym z większych WTF-ów Gita jest, że można bardzo łatwo niechcący stworzyć sobie lokalną branczę o nazwie origin/master.

4

Dzisiaj przez przypadek odkryłem, że w JS pętla for..i służąca do iteracji po kluczach obiektu pozwala na iterację po null czy undefined, czyli po czymś co nie ma sensu i co powinno się wykrzaczyć (bo lepiej "fail fast" niż potem ma łazić gdzieś błąd),

for (let k in null) console.log("aaaa"); // nie pojawi się nic, ponieważ null nie ma kluczy

Czyli w zasadzie ukrywanie błędów, bo wtedy coś będzie null czy undefined (zamiast być obiektem) a my nawet tego nie wiemy.
Szczególnie, że w innych sytuacjach JS ładnie zgłasza błąd, kiedy potraktujemy null czy undefined jak obiekt (np. próba dostępu do właściwości null czy undefined kończy się rzuceniem wyjątku).

Co prawda nowsze Object.keys() się ładnie wykrzacza jak się poda null czy undefined, jednak dalej nie wykrzacza się, jak podamy mu liczbę albo boolean

Object.keys(null); // ładnie się wykrzaczy
Object.keys(false); // []
Object.keys(10); // []

Może to i ma jakiś sens (liczby nie są w JS obiektami, ale mogą być czasem traktowane jak obiekty, oraz mają odpowiedniki w obiektach np. new Number(10) tworzy liczbę, która jest obiektem), ale to tylko sprawia, że ten język staje się coraz bardziej zamotany. Mówi się o tym, że JS ma słabe typowanie ale ja bym raczej powiedział, że ma chaotyczne typowanie i nigdy nie wiesz, na jaką niespodziankę trafisz.

Np. konwersja nieznanej wartości foo do stringa. Kiedyś używałem foo.toString(). I było dobrze. Dopóki nie natrafiłem na null czy undefined. Wtedy był błąd.

TypeError: Cannot read property 'toString' of null

Więc nauczyłem się, żeby nie robić foo.toString tylko zacząłem się używać tricku z niejawnym przymuszaniem:

foo + '';

Który każdą wartość zamieniał ładnie na string.

I przez długi czas tak to zdawało egzamin. Potem jednak pojawiły się Symbole w ES6. I ku mojemu zdziwieniu ten kod:

const s = Symbol();
s + '';

rzucał wyjątkiem TypeError: Cannot convert a Symbol value to a string. I co robić? Jak konwertować stringi? Wtedy przeszukałem MDNy i zorientowałem się, że można konwertować wartości (w tym symbole) do stringa w ten prosty sposób

String(foo);

i to działa chyba na wszystkim. Proste i logiczne. Nie wiem czemu na to nie wpadłem wcześniej.

Ale trzeba oczywiście wspomnieć o tym, że String(foo) to nie to samo co new String(foo). Ten pierwszy tworzy stringa, ten drugi tworzy obiekt String, czyli coś, czego w 99% ( albo i więcej) przypadków nie będziemy potrzebować, a co może nam tylko zaburzyć porównania, bo jak porównujemy z == to jest równe, a jak z === to nie jest.

String("foo") == new String("foo"); 
String("foo") !== new String("foo"); 

co jest zresztą logiczne. Bo new String("foo") to obiekt więc czemu ma być równe === stringowi? Ale mimo wszystko mylące, błędogenne.

7

Zmieniła się pewna procedura w firmie i trzeba było odzwierciedlić to w kodzie. Za taska zabrał się nie kto inny jak sam PRINCIPAL SENIOR TECH LEAD ARCHITECT. Zmiana nie była wymagająca, ale unit testy już go przerosły. Zamiast zmodyfikować 2 (dwa!) testy, odpowiedzialne za ten fragment kodu, zwyczajnie je usunął. Oczywiście nikt nie mógł zareagować, bo jego nie dotyczą code review.... FML.

2

Numer NRB, ktokolwiek widział ktokolwiek wie.

W dzisiejszym odcinku mały WTF, który dużo mówi o tym dlaczego dokumentacja jest potrzebna. Zacznijmy jednak od podstaw. Numer NRB identyfikuje wasze konta w banku. Sposób jego generowania jest opisany w normie PrPN-F-01102, ale normę mało kto czytał. Wśród programistów zapewne niewielu ma pojęcie o istnieniu tej normy. Lecz przyjrzyjmy się historii kolegi K.

K. jest młodym programistą i ostatnie 4 godziny spędził próbując rozkminić następujący fragment kodu:

nrb = "";
        for (i = 0; i < 24; i++) {
            nrb += String.valueOf(r.nextInt(10));
        }
nrb = nrb + "252100";

Numer NRB ma 26 cyfr w tym 2 kontrolne na początku. Po co zatem dodatkowe 252100? Co więcej zalicza się je do wyliczania sumy kontrolnej! I tu właśnie potrzebna jest dokumentacja, która odeśle do normy. Brak dokumentacji, 4 godziny w plecy.

0

może nie WTF, ale zaskakujące zagadnienie... przy imporcie jednego wiersza danych w Excelu do tablicy otrzymujemy tablicę dwuwymiarową (jeden z wymiarów to oczywiście 1)... która jest tak naprawdę jednowymiarowa... ale można uroczo napisać piękny kod:

tbl = Application.Transpose(Application.Transpose(tbl))

i otrzymujemy naprawdę jednowymiarową tablicę, i dosłownie, i w przenośni... takich kwiatków jest więcej... :)

4

Backend dostał prikaz integracji "biblioteki" z bliźniaczego projektu. Teraz zagadka. Kto jest odpowiedzialny za poniższy kawałek kodu?

  1. Stażysta bootkampowicz.
  2. Hinduska siła robocza.
  3. #germanskiprogramistaarchitekt 30 lat doświadczenia.
public class SimpleTenantSpecific implements TenantSpecific {
  private TenantType tenant;

  public SimpleTenantSpecific() {
  }

  public SimpleTenantSpecific(TenantType tenant) {
    this.tenant = tenant;
  }

  public void setTenant(TenantType tenant) {
    if (this.tenant != null && this.tenant != tenant) {
      throw new IllegalStateException("Tenant must not be changed once it is set. Old value: " + this.tenant + ", New value: " + tenant);
    }
    this.tenant = tenant;
  }

  public TenantType getTenant() {
    return tenant;
  }

  @Override public boolean isActivatedFor(TenantType tenant) {
    if (this.tenant == null) {
      throw new IllegalStateException("Tenant needs to be set before method might be called");
    }
    return this.tenant == tenant;
  }
}

Co ciekawe, nie ma miejsca w kodzie, gdzie pusty konstruktor, byłby wywołany bez natychmiastowego użycia settera zaraz po nim. Moim zdaniem brakuje tylko tego, żeby zapewnić niemutowalność i zabezpieczyć się przed nullami.

public TenantType getTenant() {
  if (tenant == null) {
    throw new IllegalStateException("Tenant needs to be set before method might be called");
  }
  return tenant;
}
3

Przeglądania kodu na backendzie ciąg dalszy.

public Optional<Parcel> getParcel(ParcelIdentity parcelId) {
  Optional<ParcelEntity> parcelEntityOpt = entityRepository.findByParcelId(parcelId.getStr());
  return parcelEntityOpt.isPresent() ? Optional.of(parcelEntityOpt.get().getDomainObject()) : Optional.empty();
}

I tak praktycznie wszędzie gdzie jest Optional użyty. Popełnił junior bootkampowicz, więc nie będę się znęcał. Natomiast przyklepał programista ze stażem > 5 lat.

1

Nie ma to jak rozwiązać prosty problem nadania wątkowi nazwy, w zakręcony nieczytelny i mistyczny sposób:
https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
Oczywiście jak brakuje jakiejś flagi to się kończy niezłapanym wyjątkiem :/.

3

Najczęstsza przyczyna NPE? Co zabawne zdarza się od czasu do czasu, bo ktoś coś inaczej skonfiguruje.

log.log("SqlSession initialized with env=" + sqlSessionFactory.getConfiguration().getEnvironment().getId());

Getter train has no breaks

Getter train has no breaks

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