Omówienie aplikacji domowej
Jak zwykle zaczniemy kolejny tydzień nauki od omówienia aplikacji domowej – zaczynajmy. 😉
Baza danych
Na początku miałeś zadanie stworzyć bazę danych, co możesz zrobić oczywiście taką komendą po uprzednim zalogowaniu się do MySQL
create database management;
Oraz tabelę users:
create table users( id int not null primary key auto_increment, login varchar(50) not null unique, password varchar(50) not null) );
Mając już bazę danych możemy iść dalej.
UserDao
Czas na zaimplementowanie UserDao – czyli takiego interfejsu:
public interface UserDao { void saveUser(User user); void removeUserById(Long userId); void removeUserByLogin(String login); List<User> getAllUsers(); void updateUser(User user); }
Rozpoczynamy oczywiście od inicjalizacji całego połączenia między aplikacją, a bazą danych.
public class UserDaoImpl implements UserDao { private final static UserDao instance = new UserDaoImpl(); private Connection connection; private final String databaseName = "management"; private final String tableName = "users"; private final String user = "root"; private final String password = "admin"; private UserDaoImpl() { init(); } public static UserDao getInstance() { return UserDaoImpl.instance; } private void init() { try { Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection("jdbc:mysql://localhost/"+databaseName+"?useSSL=false", user, password); } catch(Exception e) { e.printStackTrace(); } } }
Zapis użytkownika nie wygląda skomplikowanie – używamy znanego nam już inserta i wrzucamy login i password – nie wrzucamy ID, ponieważ jest ono generowane automatycznie przez bazę danych.
public void saveUser(User user) { PreparedStatement statement; try { String query = "insert into " + tableName + " (login, password) values(?, ?)"; statement = connection.prepareStatement(query); statement.setString(1, user.getLogin()); statement.setString(2, user.getPassword()); statement.execute(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } }
Usera możemy usunąć po ID korzystając z zapytania DELETE wraz z klauzulą WHERE
public void removeUserById(Long userId) { PreparedStatement statement; try { String query = "delete from " + tableName + " where id=?"; statement = connection.prepareStatement(query); statement.setLong(1, userId); statement.execute(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } }
I podobnie po loginie:
public void removeUserByLogin(String login) { PreparedStatement statement; try { String query = "delete from " + tableName + " where login=?"; statement = connection.prepareStatement(query); statement.setString(1, login); statement.execute(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } }
Wyciąganie wszystkich userów polega na parsowaniu rezultatu i tworzenia obiektu Usera – po utworzeniu wrzucamy do listy, którą na koniec zwracamy.
public List<User> getAllUsers() { List<User> users = new LinkedList<User>(); Statement statement = null; try { statement = connection.createStatement(); String query = "select * from " + tableName; ResultSet resultSet = statement.executeQuery(query); while (resultSet.next()) { Long id = resultSet.getLong("id"); String login = resultSet.getString("login"); String password = resultSet.getString("password"); User user = new User(id, login, password); users.add(user); } statement.close(); } catch (SQLException e) { e.printStackTrace(); } return users; }
A edycji usera dokonujemy przez zapytanie UPDATE z klazulą WHERE, która wyszukuje usera po jego ID:
public void updateUser(User user) { PreparedStatement statement; try { String query = "update " + tableName + " set login = ?, password = ? where id=?"; statement = connection.prepareStatement(query); statement.setString(1, user.getLogin()); statement.setString(2, user.getPassword()); statement.setLong(3, user.getId()); statement.execute(); statement.close(); } catch (SQLException e) { e.printStackTrace(); } }
Refaktoryzacja serwisów
No cóż – tutaj nie ma co się zbyt dużo rozpisywać. Usunąłem po prostu wszystkie bloki try catch, a wyjątki który były łapane są teraz wyrzucane z metody wyżej.
package service; import api.UserDao; import api.UserService; import dao.UserDaoImpl; import entity.User; import exception.UserLoginAlreadyExistException; import exception.UserShortLengthLoginException; import exception.UserShortLengthPasswordException; import validator.UserValidator; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class UserServiceImpl implements UserService { private static UserServiceImpl instance = null; private UserDao userDao = UserDaoImpl.getInstance(); private UserValidator userValidator = UserValidator.getInstance(); private UserServiceImpl() { } public static UserServiceImpl getInstance() { if (instance == null) { instance = new UserServiceImpl(); } return instance; } public List<User> getAllUsers() { return userDao.getAllUsers(); } public boolean addUser(User user) throws UserLoginAlreadyExistException, UserShortLengthLoginException, UserShortLengthPasswordException { if (isLoginAlreadyExist(user.getLogin())) { throw new UserLoginAlreadyExistException(); } if (userValidator.isValidate(user)) { userDao.saveUser(user); return true; } return false; } private boolean isLoginAlreadyExist(String login) { User user = getUserByLogin(login); return user != null; } public void removeUserById(Long userId) { userDao.removeUserById(userId); } public User getUserById(Long userId) { List<User> users = getAllUsers(); for (User user : users ) { boolean isFoundUser = user.getId().equals(userId); if (isFoundUser) { return user; } } return null; } public User getUserByLogin(String login) { List<User> users = getAllUsers(); for (User user : users ) { boolean isFoundUser = user.getLogin().equals(login); if (isFoundUser) { return user; } } return null; } public boolean isCorrectLoginAndPassword(String login, String password) { User foundUser = getUserByLogin(login); if (foundUser == null) { return false; } boolean isCorrectLogin = foundUser.getLogin().equals(login); boolean isCorrectPass = foundUser.getPassword().equals(password); return isCorrectLogin && isCorrectPass; } }
I jeszcze ProductService:
package service; import api.ProductDao; import api.ProductService; import dao.ProductDaoImpl; import entity.Product; import exception.ProductCountNegativeException; import exception.ProductNameEmptyException; import exception.ProductPriceNoPositiveException; import exception.ProductWeightNoPositiveException; import validator.ProductValidator; import java.io.IOException; import java.util.List; public class ProductServiceImpl implements ProductService { private static ProductServiceImpl instance = null; private ProductDao productDao = ProductDaoImpl.getInstance(); private ProductValidator productValidator = ProductValidator.getInstance(); private ProductServiceImpl() { } public static ProductServiceImpl getInstance() { if (instance == null) { instance = new ProductServiceImpl(); } return instance; } public List<Product> getAllProducts() throws IOException { return productDao.getAllProducts(); } public Integer getCountProducts() throws IOException { return getAllProducts().size(); } public Product getProductByProductName(String productName) throws IOException { List<Product> products = getAllProducts(); for (Product product : products ) { boolean isFoundProduct = product.getProductName().equals(productName); if (isFoundProduct) { return product; } } return null; } public Product getProductById(Long productId) throws IOException { List<Product> products = getAllProducts(); for (Product product : products ) { boolean isFoundProduct = product.getId().equals(productId); if (isFoundProduct) { return product; } } return null; } public void removeProduct(String productName) throws Exception { productDao.removeProductByName(productName); } public boolean isProductExist(String productName) throws IOException { Product product = null; product = getProductByProductName(productName); return product == null; } public boolean isProductExist(Long productId) throws IOException { Product product = null; product = getProductById(productId); return product == null; } public boolean isProductOnWarehouse(String productName) { try { for(Product product : getAllProducts()) { if (isProductExist(productName) && product.getProductCount() > 0) { return true; } } } catch (IOException e) { e.printStackTrace(); } return false; } public boolean saveProduct(Product product) throws IOException, ProductWeightNoPositiveException, ProductNameEmptyException, ProductCountNegativeException, ProductPriceNoPositiveException { if (productValidator.isValidate(product)) { productDao.saveProduct(product); return true; } return false; } }
UserFacade
Interfejs fasady Usera się zmienił – tym razem będziemy w niej zwracać Stringa zamiast boolean. 😉
public String registerUser(User user) { try { userService.addUser(user); return "Register successfully"; } catch (UserLoginAlreadyExistException e) { e.printStackTrace(); return e.getMessage(); } catch (UserShortLengthLoginException e) { e.printStackTrace(); return e.getMessage(); } catch (UserShortLengthPasswordException e) { e.printStackTrace(); return e.getMessage(); } }
W bloku try catch łapiemy wszystkie możliwe wyjątki i zwracamy ich wiadomości jako Stringa – wiadomości przychodzą np. od walidatorów.
ProductFacade
Czas na stworzenie kolejnej fasady – odpowiedzialnej za operacje na produktach.
public interface ProductFacade { String createProduct(Product product); String removeProduct(String productName); List<Product> getAllProducts(); }
Na początku stworzyłem z niej Singleton, aby rozwiązanie było lepsze. 😉
public class ProductFacadeImpl implements ProductFacade { private final static ProductFacade instance = new ProductFacadeImpl(); private final ProductService productService = ProductServiceImpl.getInstance(); private ProductFacadeImpl() { } public static ProductFacade getInstance() { return ProductFacadeImpl.instance; } }
Podczas tworzenia produktu korzystam z ProductService
public String createProduct(Product product) { try { productService.saveProduct(product); return "Create product " + product; } catch (IOException e) { e.printStackTrace(); return "[IOException] Error while add product to database"; } catch (ProductPriceNoPositiveException e) { e.printStackTrace(); return e.getMessage(); } catch (ProductWeightNoPositiveException e) { e.printStackTrace(); return e.getMessage(); } catch (ProductNameEmptyException e) { e.printStackTrace(); return e.getMessage(); } catch (ProductCountNegativeException e) { e.printStackTrace(); return e.getMessage(); } }
Łapę oczywiście jak w UserFacade wszystkie wyjątki i zwracam je jako Stringa.
public String removeProduct(String productName) { try { productService.removeProduct(productName); return "Removed product"; } catch (Exception e) { return e.getMessage(); } }
Usuwanie produktu również korzysta z serwisu (no jakby inaczej :P) i zwraca wiadomość wyjątku lub, że produkt został usunięty.
Zwrócenie wszystkich produktów nie jest niczym skomplikowanym:
public List<Product> getAllProducts() { try { return productService.getAllProducts(); } catch (IOException e) { e.printStackTrace(); } return Collections.emptyList(); }
Warto zauważyć na zapis Collections.emptyList() – który jest zalecany, gdy chcemy zwrócić pustą liste. Czemu tak? Zwracamy już pewien obiekt, który jest stworzony w pamięci, zamiast tworzyć kolejny pusty obiekt przy użyciu np. new LinkedList<>();
Main
Nasze menu wygląda aktualnie tak:
public static void startMenu() { System.out.println("MANAGEMENT MENU"); System.out.println("1 - Zaloguj się"); System.out.println("2 - Zarejestruj się"); System.out.println("0 - Wyjdź"); } public static void loggedMenu() { System.out.println("MANAGEMENT MENU"); System.out.println("1 - Dodaj nowy produkt"); System.out.println("2 - Usuń produkt"); System.out.println("3 - Wyświetl dostepne produkty"); System.out.println("0 - Wyloguj się"); } public static void productTypeMenu() { System.out.println("1 - Dodaj buty"); System.out.println("2 - Dodaj ubrania"); System.out.println("3 - Inne"); }
Dlatego lekko musimy zmodyfikwoać switcha odpowiedzialnego za opcję po zalogowaniu. 😉
while (loggedOn) { loggedMenu(); read = scanner.nextInt(); switch (read) { case 1: productTypeMenu(); read = scanner.nextInt(); Product product = null; switch (read) { case 1: product = createBootsProduct(); break; case 2: product = createClothProduct(); break; case 3: product = createOtherProduct(); break; } System.out.println(productFacade.createProduct(product)); break; case 2: System.out.println("Dostępne produkty: " + productFacade.getAllProducts()); System.out.println("Podaj nazwę produktu do usunięcia: "); String productName = scanner.next(); System.out.println(productFacade.removeProduct(productName)); break; case 3: System.out.println("Dostępne produkty: " + productFacade.getAllProducts()); break; case 0: loggedOn = false; break; }
Jak widać dodałem case 2 i case 3, które zapewniają usuwanie i wyświetlanie produktów. Korzystają oczywiście z ProductFacade, która została stworzona własnie w tym celu.
Całe rozwiązanie możesz podejrzeć tutaj.