Under MVC in Java Web

Keywords: Java SQL Database JDBC

There is a lot of code, skip it and understand that logic is studying code

Introduce

Looking back at the projects in the previous section, the final hierarchy is:

On MVC, we analyze the advantages and disadvantages of MVC design mode, and add Service layer to deal with business logic, but this is not complete. For large projects, the program structure is still not clear enough, Service layer not only deals with business logic, but also with database operations; there are still the following problems:

  • High coupling
  • Poor code reusability
  • The functional responsibilities of the Service layer are not single

DAO

To solve these problems, we need to further decouple the Service layer. How can we do this by increasing the data access layer DAO?

Definition:

DAO (Data Access Object), a data access object, refers to an object that provides CRUD functions on data; responsible for dealing with databases, between the business logic layer and the database;

Simply put:

It is to encapsulate the database operations originally in the Service layer into the DAO layer, so that the Service layer can concentrate on the processing of business logic and reduce the coupling between functions.

Hierarchy diagram after adding DAO:

Composition of DAO Layer

DAO is a design pattern for the data access layer and its complete components include the following:


It looks very complicated, yes, DAO is a complete solution for database operations, and if implemented according to the above structure, it is very standard for large business projects, but daunting for small, fast development projects

Old driver's advice: Over-design will make people unable to see the essence. When learning design mode, it is important to keep in mind the key issues you need to solve, and take the problem with you to see the role and importance of each part

Example

To better understand the nature of DAO, a simplified DAO design is used here.

Three objects in a DAO are required:

  • Data Access Object (Bean)
  • Database Connection Class
  • DAO Implementation Class

The final Service layer needs DAO implementation objects, database connection objects exist to extract duplicate code, and beans already exist in the original system

Now you need two new classes, a DAO and a database connection class, to create them like this

DBTool:

Database connection class, responsible for connecting to the database, executing queries, and closing resources

package com.kkb.test;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class DBTool {

    //Default parameters
    public   String ip = "127.0.0.1";
    public   int port = 3306;
    public   String
            user="root",
            password="admin",
            charset ="utf8",
            dbName="db1";
    private static boolean DriverLoaded=false;

    //Linking databases with default parameters
    public DBTool() throws ClassNotFoundException {
        if(DriverLoaded)return;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            System.out.println("DBTools message:Database Driver Loaded Successfully!");
        } catch (ClassNotFoundException e) {
            System.out.println("DBTools Error:Driver Loading Failed!");
            throw e;
        }
        DriverLoaded=true;
    }

    //Custom parameter initialization
    public DBTool(String ip, int port, String user, String password, String dbName) throws ClassNotFoundException {
        this();
        this.ip = ip;
        this.port = port;
        this.user = user;
        this.password = password;
        this.dbName = dbName;
    }

    //Custom parameter initialization
    public DBTool(String user, String password, String dbName) throws ClassNotFoundException {
        this();
        this.user = user;
        this.password = password;
        this.dbName = dbName;
    }


    //Get a link
    public Connection getConnection() throws SQLException {
        String url = String.format("jdbc:mysql://%s:%s/%s?characterEncoding=%s&user=%s&password=%s&useSSL=false",ip,port,dbName,charset,user,password);
        try {
            return DriverManager.getConnection(url);
        } catch (SQLException e) {
            System.out.println("DBTools Error Database Connection Failure!");
            throw e;
        }
    }

    //Execute Query Statement
    public List<Map<String,Object>> executeQuery(String sql, Object...args) throws SQLException {
        ArrayList<Map<String, Object>> res = new ArrayList<>();

        ResultSet resultSet = null;
        PreparedStatement preparedStatement = null;
        Connection connection = null;
        try {
            connection = getConnection();
            preparedStatement = getPreparedStatement(connection, sql, args);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                resultSet.getMetaData().getColumnCount();
                HashMap<String, Object> map = new HashMap<>();
                for (int i = 1; i <= resultSet.getMetaData().getColumnCount() ; i++) {
                    map.put(resultSet.getMetaData().getColumnName(i),resultSet.getObject(i));
                }
                res.add(map);
            }
        } catch (SQLException e) {
            e.printStackTrace();
            throw e;
        } finally {
            if(resultSet != null)
                resultSet.close();
            if(preparedStatement != null)
                preparedStatement.close();
            if(connection != null)
                connection.close();
        }
        return res;
    }

    //sql parameter preprocessing
    private PreparedStatement getPreparedStatement(Connection connection, String sql, Object[] args) throws SQLException {
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        int count = sql.length() - sql.replace("?", "").length();
        if(count != args.length){
            throw new SQLException("DBTool Error: Number of parameters does not match");
        }
        for (int i = 0; i < args.length; i++) {
            preparedStatement.setObject(i+1,args[i]);
        }
        return preparedStatement;
    }

    //Execute UPDATE statement
    public boolean executeUpdate(String sql,Object...args) throws SQLException {

        try {
            Connection connection = getConnection();
            PreparedStatement preparedStatement = getPreparedStatement(connection, sql, args);
            int i = preparedStatement.executeUpdate();
            if (i>0){return true;}
        } catch (SQLException e) {
            e.printStackTrace();
            throw e;
        }
        return false;
    }
}

UserDao:

Responsible for providing the required CURD methods for the Service Layer, essentially encapsulating the execution of SQL and the resolution of the results

package com.kkb.models;

