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 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.

 

 

Kamil Klimek

Od 2016 jestem programistą Java. Przez pierwsze 4 lata pracowałem jako Full Stack Java Developer. Później postanowiłem postawić nacisk na Javę, żeby jeszcze lepiej ją poznać.

Subscribe
Powiadom o
guest
0 komentarzy
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x