Wypowiedzi
-
A w Javie pewnie będzie się to musiało sprowadzić do jakichś ifów czy case'ów.
Albo od razu w tych ifach, które masz, musiałbyś dodać ustawianie odpowiednio left = new BSTNode(key) lub right = new BSTNode(key), zamiast zostawiać sobie tylko jedno przypisanie na koniec;
albo... może trochę bardziej wyrafinowanie - jakaś osobna klasa, w której zamkniesz tę selekcję węzła do modyfikacji, a później ją tylko "wykonasz". Coś jak poniżej. Choć prawdę mówiąc wydaje mi się to przerostem formy nad treścią, chyba nie ma co tak tego komplikować w tym przypadku.
public class BSTChildUpdate {
public enum ChildSelector {
LEFT, RIGHT;
};
BSTNode parent;
ChildSelector child;
public BSTNodeUpdate(BSTNode parent, ChildSelector child) {
this.parent = parent;
this.child = child;
}
public void execute(BSTNode value) {
switch (child) {
case LEFT:
parent.left = value;
break;
case RIGHT:
parent.right = value;
}
}
}
Przykład użycia:
BSTNodeUpdate update;
if (key < node.key) {
update = new BSTNodeUpdate(node, ChildSelector.LEFT);
} else {
update = new BSTNodeUpdate(node, ChildSelector.RIGHT);
}
update.execute(new BSTNode(key));
-
Odnośnie OCPJP (nie OCAJP - tego nie znam), to ja, kiedy zdawałem jego poprzednie wcielenie, tzn. SCJP, zrobiłem to za własne pieniądze i z czysto wewnętrznej motywacji. I z pełnym przekonaniem - nie żałuję.
Nie żeby dał mi ten papier sam w sobie większe szanse na pracę. W pewnych firmach być może, ale w większości - nie. On mi dał tyle, że wyznaczył bardzo konkretny kurs poprzez pewne zagadnienia tego języka i bardzo konkretne tempo jego przejścia. Zależnie od osobowości - taki drogowskaz może być Ci niepotrzebny, a może być zbawienny. Mnie był co najmniej bardzo przydatny.
Wydaje mi się, że w Twoim przypadku - skoro masz już doświadczenie i sposób myślenia, a musisz tylko wypełnić lukę w języku - to może być również dobra ścieżka.
Nie wierz, że tam musisz tylko wyryć składnię i sposób działania kompilatora (choć uważam, że to również bywa potrzebne). To uproszczenie, którego nie warto brać poważnie. Jeśli chcesz się nauczyć, a nie tylko odbębnić, to dostaniesz bardzo solidne podstawy Javy podane w zorganizowany sposób. Po prostu spójrz w jakieś wymagania czy spis treści książki i sam oceń... -
Paweł K.:
Mógłbyś wyjaśnić jak działa i kiedy należy używać redirect?
Tak w skrócie mówiąc - redirect to taka króciutka odpowiedź do przeglądarki, która zmusza ją do wysłania kolejnego żądania. Więc jeśli Ty wysyłałeś redirect z adresem "/", to tak, jakbyś mówił przeglądarce: zapomnij o wyniku poprzedniego żądania, ściągnij zasób spod adresu "/". No to przeglądarka tak robi i ściąga nowym żądaniem czysty formularz.
Teraz, kiedy zwracasz "contact", Spring generuje w odpowiedzi "normalną" treść w oparciu zapewne o jakiś contact.jsp - ciągle w ramach tego samego żądania, w którym nastąpiła walidacja, a więc mając pod ręką w modelu cały jej wynik.
Nie wiem na ile to jasno brzmi. Zawsze możesz spojrzeć np. tu: http://en.wikipedia.org/wiki/URL_redirection ;-) Wśród zastosowań zwróć uwagę szczególnie na http://en.wikipedia.org/wiki/Post/Redirect/Get -
Paweł K.:
Co może być powodem takiej sytuacji?
Myślę, że teraz powodem jest to, że do wyświetlenia formularza w przypadku błędów używasz redirecta. Nie wiem czy wiesz jak działa redirect, ale do Twojego celu się nie nadaje. Powinieneś prawdopodobnie zwrócić po prostu nazwę widoku formularza, czyli u Ciebie "contact".
Tyle, że z Twojego kodu, który zacytowałeś na początku, widzę, że ten widok wymaga jeszcze w modelu atrybutu "contactList", więc być może powinieneś tę listę jeszcze doładować. -
Na moje oko Ty nie sprawdzasz w ogóle wyniku walidacji. Czy jest dobrze, czy źle - wywołujesz contactService.addContact(contact);
Musisz swoją metodę ContactController.addContact trochę rozbudować - na wzór tego, co wkleił Bartosz. Jeśli BindingResult ma błędy, to przygotować model i pokazać ponownie formularz. Jeśli nie ma, to zapisać contact. -
Jarek Żeliński:
z mojego doświadczenia wynika, że co do zasady łatwo obronić tezę o własności prywatnej: jak ktoś potrzebuje coś mojego to musi mnie o to poprosić, nierozsądne jest rozdawanie z lenistwa kluczy od własnego mieszkania każdemu kto czasem coś ode mnie potrzebuje
No tak Jarku, przykład jest bardzo zgrabny. Ale w takim razie - co robisz kiedy potrzebujesz skorzystać z usług, którym musisz dostarczyć coś z Twojego domu? Na przykład z tapicera, któremu chciałbyś zlecić naprawę kanapy? Czy zgodzisz się, że to jest uzasadniony przypadek użycia gettera:
tapicer.napraw(dom.getKanapa())
czy masz na to inny sposób? -
Ja z tego zamieszania wyciągam taki wniosek, że ci, którzy pisali, że get/set są złe, trochę przesadzili - być może celowo, dla wywarcia wrażenia na czytelnikach :-) Tak naprawdę sami używają get/set - bo i ciężko nie używać.
Po prostu w niektórych sytuacjach można tego uniknąć, jeśli powstrzymać pierwszy odruch i chwilę się zastanowić.
Paweł Włodarski:
Na potrzeby ograniczonego miejsca i czasu weźmy sobie taki trywialny przykład.
class Account{
Waluta euroGabki;
void operacja1();
void operacja2();
}
Możemy nie chcieć ujawniać sposobu przechowywania ilość Gąbek w "logice" ale chcieć wyświetlić ich ilość
w widoku. Toteż pierwszym krokiem może być dodanie klasy, która jest odpowiedzialna stricte za reprezentację tych danych w widoku.
class AccountDTO{
String ilośćEuroGąbek;
String get();
}
Ja tego przykładu nie kupuję.
Po pierwsze - jak rozumiem, obciążyłeś klasę Account obowiązkiem generowania swojego DTO dla widoku. Nie napisałeś tego wprost, ale skoro Account ma nikomu nie udostępniać danych, to jest jedynym możliwym producentem DTO. Tymczasem moim zdaniem to, że ktoś chce wyświetlić jej dane, nie powinno jej w żadnym stopniu obchodzić. A jeśli dosłownie traktować tezę "gettery są złe" (choć jak już pisałem - uznaję, że nie należy ich traktować dosłownie), to i tak niewiele zyskałeś, bo masz getter w AccountDTO.
Po drugie - dlaczego w tym DTO jest String? Kto ma konwertować tę ilość gąbek na Stringa? Wg Twojego przykładu chyba Account (!), czy tak? Skąd ma wiedzieć jak ten String ma być sformatowany, jeśli na przykład użytkownicy oglądający ten widok są z różnych krajów i chcą różnie sformatowanych liczb? To są zadania dla widoku, nie dla klasy Account.
Nie mówiąc już o tym, że nawet gdyby przyjąć, że takie DTO będzie dobre dla widoku, to tylko dla widoku. Dla innych warstw, choćby dla jakiejś klasy, która ma zsumować ilości gąbek z wielu Accountów, już nie bardzo.
Pominąłbym tego Stringa jako uproszczenie przyjęte na potrzeby postu, gdyby nie to, że na tym opierasz swój argument, zatem chyba zrobiłeś to całkiem celowo.
I wracając do twojej uwagi :
Bo to, co od razu widać, to że jeśli jakiś atrybut Engine'u dojdzie, zniknie, zmieni typ, czy coś podobnego
to mamy potencjalnie jedną klasę więcej do modyfikacji.
To jednak po zmianie typu pola mamy potencjalnie mniej klas do zmiany bo inne klasy nie powinny uzależnić się od tego pola.
No niestety tylko dlatego, że właśnie zastąpiłeś oryginalny typ danych innym typem, skonwertowanym specjalnie dla wybranego zastosowania. -
Paweł Włodarski:
Od getterów nie uciekniesz ale nie o to tutaj chodzi. Przenosisz getery z Engine do czegoś co jest strukturą danych zawierającą dane o obiekcie Engine. Struktura ta jest używana tylko do wyświetlenia danych na stronie i w innych warstwach aplikacji bezpośrednio geterów w klasie engine nie napotkamy.
Czy możesz wyjaśnić co konkretnie taki zabieg ma na celu? Bo to, co od razu widać, to że jeśli jakiś atrybut Engine'u dojdzie, zniknie, zmieni typ, czy coś podobnego, to mamy potencjalnie jedną klasę więcej do modyfikacji.
Jak dotąd ta teoria o złu getterów i setterów wygląda jak dla mnie na coś, co wywodzi się od słusznego dążenia do hermetyzacji, ale zostało tak bardzo spotęgowane, że ociera się o absurd. Bo jeśli reszta systemu albo użytkownik potrzebuje informacji o mocy albo pojemności skokowej silnika, to potrzebuje i kropka.
Jeśli się mylę, to bardzo chętnie poszerzyłbym horyzonty, dlatego proszę o to wyjaśnienie :-) -
Obstawiam, że brakuje Ci apostrofów:
query = "select link from odwiedzone where link='" +url + "'";
Ale lepiej:
query = "select link from odwiedzone where link = ?";
PreparedStatement pstmt = conn.prepareStatement(query);
pstmt.setString(1, url);
rs = pstmt.executeQuery();
Poza tym w swoim if-ie sprawdzasz nie to, co trzeba ;-) -
A nie możesz wykonać wcześniej jeszcze jednego selecta z tabeli Odwiedzone z warunkiem where link = <twój_link> i sprawdzić czy zwróci zero wierszy, czy nie zero? Może nawet zamiast zapytania o ostatnie id - jeśli masz wpływ na schemat bazy danych, to tę kolumnę mógłbyś ustawić jako auto_increment i pozostawić wygenerowanie wartości bazie.
Na marginesie. Nie wiem skąd się bierze wartość argumentu url. Jeśli przypadkiem wprost z requesta, to wystrzegaj się tego. Wklejając w taki sposób w SQLa cokolwiek, co przychodzi bezpośrednio od użytkownika, możesz się wystawić na tego typu atak: http://en.wikipedia.org/wiki/SQL_injection.
Nawet jeśli nie grozi Ci katastrofa w tym konkretnym zapytaniu, to taka praktyka jest niebezpieczna i trzeba się jej oduczyć. -
A ja uważam, że to oburzenie na tego typu rozmowy jest przesadne. Co do bezsensu niektórych z tych pytań pewnie można by się zgodzić, ale myślę, że wiele z nich ma jednak uzasadnienie.
Zdarza się - myślę, że sporo nas tutaj, na tej grupie, o tym wie - że w ogłoszeniu przyciąga kandydata coś innego, niż tylko kasa. Że coś mu tam w sercu drgnęło, może dostrzegł szansę na jakieś spełnienie, na to, żeby się pracując jednocześnie pobawić. Wydaje mi się, że taki ktoś wcale nie bez ochoty o tym porozmawia, również z HRowcem. A co ważniejsze - ten HRowiec, jeśli oczywiście zna się na swojej pracy, z dużym prawdopodobieństwem to zauważy. Nie wiem jak traktują to pracodawcy - jak dla mnie to coś bardzo cennego, bo może zapowiadać, że trafiło się na perełkę, z której będzie świetny specjalista, nawet jeśli w tej chwili jest początkujący.
Z drugiej strony jeśli jedyną motywacją do pracy są pieniądze, a jedynym powodem przyjścia kandydata na rozmowę w dane akurat miejsce, jest chwilowy brak miejsc ciekawszych, to owszem - zapewne cała ta rozmowa z jego strony jest ściemą. Być może takie właśnie uczucia najczęściej towarzyszą autorowi tej nieco dziecinnej wyliczanki pod podanym linkiem. Ale mam wrażenie, że to też dzięki takiej rozmowie nietrudno wyczuć.
Wyobrażam sobie, że do tego, jak również do wybadania kilku innych rzeczy, które z kodu trudno wyczytać - umiejętności wypowiedzenia się (programista przecież też musi komunikować się z innymi; im wyższe jego stanowisko, tym częściej go to czeka i tym sprawniej powinien potrafić dostosować język do różnych odbiorców), sposobu myślenia kandydata o swojej pracy, karierze, problemach zawodowych, prawdopodobieństwa jego szybkiego odejścia, skłonności do konfliktu, itd., przydatne są właśnie te znienawidzone pytania. Nie po to, żeby sprawdzić przebojowość - a przynajmniej taką mam nadzieję. No bo gdyby ktoś faktycznie odrzucił zdolnego kandydata na programistę tylko dlatego, że jest niewystarczająco przebojowy, to zgadzam się z Dariuszem - wielka szkoda, może przede wszystkim dla niego samego. Ufam, że rozumowanie rozmówców ze strony HR jest jednak głębsze, niż niektórzy oburzeni kandydaci twierdzą.
Oczywiście możliwe jest też, że komuś Bozia dała talenty do przemawiania, a poskąpiła talentów do bycia inżynierem. Taki ktoś być może HR nabierze, więc jakaś forma sprawdzianu umiejętności technicznych jest bardzo potrzebna i chyba zwykle ma miejsce. Linus ma rację :-) -
O nie, tego nie mogę zrobić :-) To moja ostatnia szansa, żeby Ci utrudnić takie rozwiązanie ;-) Poważnie - jeśli jedyna różnica między tymi persistence unitami to miałyby być dane dostępowe do bazy, to nie tędy droga. Zrób jeden persistence unit, w którym w ogóle nie wpisuj tych danych, tylko podaj nazwę datasource:
...
<non-jta-data-source>jdbc/myDatasource</non-jta-data-source>
...
Szczegóły tego datasource (URL, user, hasło, i potencjalnie dużo innych) podefiniuj na serwerach na poszczególnych środowiskach.
http://tomcat.apache.org/tomcat-6.0-doc/jndi-resources... -
Ja jeszcze raz zaryzykuję nieodpowiedzenie na Twoje konkretne pytanie :-) Bo nurtuje mnie czym tak naprawdę ma się różnić Wasza warstwa ORM między środowiskami. Nie chodzi tu rozumiem o adres bazy danych, bo od tego jest abstrakcja zwana... datasource.
Mają mieć różny zestaw encji? Wydaje mi się to karkołomne. Różnych persistence providerów? Chyba nie mniej karkołomne. Może żebyśmy się wypowiedzieli czy to ma sens (bo o to również pytałeś), przydałoby się, żebyśmy to wiedzieli.
No ale jeśli już rzeczywiście chcesz tak robić, to może pomógłby Ci SpringEL? Coś w tym rodzaju:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="#{ systemProperties['persistenceUnitName'] }"/>
</bean>
-
Jedna z opcji to parametryzacja takich plików i przepuszczanie ich na etapie budowania przez mechanizm, który podstawia wartości parametrów - różne dla różnych środowisk.
Jeśli np. użyłbyś do budowania Mavena, to jest to tam nazywane "filtering". Można się wtedy obyć bez żadnych if-ów. -
XML-RPC to w gruncie rzeczy bardzo prosty protokół. Na wszelki wypadek podam link do specyfikacji: http://xmlrpc.scripting.com/spec.html. Warto przeczytać, jest zaskakująco krótka.
Nie wiem na czym polega to niedziałanie, o którym piszesz. Żeby podzielić problem na pół, może spróbuj "na piechotę" wywołać metody tego serwisu - albo jakimś narzędziem albo wręcz na szybko napisanym programem w Javie. Tutaj jakiś przykład z użyciem biblioteki Apache'a: http://ws.apache.org/xmlrpc/client.html
Zobacz czy serwis w ogóle odpowiada, jeśli tak, to co. Być może to z nim jest coś nie tak. Nie chce mi się wierzyć, żeby biblioteki dla Javy sobie z tym nie radziły. -
Mnie Twój artykuł bardzo zaskoczył, żeby nie powiedzieć zbulwersował! :-) Uważam, że spośród wszystkich haseł skracanych przez anglojęzycznych do chwytliwych akronimów, DRY jest akurat święte.
Don't Repeat Yourself. Jeśli się powtarzasz, zaszywając wiedzę o tej samej rzeczy w kilku miejscach, tworzysz program, który jest nielogiczny, potencjalnie niespójny i szybko może się stać koszmarem w utrzymaniu. Każdą zmianę i poprawkę do zmultiplikowanej funkcjonalności musisz wprowadzać w wielu miejscach. Testować - czy to automatycznie, czy białkowo - też powinieneś osobno każde z tych miejsc.
Dlatego starałem się dosyć uważnie przeczytać przykład. Jaka to wysoka cena może towarzyszyć usuwaniu duplikacji? Chyba nie do końca rozumiem go w szczegółach, ale wydaje mi się, że Ty właśnie zakładasz, że każde wykorzystanie ponowne kodu musisz osiągać przez dziedziczenie. To nieprawda - stosujmy kompozycję. Sporo o tym napisano.
Czyli - w Twoim przykładzie - nie umieszczamy kodu do pobierania użytkownika z bazy danych w kontrolerach, tylko w niższej warstwie, usługowej. Kontrolerom dajemy referencję do usług. Tylu kontrolerom, ilu chcemy, bez żadnego duplikowania kodu.
Jeśli źle zrozumiałem przykład i nie da się go załatwić kompozycją, to wyjaśnij go proszę. -
Polecam dokładniejszą lekturę logu ;-) Nie tworzy Ci tabeli, bo baza danych nie zgadza się na kolumnę typu varchar(255), na której miałaby zastosować automatyczne zwiększanie - identity (start with 1). Też bym się nie zgodził na jej miejscu! :-D
Jeśli chcesz taki liczbowy automatycznie zwiększany klucz główny, to zrób to tak, jak zrobiłeś w innych HBMach - tam jakoś uzyskałeś typ bigint. Jeśli to ma być klucz tekstowy, to musisz wybrać jakiś inny <generator>.
A co do ostatniego problemu - pomijając, że znów nie do końca rozumiem dlaczego chciałbyś zobaczyć INSERT INTO podczas tworzenia tabel (domyślam się, że to taki skrót myślowy), to przede wszystkim mam wrażenie, że Ty w swoim programie wywołujesz puste metody createpanel() i displaypanel(). -
A dlaczego masz aż trzy pliki konfiguracyjne? O ile nie masz jakiejś nietypowej potrzeby, o której nie wiem, powinieneś mieć tylko jeden: hibernate.cfg.xml.
Zerknij:
http://docs.jboss.org/hibernate/core/3.5/reference/en/...
W nim powinieneś wpisać HBMy wszystkich encji, których chcesz używać w swojej SessionFactory. W Twoim przypadku prawdopodobnie wszystkie trzy, coś w tym stylu:
<mapping resource="admin.hbm.xml"/>
<mapping resource="panel.hbm.xml"/>
<mapping resource="strona.hbm.xml"/>
Ustaw też na razie
<property name="hbm2ddl.auto">update</property>
i spróbuj uruchomić. -
Ja się zgubiłem. Jeszcze raz: masz dwa pliki hbm: admin i panel? Ale w konfiguracji Hibernate (tzn. w hibernate.cfg.xml) odwołujesz się tylko do jednego z nich: admin, tak? Hibernate nic zatem nie wie o panelu. O ile rozumiem, ten mapping, którego nam pokazujesz, to jest właśnie panel. On jest więc zupełnie nieistotny.
Zawartości admin.hbm.xml nam chyba nie pokazałeś, ale domyślam się, że mapowany jest on do tabeli ADMIN. Hibernate więc, poinstruowany przez Ciebie parametrem hbm2ddl.auto=create, próbuje przy starcie aplikacji taką tabelę stworzyć.
Jaki jest właściwie Twój problem? :-)
Jeśli taki, że wykłada się z komunikatem, że taka tabela już istnieje - to chyba dlatego, że...taka tabela już istnieje ;-) Ja osobiście nie używałem opcji create, ale z opisu wygląda mi ona na taką, która da się uruchomić na danym schemacie bazy danych tylko raz. Stworzy tabele, one już zostają, a jeśli próbujesz drugi raz - baza się buntuje. Może lepsza dla Ciebie byłaby opcja update? Albo create-drop? (uwaga, ta druga robi czystkę) -
Intencje są takie same, jak u kolegów powyżej: przedstawić liczbę oznaczającą numer pliku w systemie liczbowym o podstawie innej, niż 10. Większej, niż 10, bo wtedy zapis będzie potencjalnie krótszy. Maksymalna podstawa, przy której możemy użyć "alfabetu" złożonego z cyfr dziesiętnych i liter łacińskich, to 36, bo tyle w sumie jest tych znaków.
Przytoczona przeze mnie metoda nie robi nic innego. Tyle, że ona z góry zakłada, że ten "alfabet" jest właśnie taki: 0123456789abcdefghijklmnopqrstuvwxyz. Twoje oryginalne pytanie w zasadzie nie przewidywało cyfr, tylko litery. Jeżeli rzeczywiście Ci na tym zależy, to użyj kodu Jakuba :-)
- 1
- 2