Tym razem nie u siebie, ale u Macieja na devstyle.pl wspominam, jak to było z konkursem Daj się poznać. Właśnie startuje kolejna edycja!
Tym razem nie u siebie, ale u Macieja na devstyle.pl wspominam, jak to było z konkursem Daj się poznać. Właśnie startuje kolejna edycja!
Wraz z majem zakończył się konkurs Daj się poznać, w którym z maniakalnym uporem brałam udział od samego początku do samego końca.
Wygląda na to, że dostałam się do ścisłego finału! Lista finalistów jest dostępna tutaj, razem z linkiem do ankiety, w której można oddać na mnie głos. Byłoby mi bardzo miło, gdybym wygrała wypasione krzesło. Głosującym dam w nim posiedzieć i jeszcze podam piwo 😉
Listę moich wpisów konkursowych można znaleźć w poprzednim tekście: Podsumowanie mojego udziału w konkursie „Daj się poznać”.
Przy okazji, krótkie wyjaśnienie, dlaczego nie opublikowałam nic przez ostatni tydzień:
Bardzo dziękuję za dotychczasowe wsparcie.
Do zobaczenia wkrótce!
Od początku marca biorę udział w konkursie Daj się poznać, organizowanym przez Macieja z bloga Devstyle. Konkurs oficjalnie kończy się jutro.
Na stronie agregującej blogi zgłoszone do konkursu wyraźnie widać trend podsumowań. Niniejszym do niego dołączam. Pozwolę sobie również przypomnieć tekst, który napisałam na półmetku: Konkurs „Daj się poznać”: status i wrażenia.
Proste! Przez co najmniej 10 tygodni na przestrzeni 3 miesięcy (marzec-maj) należało rozwijać swój projekt open source i blogować na jego temat.
Wymagania wobec mojego projektu opisałam w pierwszym konkursowym wpisie: Dam się poznać: mój projekt open source.
Podobno gala z wręczeniem nagród!
A ja powoli wracam do pracy zarobkowej.
Nie zamierzam porzucać projektu, ale na pewno będę blogować o niem trochę rzadziej.
Poprzedni wpis poświęciłam kwestii wysyłania maili przez aplikację napisaną w Javie.
Kontynuując, teraz chcę upewnić się, że świeżo zarejestrowany użytkownik to człowiek (a nie bot) i że podał poprawny adres email. Poniżej opisuję kroki prowadzącego do tego celu.
Po wypełnieniu formularza rejestracyjnego obiekt reprezentujący użytkownika jest zapisywany w bazie danych jako nieaktywny. Razem z nim zapisywany jest losowo wygenerowany kod do aktywacji konta. Na tym etapie użytkownik nie może się logować, nie zna też przypisanego mu kodu.
1 2 3 4 5 6 7 8 |
... @Document public class User { ... private boolean confirmationStatus; private String confirmationId; ... } |
1 2 3 |
private String createConfirmationID() { return java.util.UUID.randomUUID().toString(); } |
Aplikacja wysyła na adres podany przez użytkownika wiadomość zawierającą link aktywacyjny. Kod wygenerowany losowo w poprzednim kroku jest doklejony do adresu jako parametr URL.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
... @Controller public class UserController extends WebMvcConfigurerAdapter { ... @RequestMapping(value = "/signup", method = RequestMethod.POST) public String checkPersonInfo(@Valid User user, BindingResult bindingResult, Model model) { ... user.setConfirmationId(createConfirmationID()); emailService.send(user.getEmail(), "Szafbook Account Confirmation Link", "http://szafbook.pl/confirm?id=" + user.getConfirmationId()); userRepository.save(user); model.addAttribute("message", "Please, check your mailbox for the email confirmation link. "); return "message"; } ... } |
Wejście pod adres podany w mailu powoduje uruchomienie servletu weryfikacyjnego. Aplikacja szuka w bazie danych obiektu użytkownika, któremu przypisano kod podany jako parametr. Jeśli taki użytkownik istnieje, jest oznaczany jako aktywny. Od tej chwili może się już logować, ponieważ potwierdzono (czy raczej zwiększono prawdopodobieństwo), że to rzeczywista osoba.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
... @Controller public class ConfirmationController { @Autowired private UserRepository userRepository; @RequestMapping("/confirm") public String greeting(@RequestParam(value="id", required=true) String confirmationId, Model model) { User user = userRepository.getUserByConfirmationId(confirmationId); String message = "Invalid confirmation id. Contact us or try again."; if(user!=null){ if(!user.isConfirmationStatus()){ user.setConfirmationStatus(true); user.setConfirmationId(null); userRepository.save(user); } message = user.getUserName() + ", your account has been verified. You may now log in. "; } model.addAttribute("message", message); return "message"; } } |
Należałoby usunąć zbyt długo przechowywane nieaktywne konta. Ale jeszcze się za to nie zabrałam 🙂
Oto ilość pracy, którą włożyłam w wyszukanie użytkownika w bazie danych MongoDB po wartości confirmationId
:
1 2 3 4 5 6 |
... public interface UserRepository extends CrudRepository<User, BigInteger>{ public User getUserByConfirmationId(String confirmationId); } |
Resztę robi za mnie Spring Data, parsując nazwę metody!
Więcej informacji na ten temat można znaleźć tutaj.
To ostatni techniczny wpis w ramach konkursu Daj się poznać 2016. Jutro postaram się napisać krótkie podsumowanie. O ile wieczorem na spotkaniu młodych mam nie poleje się za dużo wina.
Warunkiem utworzenia konta użytkownika jest podanie poprawnego adresu email, do którego dany użytkownik rzeczywiście ma dostęp.
W kolejnym wpisie omówię dokładniej proces weryfikacji. Na teraz istotne jest to, że moja aplikacja musi być w stanie wysłać do użytkownika maila z linkiem do uaktywnienia konta.
Korzystam z frameworka Spring Boot (czyli właściwie z nakładki ułatwiającej korzystanie z frameworka Spring zgodnie z zasadą “konwencja ponad konfiguracją”).
Kroki do realizacji mojego celu są następujące:
1 2 3 4 |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
... import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; @Service public class EmailService { @Autowired private JavaMailSender javaMailSender; public void send(String to, String title, String contents) { MimeMessage mail = javaMailSender.createMimeMessage(); try { MimeMessageHelper helper = new MimeMessageHelper(mail, true); helper.setTo(to); helper.setReplyTo("auto@szafbook.pl"); helper.setFrom("auto@szafbook.pl"); helper.setSubject(title); helper.setText(contents); } catch (MessagingException e) { e.printStackTrace(); } finally { } javaMailSender.send(mail); } } |
1 2 3 4 5 6 7 8 |
... spring.mail.host=serwer.smtp spring.mail.port=nr.portu spring.mail.username=username spring.mail.password=password spring.mail.properties.mail.smtp.starttls.enable=true spring.mail.properties.mail.smtp.starttls.required=true spring.mail.properties.mail.smtp.auth=true; |
Koniecznie jest nadanie wartości true
właściwości spring.mail.properties.mail.smtp.starttls.enable
, a najlepiej całej trójce kończącej powyższy listing.
To akurat sprawa typowa dla serwisu GMail. Jeśli chcesz, żeby aplikacja łączyła się z kontem email i w Twoim imieniu wysyłała maile, musisz stworzyć specjalne hasło aplikacji, różne od hasła prywatnego. W każdej chwili możesz podejrzeć listę aplikacji, które korzystają z takiego hasła (na stronie https://security.google.com/settings/security/apppasswords), a także dowolną z nich zablokować.
Google uprzejmie zwraca uwagę, że nadal nie jest to najbezpieczniejsze rozwiązanie.
Ten błąd pojawił się, kiedy przełączyłam konto z GMaila na adres email na mojej wirtualnej instancji obsługującej kilka domen. Początkowo myślałam, że Java Mail nie chce pracować z “wirtualnymi” adresami obsługiwanymi przez serwer SMTP w innej domenie. Ostatecznie okazało się jednak, że przyczyną błędu był nadmiarowy biały znak w pliku application.properties 🙂
Wiele aplikacji wymusza na użytkowniku podanie działającego adresu email. Główne powody to:
Wiele osób tworzy sobie dodatkowe konto email, które używane jest do rejestracji w mniej istotnych serwisach (sama mam osobne konto do zakupów internetowych). Zdarza się jednak, że adresu email żąda strona lub aplikacja, z którą na pewno już nigdy nie będziesz mieć nic wspólnego. Co wtedy zrobić?
Możesz założyć tzw. jednorazowe (ang. disposable) konto email, np. w serwisie Guerilla Mail. Poczta, która tam trafia, jest przechowywana przez godzinę niezależnie od tego, czy ktoś ja odczyta, czy nie.
Ocenę moralną pozostawiam Czytelnikowi.
Do tej pory uruchamiałam aplikację tylko lokalnie. Przed odpaleniem pliku .jar z wbudowanym serwerem Tomcat musiałam najpierw uruchomić lokalny serwer bazy danych.
W serwisie Pivotal istnieje tzw. Marketplace, w którym można kupować dodatkowe usługi. Wygląda to tak:
Odnalazłam na liście interesujący mnie element:
Moim celem jest teraz włączenie serwisu i powiązanie go z moją aplikacją Szafbook.
Mogę zrobić to z wiersza poleceń przy użyciu narzędzia cf (muszę wówczas użyć poleceń cf create-service
, cf bind-service
i ewentualnie cf-restage
), mogę też wyklikać swoje wymagania w przeglądarce, na stronie Pivotal:
W przypadku wielu dodatkowych serwisów muszę wybrać pomiędzy kilkoma “planami” – najczęściej jeden z nich jest darmowy i dość ograniczony (np. 5 jednoczesnych połączeń, 20MB pojemności), a kolejne są coraz mocniejsze i coraz droższe. MLab na razie ma tylko darmową wersję.
Po kilkunastu sekundach mam już działające połączenie pomiędzy bazą a aplikacją:
Chmura jest w stanie samodzielnie podmienić źródło danych aplikacji w taki sposób, by odwoływało się do nowoutworzonego serwisu. Programista nie musi nawet znać adresu tej usługi! Choć, oczywiście, może go sobie podejrzeć:
development
na cloud
) przy budowaniu na potrzeby wdrożenia.
1 |
spring.profiles.active=cloud |
O MongoDB pisałam już w dwóch wcześniejszych postach: Jak zacząć z MongoDB? i MongoDB + Spring Data.
Dzisiaj postanowiłam zaimplementować wreszcie zapis danych z formularza użytkownika w mojej nierelacyjnej (dokumentowej) bazie danych.
W poprzednim tekście pokazałam, jakich adnotacji należy użyć w klasie User
, żeby jej instancje zostały poprawnie zapisane w bazie danych. Żeby faktycznie zapisać dane z formularza, musiałam tylko dopisać kilka linii w kodzie kontrolera:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
... @Autowired private UserRepository userRepository; ... @RequestMapping(value = "/signup", method = RequestMethod.POST) public String checkPersonInfo(@Valid User user, BindingResult bindingResult, Model model) { if (bindingResult.hasErrors() || !processPasswords(user, bindingResult)) { return "userform"; } model.addAttribute("currentUser", user); userRepository.save(user); return "userpage"; } |
Zależało mi na spełnieniu trzech warunków:
Dodałam następujące pola do klasy reprezentującej użytkownika:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@Document public class User { ... @Transient @NotNull @Size(min=5,max=30) private String password1; @Transient @NotNull @Size(min=5,max=30) private String password2; ... } |
Specjalnie zaznaczyłam adnotację @Transient
. Dzięki jej użyciu wskazane pola (w tym wypadku hasła zapisane jawnym tekstem) nie zostaną zapisane w bazie danych.
Pola musiałam również dopisać do formularza. Użyłam pola input
w wersji password
, żeby zamiast liter wyświetlić gwiazdki (czy tam kropki).
1 2 3 4 5 6 7 8 9 10 |
<tr> <td>Password:*</td> <td><input type="password" th:field="*{password1}" /></td> <td th:if="${#fields.hasErrors('password1')}" th:errors="*{password1}">Password error</td> </tr> <tr> <td>Repeat Password:*</td> <td><input type="password" th:field="*{password2}" /></td> <td th:if="${#fields.hasErrors('password2')}" th:errors="*{password2}">Password error</td> </tr> |
Wreszcie, hasła. Nie powinny być przechowywane w bazie danych w postaci jawnej. Powodów jest wiele. Na przykład taki: ludzie często używają tych samych haseł w wielu miejscach. Gdyby administrator bazy danych miał wgląd w hasła do aplikacji, mógłby podjąć próbę zalogowania się na konta użytkowników w innych serwisach (np. gmail) przy użyciu tego samego hasła i adresu email.
Oprócz pokazanych już dwóch pól do wprowadzenia hasła roboczego, klasa User
przechowuje jeszcze następujące dwie wartości:
1 2 3 4 5 6 7 8 9 10 11 |
@Document public class User { ... private String passwordEncrypted; private String passwordSalt; ... } |
passwordSalt
to losowa wartość doklejana do hasła przed jego “zaszyfrowaniem” (dzięki niej w bazie danych nie będzie widać, że dwa użytkownicy przypadkowo użyli tego samego hasła), passwordEncrypted
to wynik szyfrowania.
Losowanie i szyfrowanie realizuje następujący pomocniczy serwis, wywoływany z kontrolera dzięki adnotacji @Autowired
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@Service public class PasswordsHelper { private SecureRandom generator; private String encryptionAlgorithm = "PBEWithMD5AndDES"; public PasswordsHelper() throws NoSuchAlgorithmException{ generator = new SecureRandom(); } public String getNextPasswordSeed(){ return String.valueOf(generator.nextInt()); } public String encrypt(String password){ SecretKey secretKey = new SecretKeySpec(password.getBytes(), encryptionAlgorithm); return secretKey.getEncoded().toString(); } ... } |
Kontroler wygląda tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
... @Controller public class UserController extends WebMvcConfigurerAdapter { ... @Autowired private PasswordsHelper passwordsHelper; @RequestMapping(value = "/signup", method = RequestMethod.POST) public String checkPersonInfo(@Valid User user, BindingResult bindingResult, Model model) { if (bindingResult.hasErrors() || !processPasswords(user, bindingResult)) { return "userform"; } ... return "userpage"; } private boolean processPasswords(User user, BindingResult bindingResult) { if (!user.getPassword1().equals(user.getPassword2())) { bindingResult.rejectValue("password1", "Passwords don't match", "Passwords don't match"); bindingResult.rejectValue("password2", "Passwords don't match", "Passwords don't match"); return false; } user.setPasswordSalt(passwordsHelper.getNextPasswordSeed()); user.setPasswordEncrypted(passwordsHelper.encrypt(user.getPasswordSalt() + user.getPassword1())); return true; } } |
Podanie niepoprawnych danych w formularzu z nieznanego mi powodu zaczęło przenosić mnie na moją własną stronę błędu (zamiast wyświetlenia komunikatu przy polu formularza, które spowodowało problem).
Co się okazało?
W pewnym momencie zmieniłam sygnaturę metody kontrolera z:
1 |
public String checkPersonInfo(@Valid User user, BindingResult bindingResult) { |
na:
1 |
public String checkPersonInfo(@Valid User user, Model model, BindingResult bindingResult) { |
ponieważ chciałam dopisać informacje do modelu.
Okazuje się, że parametr reprezentujący BindingResult
musi następować bezpośrednio po parametrze, przez który przekazywany jest dotyczący go obiekt (co ma sens, gdy jeden formularz obsługuje wiele obiektów).
Formularz zaczął działać poprawnie po zamianie kolejności parametrów:
1 |
public String checkPersonInfo(@Valid User user, BindingResult bindingResult, Model model) { |
Kolejny błąd zaskoczył mnie, kiedy testowałam sytuację, w której użytkownik wpisze dwa różne hasła. Okazało się, że poczyniłam złe założenia na temat parametrów metody rejectValue, za pomocą której chciałam przekazać do formularza informację o wykryciu problemu. Napisałam:
1 |
bindingResult.rejectValue("password1", "Passwords don't match"); |
zakładając, że drugi parametr to komunikat o błędzie. Tymczasem – to kod błędu. Komunikat można przekazać dopiero w trzecim parametrze (poprawny kod widać na listingu z metodą processPasswords
). Błąd wynikał zatem z braku komunikatu o błędzie, który był wymagany w szablonie.
Kod jest dostępny w moim repozytorium GitHub.
W mojej aplikacji webowej korzystam z biblioteki Actuator, która daje dostęp do masy pożytecznych informacji na temat działania aplikacji (w czasie rzeczywistym).
Wystarczy dodać w pom.xml następujący wpis:
1 2 3 4 |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> |
Po uruchomieniu aplikacji uzyskuję dostęp do wielu stron ze szczegółami działania aplikacji. Przykładowo, pod adresem /health znajdę podstawowe informacje o aktualnym statusie aplikacji:
Wszystko pięknie, tyle tylko, że nie chcę dzielić się takimi danymi z przypadkowymi (lub, co gorsza, nieprzypadkowymi) użytkownikami Internetu.
Pokazywałam już (we wpisie Spring Boot: Bezpieczeństwo 101) jak oznaczyć strony, które mają być wyświetlane tylko użytkownikom o statusie administratora. Problem z Actuatorem polega na tym, że generowane przez tę bibliotekę adresy nie mają wspólnego rdzenia. Mamy np.: /env, /metrics, /trace. Ich wymienianie jest nieco upierdliwe!
Wtem! Wyczytałam (w Spring Boot in Action), że można dodać tym stronom wspólny przedrostek za pomocą wpisu w pliku application.properties:
1 |
management.context-path=/actuator |
A następnie zabezpieczyć dostęp do stron na tej ścieżce:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
... @Configuration @EnableGlobalMethodSecurity(securedEnabled = true) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { ... @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/", "/signup", "/about", "/assets/**","/login").permitAll() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/actuator/**").hasRole("ADMIN") .anyRequest().authenticated() .and() ... } } |
I voilà! Po zalogowaniu się jako administrator widzę:
W poprzednim wpisie pokazałam, jak dodawać dokumenty i kolekcje do nierelacyjnej bazy MongoDB oraz jak połączyć się z tą bazą z kodu javowego.
W tym odcinku pokażę, jak łatwo utrwalić w MongoDB obiekty przetwarzane w aplikacji webowej.
Kolejny raz korzystam ze “startera” Spring Boot, który grupuje w sobie kilka podstawowych bibliotek realizujących spójny cel – w tym wypadku mapowanie, zapis i odczyt danych z MongoDB:
1 2 3 4 |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency |
Do obiektu reprezentującego użytkownika bazy danych dodałam następujące adnotacje:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
... import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.DBRef; import org.springframework.data.mongodb.core.mapping.Document; @Document public class User { @Id private BigInteger id; private String displayedName; private String description; private Calendar birthday; private URI profilePic; ... @DBRef private List<Size> sizes; ... } |
@Document
oznacza, że mamy do czynienia z obiektem zapisywanym w bazie danych. Jest to odpowiednik @Entity w wersji Spring Data dla relacyjnych baz danych.@Id
to automatycznie generowany, obowiązkowy identyfikator dokumentu w ramach kolekcji w MongoDB.@DBRef
oznacza odwołanie do innej kolekcji w bazie. Uwaga! Nie wolno tworzyć odwołań obustronnych, MongoDB brzydko się przez nie zapętli. Zamiast tego należy po jednej stronie jawnie zapisać identyfikator dokumentu.Muszę tylko zasygnalizować, że będę chciała pobierać z bazy obiekty danego typu:
1 2 3 4 5 6 7 8 9 |
package pl.namiekko.controllers; import java.math.BigInteger; import org.springframework.data.repository.CrudRepository; import pl.namiekko.entities.User; public interface UserRepository extends CrudRepository<User, BigInteger>{ } |
W celu przetestowania konfiguracji dodałam odwołanie do kolekcji użytkowników do mojego powitalnego kontrolera GreetingController:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@Controller public class GreetingController { @Autowired private UserRepository userRepository; @RequestMapping("/") public String greeting(@RequestParam(value="name", required=false, defaultValue="World") String name, Model model) { model.addAttribute("name", name); for(User user: userRepository.findAll()){ model.addAttribute("retrievedName", user.getUserName()); break; } return "index"; } } |
Nazwa pierwszego napotkanego użytkownika zostanie dodana do modelu.
W powitalnym szablonie Thymeleaf dopisałam kod, który wyświetli nazwę odnalezionego w bazie danych użytkownika, o ile taki użytkownik istnieje:
1 2 3 |
... <p th:if="${retrievedName != null}" th:text="'Our database user is, ' + ${retrievedName} + '!'" /> ... |
Mogę skorzystać ze sposobu pokazanego w poprzednim wpisie. Mogę też przygotować kod, który będzie wywoływany przy każdym uruchomieniu aplikacji:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@Service public class DatabaseLoader { ... @PostConstruct private void initDatabase() { userRepository.deleteAll(); User user = new User(); user.setEmail("user@example.com"); user.setDisplayedName("First User <3"); user.setUserName("first1"); userRepository.save(user); } } |
Tym razem oparłam się na wartościach domyślnych. Nie wpisałam w application.properties żadnych danych na temat połączenia z bazą danych. Oczywiście musiałam uruchomić mój serwer MongoDB.
Wszystko zadziałało.
Mogę podejrzeć zastosowane wartości właściwości związanych z MongoDB dzięki dołączonej w pom.xml bibliotece Spring Boot Actuator. Pod adresem http://localhost:8080/configprops widzę m.in. takie dane:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
"spring.data.mongodb.CONFIGURATION_PROPERTIES":{ "prefix":"spring.data.mongodb", "properties":{ "database":null, "password":null, "fieldNamingStrategy":null, "gridFsDatabase":null, "port":null, "host":null, "uri":"mongodb://localhost/test", "authenticationDatabase":null, "username":null } } |
Test
to baza danych, którą utworzyłam na potrzeby poprzedniego wpisu.
@Autowired
w kontrolerze w punkcie 4 (komunikat CrudRepository ... is no accessor method!
)Timed out after 10000 ms while waiting for a server that matches AnyServerSelector{} ... caused by {java.net.ConnectException: Connection refused: connect}}
)Najwyższa pora dodać do mojej konkursowej aplikacji jakąś bazę danych. Od początku zakładałam, że z powodów edukacyjnych użyję bazy nierelacyjnej. Zdecydowałam się w końcu na MongoDB. Jest to jedna z popularnych baz nierelacyjnych obsługiwanych przez mojego dostawcę PaaS (Platform as a Service), tj. Pivotal Web Services.
Swoją działalność w tym obszarze opieram na następujących źródłach:
Po instalacji serwera bazodanowego wykonuję następujące kroki:
1 |
mongod --dbpath istniejaca/sciezka |
_id
:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "_id": "12345", "email": "mail@mail", "user_name": "first", "displayed_name": "First User", "description": "aaa", "birthday": "2004-01-16T00:00:00Z", "wardrobes": [{ "name": "main wardrobe" }, { "name": "winter wardrobe" }] } |
1 |
mongoimport --db test --collection users --drop --file git\szafbook\src\main\resources\templates\User.json |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package pl.namiekko; ... public class MongoMain { public static void main(String[] args) { //connect MongoClient mongoClient = new MongoClient(); MongoDatabase db = mongoClient.getDatabase("test"); ... //disconnect mongoClient.close(); } } |
1 2 3 4 5 6 7 8 |
//znajdź wszystkich użytkowników FindIterable<Document> result = db.getCollection("users").find(); //pobierz pierwszy wynik Document d = result.first(); //wypisz System.out.println(d); //Document{{_id=12345, email=mail@mail, user_name=first, ... |
d
pełni tu rolę wzorca, według którego odnajdywany jest rekord z bazy):
1 2 3 |
//aktualizacja pierwszego dokumentu, który pasuje do wzorca d db.getCollection("users").updateOne(d, new Document("$set", new Document("user_name", "second"))); |
1 2 |
//stwórz indeks w celu przyspieszenia wyszukiwania według pola user_name db.getCollection("users").createIndex(new Document("user_name", 1)); |
No i działa 🙂
Cały plik jest dostępny w GitHub.