import com.kkb.test.DBTool;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class UserDao {
    private DBTool tools;

    //Constructor
    public UserDao() throws ClassNotFoundException {
        tools = new DBTool();
    }

    //New Users
    public boolean insertUser(UserBean user) throws SQLException {
        String sql = "insert into user values(null,?,?)";
        return tools.executeUpdate(sql, user.getName(), user.getPwd());

    }

    //delete user
    public boolean deleteUser(UserBean user) throws SQLException {
        String sql = "delete from user where id = ?";
        return tools.executeUpdate(sql, user.getId());

    }

    //Update User
    public boolean updateUser(UserBean user) throws SQLException {
        String sql = "update user set name = ? , pwd = ?  where id = ?";
        return tools.executeUpdate(sql, user.getName(), user.getPwd(), user.getId());
    }

    //Query all users
    public List<UserBean> queryAllUser() throws SQLException {
        ArrayList<UserBean> beans = new ArrayList<>();

        List<Map<String, Object>> maps = tools.executeQuery("select *from user");
        //To List
        for (Map<String, Object> temp : maps) {
            UserBean bean = getUserBean(temp);
            beans.add(bean);
        }
        return beans;
    }

    //map to bean method
    private UserBean getUserBean(Map<String, Object> temp) {
        UserBean bean = new UserBean();
        bean.setId((Integer) temp.get("id"));
        bean.setName((String) temp.get("name"));
        bean.setPwd((String) temp.get("pwd"));
        return bean;
    }

    //Query by ID
    public UserBean queryUserByID(Integer id) throws SQLException {
        List<Map<String, Object>> maps = tools.executeQuery("select *from user where id = ?", id);
        //To List
        for (Map<String, Object> temp : maps) {
            UserBean bean = getUserBean(temp);
            return bean;
        }
        return null;
    }

    //Logon Authentication
    public UserBean checkLogin(UserBean login) throws SQLException {
        List<Map<String, Object>> maps = tools.executeQuery("select *from user where name = ? and pwd = ?", login.getName(), login.getPwd());
        for (Map<String, Object> temp : maps) {
            UserBean bean = getUserBean(temp);
            return bean;
        }
        return null;
    }

    //Query by name
    public UserBean queryUserByName(String name) throws SQLException {
        String sql = "select *from user where name = ?";
        List<Map<String, Object>> maps = tools.executeQuery(sql, name);
        if (!maps.isEmpty()){
            return getUserBean(maps.get(0));
        }
        return null;
    }
}

UserService:

Instead of using JDBC, use UserDao to complete database operations

package com.kkb.srvices;

import com.kkb.exceptions.LoginException;
import com.kkb.models.UserBean;
import com.kkb.models.UserDao;

import java.sql.*;

//User-related business logic
public class UserService {

    private UserDao dao;

    public UserService() throws ClassNotFoundException {
        this.dao = new UserDao();
    }

    //Method used to check login
    public UserBean checkLogin(UserBean reqBean) throws LoginException {
        //Determine whether the parameter is valid
        if(reqBean.getName() == null || reqBean.getPwd() == null ||
            reqBean.getName().equals("") || reqBean.getPwd().equals("")
        ){
            throw new LoginException("User name or password cannot be empty!");
        }else {
            try {
                UserBean bean = dao.checkLogin(reqBean);
                if(bean != null)
                    return bean;
                else
                    throw new LoginException("ERROR Incorrect username or password!");
            } catch (SQLException e) {
                e.printStackTrace();
                throw new LoginException("The database is blown up!");
            }
        }
    }
}

Thus, using DAO (Data Access Object) to decouple the Service layer improves the maintainability of the code, but the complexity of the corresponding program also increases

Emphasize:

Methods in DAO s are not fixed and should be designed to meet specific business needs

MVC+DAO execution process:

What about factories and connections?

Design mode is like a double-edged sword, bringing scalability, maintainability, and other advantages, while the addition of design mode will make the program more complex

What we need to do is adopt the right design pattern in the right project

There are no factories and interfaces used in the above cases, so what should I do?

Analysis:

When the project reaches a later stage, the company makes a lot of money and wants to replace a stronger Oracle database, oracle's sql is slightly different, so you have to rewrite a new DAO implementation, and after that you have to look up all the places where UserDao is used, make all the changes, and you cry for the first time...

You're smart. You won't let this happen to you anymore, so you...... resigned!

Summary:

Since both methods are the same, only the SQL statements are different, extract an interface for them, define a factory class for creating objects, and modify the factory class to replace Dao's implementation class when you change the implementation class in the future

The overall structure is as follows:

If you finally provide a configuration file and let Factory go to the configuration file to get the type of Dao that needs to be created, everything will be perfect!

The MVC+Service+DAO design pattern is finished here

A Servlet handles requests for multiple different functions

Wait a moment. I can vaguely remember what problem in the first article wasn't solved?

The solution to this problem is simple:

First map the requests that need to be processed to the Servlet, and then select the corresponding processing method in the Servlet based on the request path.

Of course, there are different ways to match paths

  1. Determine the path directly in the Servlet and call the method, (compare low)
  2. Find a method that matches the path through reflection (method name is the same, limited)
  3. Reflections + annotations allow the method name to be arbitrary and annotations provide the path (flexible, high-end)

SpringMVC takes the third approach

Want to write your own MVC framework? Come on.

Posted by zTagged on Mon, 16 Dec 2019 20:53:49 -0800