Personal development experience -- my java learning path (school chapter)

Posted by discombobulator on Thu, 17 Feb 2022 18:19:57 +0100

Personal introduction:

Name: not here

Contact information:

Platform: QQ / wechat / Xiaopo station / gitHub
gitHub: https://github.com/kkzxm
Unified nickname: cool house Xiaoming
head portrait:

Code perception:
Less code means less bugs and less maintenance costs!
On the number of lines of code to determine the salary... I dare not judge, and I dare not imagine such a scene

Explanation of terms in advance:
Module: in this article, module does not refer to the module in maven, but,
An entity class, such as Student entity class + Student persistence layer class + Student business layer + Student control layer class,
Maybe it's wrong to call it this way, but it's just right for this article

Personal history

(the sorting method is not sorted according to the technical content, but is done or learned easily when learning this technology)

jdbc phase

When learning jdbc, the teacher also optimized the overall project structure. It's really unclear how to optimize it in a sentence or two. In short, it uses jdbc metadata and other tools such as jdbc utils.

sql generator

None of the above is important, but in this process, I found that each sql statement is basically fixed, and the front part of the query is
select * from table name where
At that time, I wanted to do something to simplify the development of jdbc (at that time, there was no concept of framework). I called it sql generator
Principle: StringBuffer string is spliced character by character (I didn't know there was xml at that time)
Therefore, in the next two months, because I needed to do this, I reflected what I hadn't learned at that time: get the class name to replace the representative name *, and the mapping of the result set is completely completed by reflection,
Summary: this method is invincible to a single table. As long as you adjust the method of service layer, the others are completely automatic, regardless, but
When making the above sql statement generator, I found that the code of each module is the same, so I made a code generator conveniently:

Next generation code generator

Principle: StringBuffer string splicing

  1. First version:
    (1) Data source: txt file
    (2) File content: (because the source code has been lost, it may have been written like this before)
Class 1 remarks class name 1{
	 Attribute 1 remarks: attribute 1 type attribute 1 identifier;
	 Attribute 2 remarks: attribute 2 type attribute 2 identifier;
	 .....wait...
},
Class 2 remarks class name 2{
	Attribute 1 remarks: attribute 1 type attribute 1 identifier;
	Attribute 2 remarks: attribute 2 type attribute 2 identifier;
	.....wait...
}

(3) Additional notes:

Class comments and attribute comments are used in sql generator. It seems that when you want to query the value of an attribute,
Instead of adding an attribute to this attribute, you can enter it instead:

public class{
	....
	@Comment("Student name")
	private String studentName;
	....
}

@Comment ("student name")
(attributes can be found through comments, and comments can also be found through attributes)

For example:

studentService.selectByAttr("Student name","Cool house Xiaoming");
studentService.selectByAttr("studentName","Cool house Xiaoming");
//After the above two sentences are executed, the returned result is the same data

(4) Summary:
txt can hold too little information. If you want to hold more information, it means that it is more difficult to parse, and it is easier to report errors (for example, find spaces in txt documents)
Later, after learning dom4j, I learned about xml for the first time. I made a quick decision and abandoned the txt parsing method, but only abandoned the txt parsing method, and nothing else has changed (for example, it can only generate entity layer code, that's all)

The source code of the above sql generator and code generator is no longer available, but the idea is basically like this,

(5) Recall: I remember when I was doing the test, I first created the table, and then, through the table structure, handwritten txt document or xml document, I called the code generator entry to generate the entity layer code

At this time, it's not time to really do the project. The climax is below

servlet phase

In the new semester, we changed our teachers,
I learned servlet + jsp by myself,
I sat at the last table in the classroom with two monitors, while watching the teaching video of the small broken station and tapping my own things,
As we all know,
The servlet needs to inherit from the HttpServlet class and override one of the doGet or doPost methods,
And a servlet class can only process one request,
So how can a servlet handle multiple requests?
The answer is: instead of directly inheriting from HttpServlet, write a BaseServlet class, use it to inherit HttpServlet, and add a request each time: active, followed by a reflected operation (active is consistent with the method name). The problem is solved successfully!
But I will not be satisfied with this!

When I didn't teach baservlet in the project

/**
 * @Author: Cool house Xiaoming
 * <p>
 */
public abstract class BaseServlet extends HttpServlet {
	//Through this attribute active, a server can handle multiple requests
    protected String active;
    
    protected HttpServletRequest req;
    protected HttpServletResponse resp;
    
    //Automatically configured entity
    protected Object entity;
    
    //Pictures (used when uploading files)
    protected TuPian tuPian = new TuPian();
    //Administrator ()
    private Manager manager;

	//This is implemented by the subclass. Through this method, the parent class can know who the entity is and other information
    abstract protected void setEntityAndSVC();
    
    //Region doget and doPost
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        try {
            setInfo(req, resp);
            getActive();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        try {
            setInfo(req, resp);
            getActive();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //endregion

    /**
     * Every time the doGet or doPost method is executed,
     * It will be implemented here as soon as possible,
     * Internal method, setting coding method and ObjectSvc, etc
     */
    private void setInfo(HttpServletRequest req, HttpServletResponse resp) throws Exception {
    	//Assign values to req and resp above
        this.req = req;
        this.resp = resp;
        //Set the character code. Every time you write one more method, you have to set the character code once. I'm afraid it's not Shi Lezhi's reincarnation
        setCharacter();
        //Set entity object and service (prepare for reflection assembly)
        setEntityAndSVC();
        //Set up the global administrator (the project was complicated at that time, and this was not needed originally)
        setManager();
        //Here is to consider when uploading files
        if (formIsMultipart()) {
        	//If the file is uploaded, the entity object cannot be assembled through ordinary methods
            fileInput();
        } else {
        	//If the file is not uploaded, various required values can be obtained through ordinary methods
            active = req.getParameter("active");
            //Sets the entity object entity of the current operation
            Map parameterMap = req.getParameterMap();
            /*
            This was previously done through the jar package of BeanUtils,
            But that jar bag No more than one parameter, direct error reporting Then he gave up,
            I tried to imitate its principle and made one
            */
            setObInfo(entity, parameterMap);
        }
    }

    /**
     * Set global administrator
     */
    private void setManager() {
        Object manager = req.getSession().getAttribute("manager");
        if (manager != null) {
            this.manager = (Manager) manager;
        }
    }

    /**
     * Set character encoding
     */
    private void setCharacter() throws UnsupportedEncodingException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=UTF-8");
    }

    /**
     * Check whether the form is submitted in segments
     */
    private boolean formIsMultipart() {
        return ServletFileUpload.isMultipartContent(req);
    }

    /**
     * Get the active of the reflection object and call the corresponding method
     */
    private void getActive() {
        try {
            Method activeMethod = this.getClass().getDeclaredMethod(active);
            activeMethod.invoke(this);
            req = null;
            resp = null;
        } catch (Exception e) {
            System.out.println(active);
            e.printStackTrace();
        }
    }

    //region handles file upload
	/*
	Here, the absolute path was used in the project at that time, so there was no problem on my computer,
	But during the demonstration, someone else's computer was used. After the picture was uploaded successfully, the picture reported 404,
	Also for this reason, the competition did not get the place
	*/
    /**
     * Method of file upload
     */
    private void fileInput() throws Exception {
        FileItemFactory fileItemFactory = new DiskFileItemFactory();
        ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
        List<FileItem> fileItems = servletFileUpload.parseRequest(req);
        Map keyVal = new HashMap();
        for (FileItem fileItem : fileItems) {
            String fieldName = fileItem.getFieldName();
            String val = "";
            if (fileItem.isFormField()) {
                //Normal form
                val = fileItem.getString("UTF-8");
                if (fieldName.equals("active")) active = val;
            } else {
                //File upload
                //original file name
                String fileName = fileItem.getName();
                //Get suffix
                String fileSuffix = getFileSuffix(new File(fileName));
                //Current time string
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmssSS");
                String format = simpleDateFormat.format(new Date());
                //Splice the current time with the suffix (get a new file name)
                String newFileName = format + fileSuffix;
                //Assign the new file name to the name attribute of the picture
                tuPian.setTuPianName(newFileName);
                //Add administrator foreign key to the picture
                tuPian.setManager(manager);
                //Write the picture address and other information into the database
                new TuPianServiceIMP().addTuPian(tuPian);
                //File write to disk
                fileItem.write(new File("/img/" + newFileName));
                //Check the latest picture Id
                int lastTuPianId = lastTuPianId().getTuPianId();
                //Give the id of the last picture to val
                val = String.valueOf(lastTuPianId);
            }
            keyVal.put(fieldName, val);
            setObInfo(entity, keyVal);
        }
    }
    //endregion

    //endregion
    /**
     * View the last picture in the database
     */
    private TuPian lastTuPianId() {
        TuPianService tuPianService = new TuPianServiceIMP();
        TuPian lastTuPian = tuPianService.findLastTuPian(tuPian);
        return lastTuPian;
    }

    /**
     * When the verification code is used
     * Note: in the input box of verification code, the name value is verification < br / >
     * Return true for the same and false for the different;
     *
     * @return Check whether the entered verification code value is the same as the entered one,
     */
    protected boolean getYzmFlag() {
        HttpSession session = req.getSession();
        String yzm_key = String.valueOf(req.getSession().getAttribute("KAPTCHA_SESSION_KEY"));
        session.removeAttribute("KAPTCHA_SESSION_KEY");
        String verification = req.getParameter("verification");
        boolean equals = yzm_key.equals(verification);
        if (!equals) req.setAttribute("verificationError", "Information error!or!Duplicate submission form");
        else req.removeAttribute("verificationError");
        return equals;
    }

    //region forwarding and redirection

    /**
     * General method of forwarding and redirection
     * In this method, the last value represents the selected direction:
     * Optional values: < br / >
     * Forwarding front end < br / >
     * Redirect front end < br / >
     * Forwarding backend < br / >
     * Redirect backend < br / >
     *
     * @param pageName File name / resource name
     * @param val      Selected direction
     */
    protected void zfOrCdx(String val, String pageName) {
        switch (val) {
            case "Forwarding front end":
                transmitFrontPage(pageName);
                break;
            case "Forwarding backend":
                transmitBackPage(pageName);
                break;
            case "Redirect front end":
                redirectFrontPage(pageName);
                break;
            case "Redirect backend":
                redirectBackPage(pageName);
                break;
            default:
                System.out.println(val + "Input error");
                break;
        }
    }

    //region forwarding and redirection - > bottom

    /**
     * Forwarding method
     */
    protected void transmit(String pageName) {
        try {
            System.out.println(pageName);
            req.getRequestDispatcher(pageName).forward(req, resp);
        } catch (ServletException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * redirect
     */
    protected void redirect(String pageName) {
        try {
            resp.sendRedirect(pageName);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //endregion

    //region front end page forwarding and redirection

    /**
     * Forward front page
     */
    protected void transmitFrontPage(String pageName) {
        transmit("pages/store/" + pageName);
    }

    /**
     * Reset front page
     */
    protected void redirectFrontPage(String pageName) {
        try {
            resp.sendRedirect(getFrontPage(pageName));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //endregion

    //region backend page forwarding and redirection

    /**
     * Forward back-end page
     */
    protected void transmitBackPage(String pageName) {
        transmit("pages/admin/" + pageName);
    }

    /**
     * Redirect back-end pages
     */
    protected void redirectBackPage(String pageName) {
        try {
            resp.sendRedirect(getBackPage(pageName));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //endregion

    //endregion

    //region get address
    protected String getPath() {
        return String.valueOf(this.getServletContext().getAttribute("base"));
    }

    /**
     * Get the front-end address
     */
    protected String getFrontPage(String pageName) {
        return String.valueOf(this.getServletContext().getAttribute("front")) + pageName;
    }

    /**
     * Get back-end address
     */
    protected String getBackPage(String pageName) {
        return String.valueOf(this.getServletContext().getAttribute("back")) + pageName;
    }
    //endregion
}

One of the subclasses

package com.sdllb.Servlet.IMP;
/**
 * @Author: Cool house Xiaoming
 * @CreateTime: 2020-12-01 09:45
 */
@WebServlet("/yiLiaoYonPinType")
public class YiLiaoYonPinTypeServletIMP extends BaseServlet implements YiLiaoYonPinTypeServlet {
    private YiLiaoYongPinTypeService yiLiaoYonPinTypeService;
    private YiLiaoYonPinType yiLiaoYonPinType;

    @Override
    protected void setEntityAndSVC() {
        entity = new YiLiaoYonPinType();
        yiLiaoYonPinType = (YiLiaoYonPinType) this.entity;
        yiLiaoYonPinTypeService = new YiLiaoYongPinTypeServiceIMP();
    }

	/**
	add to
	*/
    @Override
    public void addYiLiaoYonPinType() {
        if (yiLiaoYonPinTypeService.addYiLiaoYongPinType(yiLiaoYonPinType)) {
            req.setAttribute("addYiLiaoYongPinTypeResult", true);
        } else {
            req.setAttribute("addYiLiaoYongPinTypeResult", false);
        }
        zfOrCdx("Forwarding backend", "addYiLiaoYonPinType.jsp");
    }

	/**
	Paging query
	*/
    @Override
    public void limitYiLiaoYonPinType() {
        int thisPage = Integer.parseInt(req.getParameter("pageNo"));
        YiLiaoYonPinType_Page yiLiaoYonPinType_page = new YiLiaoYonPinType_Page(thisPage, 5);
        req.setAttribute("yiLiaoYonPinType_page", yiLiaoYonPinType_page);
        zfOrCdx("Forwarding backend", "yiLiaoYonPinType_list.jsp");
    }

	/**
	delete
	*/
    @Override
    public void deleteYiLiaoYonPinType() {
        if (yiLiaoYonPinTypeService.removeYiLiaoYonPinType(yiLiaoYonPinType)) {
            req.setAttribute("deleteYiLiaoYonPinType", "OK");
        } else {
            req.setAttribute("deleteYiLiaoYonPinType", "NO");
        }
        zfOrCdx("Forwarding backend", "yiLiaoYonPinType_list.jsp");
    }

	/**
	modify
	*/
    @Override
    public void updateYiLiaoYonPinType() {
        int id = Integer.parseInt(req.getParameter("Id"));
        if (yiLiaoYonPinTypeService.updateYiLiaoYongPinType(id, yiLiaoYonPinType)) {
            req.getSession().setAttribute("addYiLiaoYongPinTypeResult", true);
        } else {
            req.setAttribute("addYiLiaoYongPinTypeResult", false);
        }
        zfOrCdx("Forwarding backend", "addYiLiaoYonPinType.jsp");
    }
	
	/**
	Query by Id before modification
	*/
    @Override
    public void selectYiLiaoYonPinTypeById() {
        YiLiaoYonPinType yiLiaoYonPinTypeById = yiLiaoYonPinTypeService.findYiLiaoYonPinTypeById(yiLiaoYonPinType);
        req.setAttribute("yiLiaoYonPinType", yiLiaoYonPinTypeById);
        zfOrCdx("Forwarding backend", "addYiLiaoYonPinType.jsp");
    }
}

Don't say: zfOrCdx("forwarding backend", "addYiLiaoYonPinType.jsp");
This method is really cool. It's so cool that it's explosive
(of course, it's also a hidden danger for me. I don't know how to forward and redirect until now)

sql generator crashes in servlet project

Until now, there has been no problem with the project structure at the control level, but the problem does not appear here
Originally, I thought that my sql generator would be able to display its skills and perform well at this moment
As a result, it was just like Zhang Sigui's horse. As soon as it was pulled out, it turned over to me directly, 55555
As I said before, its single watch is invincible,

This project is a medical system (divided into foreground display page and background administrator page, and my BaseServlet is also designed based on this project)

Because the previous sql generator made by itself can only operate single tables, and it is almost powerless in the face of multiple tables;
At that time, there was another solution: a query was divided into two times
For example:
Two entity classes, one is student and the other is school. Student refers to school
The student is queried for the first time, which contains the Id of the school,
Query again through the Id of the school and load it into the student object
But I refused. Although it's not a formal project, I don't like this operation,
In this way, the class structure of student will become as follows:

I feel very uncomfortable in any way

public class Studnet{
	private int id;
	.....//Other properties
	private int schoolId;
	private School school;
}

So I decided to upgrade my sql generator to a tool that can operate multiple tables.
But I finally failed, completely failed!!!
A memory leak occurred while testing the sql generator. Like recursion, the program falls into an endless loop,
In that night's self-study, I tried every means to stop recursion, but it didn't work.
It is intended to be closed in the form of a counter, but the value of the counter attribute will be overwritten every time, which means that there is no counter designed.

In this embarrassing situation, if you push down the sql generator written before,
Rewrite normal to write jdbc code to realize, ah, but the project defense is urgent, and the time is too late.
I don't know where the courage came from, and I don't know what I was thinking at that time.
The result is to pick up the small broken station and open the video of MyBatis:

Remedy after sql generator crash -- > mybatis & & maven

So I studied by myself the next night, opened a small broken station and began to learn MyBatis
It's my honor to finish the knowledge points in one day.
Before MyBatis, I didn't know Maven,
It's just that I did it in the video, and I followed suit,
Until the third day, Maven had a problem,
Only then did I know that I had to match Ali to the mirror image. What is Ali to the mirror image?????
In addition, the multi table operation of MyBatis is also from Baidu (there is no teaching in the video (the quick start video won't teach too much))


Results of self-study on the evening of the third day:

package com.sdllb.service.root;

public class BaseService {
	//sqlSession factory (one in the whole project is enough)
    private static SqlSessionFactory factory;
    private SqlSession sqlSession;

    static {
        //There is no need to explain this paragraph. You can get it to the sqlSession factory
        try {
            String config = "mybatis.xml";
            InputStream in = Resources.getResourceAsStream(config);
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            factory = builder.build(in);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
	
	/*Get sqlsession from sqlsession factory
	(I thought this method would be used sometimes, just in case it was not set to private, but it has not been set)
	*/
    public SqlSession getSqlSession() {
        sqlSession = null;
        if (factory != null) {
            sqlSession = factory.openSession(true);
        }
        return sqlSession;
    }
	
	/*
	Get the interface class of Dao layer
	*/
    public <T> T getDaoInterface(Class<T> daoInterface) {
        SqlSession sqlSession = this.getSqlSession();
        return sqlSession.getMapper(daoInterface);
    }

	/*
	Transaction commit
	*/
    public void commit() {
        sqlSession.commit(true);
    }
    
	/*
	Close sqlSession
	*/
    public void close() {
        sqlSession.close();
    }
}

One of the services

package com.sdllb.service.IMP;

public class YiLiaoYongPinTypeServiceIMP implements YiLiaoYongPinTypeService {
	/*
	For the first time, the BaseService is inherited, but there is still a problem,
	I don't remember the specific problem. It seems to be a session conflict,
	After that, it is changed to this method of attribute introduction
	*/
	//new BaseService object
    private BaseService baseService = new BaseService();
    //Get Dao interface through BaseService object,
    private YiLiaoYongPinTypeDao yiLiaoYongPinTypeDao = baseService.getDaoInterface(YiLiaoYongPinTypeDao.class);

    @Override
    public boolean addYiLiaoYongPinType(YiLiaoYonPinType yiLiaoYongPinType) {
        int i = yiLiaoYongPinTypeDao.addYiLiaoYonPinType(yiLiaoYongPinType);
        
        /*
        In this position, it can be said that this sentence is really redundant,
        If there is no such sentence, it can be returned directly,
        (At that time, there was no concept of AOP, but only after the project was completed)
        */
        baseService.close();
        
        return i > 0;
    }

    @Override
    public boolean updateYiLiaoYongPinType(int id, YiLiaoYonPinType yiLiaoYonPinType) {
        int i = yiLiaoYongPinTypeDao.updateYiLiaoYonPinType(id, yiLiaoYonPinType);
        baseService.close();
        return i > 0;
    }

    @Override
    public List<YiLiaoYonPinType> findAllYiLiaoYongPinType() {
        List<YiLiaoYonPinType> yiLiaoYonPinTypes = yiLiaoYongPinTypeDao.selectAllYiLiaoYongPinType();
        baseService.close();
        return yiLiaoYonPinTypes;
    }

    @Override
    public List<YiLiaoYonPinType> findLimitYiLiaoYonPinType(int thisPage, int pageSize) {
        if (thisPage <= 0) thisPage = 1;
        int start = (thisPage - 1) * pageSize;
        List<YiLiaoYonPinType> yiLiaoYonPinTypes = yiLiaoYongPinTypeDao.limitYiLiaoYonPinTypeSelect(start, pageSize);
        yiLiaoYongPinTypeDao.limitYiLiaoYonPinTypeSelect(start, pageSize);
        baseService.close();
        return yiLiaoYonPinTypes;
    }

    @Override
    public int findYiLiaoYonPinTypeCount() {
        int i = yiLiaoYongPinTypeDao.selectYiLiaoYonPinTypeCount();
        baseService.close();
        return i;
    }

    @Override
    public boolean removeYiLiaoYonPinType(YiLiaoYonPinType yiLiaoYonPinType) {
        int i = yiLiaoYongPinTypeDao.deleteYiLiaoYonPinType(yiLiaoYonPinType);
        baseService.close();
        return i > 0;
    }

    @Override
    public YiLiaoYonPinType findYiLiaoYonPinTypeById(YiLiaoYonPinType yiLiaoYonPinType) {
        YiLiaoYonPinType newYiLiaoYonPinType = yiLiaoYongPinTypeDao.selectYiLiaoYonPinTypeByID(yiLiaoYonPinType.getYiLiaoYonPinTypeId());
        baseService.close();
        return newYiLiaoYonPinType;
    }
}

At the end of the project, sort out the public code,

(
Common static methods, etc. make a separate project and click install with Maven for other projects to share,
At this point, it has become my code warehouse,
I also gave this project a name: lingDream
Just because there is a word "Ling" in my name;
Perhaps, the name always feels so awkward, but with my English level, I really can't think of a better name
)

SSM

When learning SSM, I have nothing to say,
Only after writing one or two Mapper layer methods, because this is the second Mapper layer method I officially wrote. The first time is in the last Servlet project,
At this time, I found that each Mapper class code has a xxinsert () method. Of course, there is no time to think like the last project.

After a try, the prefix xXX is removed, and the insert () method is mentioned in BaseMapper,
In my memory, I may have encountered some problems at that time, and then introduced generics to solve them,

package ling.evidences.dao.root;

import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * Generic T is declared by sub interfaces
 * @Author: Cool house Xiaoming
 * @CreateTime: 2020-12-26 14:24
 */
public interface BaseDao<T> {
    //region query

    /**
     * Query all
     */
    List<T> selectAll();

    /**
     * Query by Id
     */
    T selectById(T t);

    /**
     * How many pieces of data are queried in total
     */
    int selectAllCount();

    /**
     * Paging query
     */
    List<T> selectLimit(@Param("start") int start, @Param("pageSize") int pageSize);

    //endregion

    /**
     * increase
     */
    int insert(T t);

    /**
     * delete
     */
    int delete(T t);

    /**
     * modify
     */
    int update(T oldS,T newS);
}

One of the subclasses

package ling.evidences.dao;

import ling.evidences.dao.root.BaseDao;
import ling.evidences.entity.School;
/**
 * BaseDao One of the sub interfaces of the interface,
 * There are no abstract methods defined in this interface,
 * The only thing to do is declare generics,
 * If you need to add any special methods, such as abc(),
 * We need to consider whether abc() can be shared,
 * If yes, it is directly added to the BaseDao interface,
 * If not, just put it in the interface (add it here, and the later service needs a type conversion)
 * <School>
 * @Author: Cool house Xiaoming
 * @CreateTime: 2020-12-25 10:47
 */
public interface SchoolDao extends BaseDao<School> {
	// If there are no special circumstances, there is no need to write any bit of code here
}

BaseMapper wrote it, and then came to mention BaseService,
(at this time, two mistakes in decision-making made me go around a big circle)

package ling.evidences.service.root;

import java.util.List;

/**
 * @Author: Cool house Xiaoming
 * @CreateTime: 2020-12-26 15:06
 */
public interface BaseService<T> {
    //region query
    /**
     * Query all
     */
    List<T> findAll();

    /**
     * Query by Id
     */
    T findById(T t);

    //endregion

    /**
     * increase
     */
    boolean add(T t);

    /**
     * delete
     */
    boolean remove(T t);

    /**
     * modify
     */
    boolean modify(T oldT,T newT);
}

Extract the abstract methods related to paging into another interface

package ling.evidences.service.root;

import ling.evidences.tool.BasePage;

import java.util.List;

/**
 * Extract the paging model
 * (Paging model (requires a Service)
 * @Author: Cool house Xiaoming
 * @CreateTime: 2020-12-25 15:51
 */
public interface PageService<T> {
    /**
     * How many records are there in total
     */
    int findAllCount();

    /**
     * Paging query
     */
    List<T> findLimit(int start, int pageSize);

    /**
     * Get paging model object
     * default method
     */
     default  BasePage<T> getBasePage() {
        return new BasePage<>(this);
    }
}

Abstract implementation class, I named it Transition, which means bridge

package ling.evidences.service.root;

import ling.evidences.dao.root.BaseDao;

import java.util.List;

public abstract class Transition<T>
        implements PageService<T>, BaseService<T> {

	/**
	 * Whether it's a single table query, a multi table query, or something
	 * So as long as it belongs to the service business class,
	 * Then it must have an object with dao layer,
	 * Because the operation of each step above focuses on generics,
	 * At this point, the fundamental role of generics is fully reflected!
	 */
    protected BaseDao<T> baseDao;

	/**
	 *
	 * The first decision error: select * * setter * * to inject;
	 * The second decision mistake: I know the @ Service and @ AutoWrite annotations, but I didn't use them.
	 * The first mistake caused the Service layer to fail to use @ AutoWrite, which is always Null. I never understood it.
	 * Although the * * constructor * * can also be used to inject, I really didn't want to see it in my mind at that time,
	 * The little constructor added to the subclass service, so the * * constructor * * injection method is abandoned before testing,
	 * Now I want to come, I can't help but regret it
	 * End result: my BeanXml configuration file, unlimited growth
	 *
	 * The following are the original notes:
	 *	 	 	 
	 * Because generics have always been emphasized,
	 * Therefore, even the method of setting the first Dao layer can be extracted directly,
	 * Don't worry about problems (generics don't match, and errors are reported directly at runtime)
	 * (Unless you deliberately don't correspond, the generic type of each node)
	 */
    public void setBaseDao(BaseDao<T> baseDao) {
        this.baseDao = baseDao;
    }

    @Override
    public List<T> findAll() {
        return baseDao.selectAll();
    }

    @Override
    public T findById(T T) {
        return baseDao.selectById(T);
    }

    @Override
    public boolean add(T T) {
        return baseDao.insert(T) > 0;
    }

    @Override
    public boolean remove(T T) {
        return baseDao.delete(T) > 0;
    }

    @Override
    public boolean modify(T oldT, T newT) {
        return baseDao.update(oldT, newT) > 0;
    }

    @Override
    public int findAllCount() {
        return baseDao.selectAllCount();
    }

    @Override
    public List<T> findLimit(int start, int pageSize) {
        return baseDao.selectLimit(start, pageSize);
    }
}

One of the subclasses

package ling.evidences.service.impl;

import ling.evidences.entity.School;
import ling.evidences.service.root.Transition;

/**
 * @Author: Cool house Xiaoming
 * @CreateTime: 2020-12-25 10:47
 */
public final class SchoolServiceImpl extends Transition<School> {
	//There is no need to add code here if there are no special circumstances
	//Of course, multi table queries are special cases (described in the next code block)
}

Final result: available, but the xml configuration file grows infinitely, and for the control layer in spring MVC, the parent class is not successfully extracted

SSM learned almost the same, so he wrote the following article:
Click on the article title:
SSM oriented interface development_ Is this really OK in actual development?

Maybe,
Looking back now, it should be called
Generic development

After writing the SSM article, upgrade the code generator

After the baptism of SSM, I can feel that the previous code generator is no longer applicable because it can only generate Entity layer.

After the last project,
I used shift+delete to delete this damn sql generator, which almost killed me.
But the code generator remains, but it also needs to be upgraded.
It must be impossible to splice with StringBuffer bit by bit as before.
After all, Entity is not the only java file in front of me this time
So I need to find a technology through
Template + data = > result
After hard work,
It really made me find one on the little broken station.
At this stage, I learned that there is a template engine.
Technology used: freemaker + jdbc metadata
Disadvantages: freemaker requires a suffix of ftl
Everything else is fine, but the disadvantage is that the editor didn't give me guidance, which I can understand,
But how many copies does it mean?? More often, a null pointer exception will directly fill the whole file with errors
Well,,,,, fortunately, it can be used. Before it is solved, it can only generate the entity layer. Use it for the time being!
Later, I learned other skills and came back to pick you up!!

My SSM and school Oracle (coming winter vacation)

When I was learning SSM, the school teacher taught Oracle database, but I didn't follow,
Although this is not something to show off,
But my idea was that if the company needed Oracle,
I will be able to get the knowledge of Oracle in a controllable time, and start it immediately
I need Spring more than Oracle,
Closer to the mood at that time, what I need is AOP,
But in SSM study, I gained more than I thought,

vacation

After learning SSM, the school also ushered in a holiday. I planned to use the holiday to do a project, but there were too many trivia and there was no chance until the beginning of school. At this time, the heart of using SSM to develop a project has been exhausted.

First two weeks after leave

The last semester began. The school changed its teachers again. It taught MyBatis. I didn't listen to it after one or two classes.
I think they finished learning Oracle database, and I finished learning SSM during the time they studied oracle.

Although I started school for a week or two, I was not in a hurry to learn Spring Boot, but continued to improve my code generator and my English word project with SSM framework,
After all, I don't have enough time to do this at home during the holiday.

springBoot phase

It's also a coincidence that I entered the SpringBoot stage because some friends in the group chatted,
It said, don't use SSM. It's cool to use Spring Boot for projects,
Then I opened the little broken station and began to learn Spring Boot.
Don't say, it's really more comfortable than SSM.

Personal English words project (first do a little bit with SSM, change SpringBoot, and then pause)

English word project: when there was no administrator related table in the early stage, there were eight tables. At this time, I didn't learn that Maven can divide a project into multiple modules and develop them;
At this time, there is almost no way to face a long string of classes and a pile of page files,
The only way to think of is to change the page html template files (at this time, the thymeleaf template engine that has been used) are separated by folders.

All (eight) sheets{
	Words are related to Chinese{
		Word list:
			(The foundation of the project)
			
		Chinese table:
			(Chinese translation)
			
		Relationship between words and Chinese:
			(Each English word will not correspond to only one Chinese, and a Chinese word will not correspond to only one English word)
	}
	Word type correlation{
		Word type list:
			(noun?adverb?...)
			
		Word type relationship table:
			(Each English word will not correspond to only one type, and a type will not correspond to only one English word)
			
	}
	Word label related{
		Word label list:
			(People always like to label and classify piles of things)
			
		Relationship between words and labels:
			(A word can be labeled differently, and a label does not belong to only one word)
			
		Word label group table:
			(There are too many labels. Divide it into groups, just like a folder)
	}
}

Does this sort the pages into folders solve the problem? Actually not!! The entire project folder becomes deeply nested
For example, you need to find the addition page of the Chinese table,
route:
/admin/page/wordAndChinese/chinese/insertChinese.html

The admin folder refers to the administrator page,
At the same level as page is another folder called adminPublic, which is a page part that is publicly referenced

Personal English word project, that's it, and then

srpingBoot project (I: (school dormitory management system))

In the third week of school, the school teacher told me not to do my ~ ~ "English word project" ~ ~ garbage project. He found me a new project and let me do it.
Seriously, if he asked me in advance what project I was doing and asked me not to do it, I would be really obedient, but this
Well, I admit, I finally took the project on the condition that I don't do any homework or listen to classes.

The school dormitory management system has 15 tables in total

All tables{
	Dormitory table:
		(The foundation of the whole project)
		
	Dormitory zoning table:
		(Boys' first floor, girls' first floor......)
		
	Dormitory status table: 
		(Because of the special circumstances of the school,
		Some things are written here,
		For example: is the shower head in the dormitory broken,
		0 if it's broken, 1 if it's not broken)
		
	Dormitory type table:
		(Boys' dormitory, male teachers' dormitory, girls' dormitory and female teachers' dormitory)
		
	Dormitory health score:
		(The dormitory will score every once in a while)
		
	Dormitory health score title table:
		(Its function is like domain name and Ip The address is the same. Make a title for the score of each issue to remember)
		
	Class table:
		(If there is something wrong with the dormitory, you can quickly find out that the dormitory belongs to a class)
		
	Administrator table:
		(Sign in)
		
	Administrator type table:
		(Permission management: 1: ordinary account, 0: Super administrator account)
		
	Personnel table:
		(All teachers and students are human beings,I've tried before. If you directly build a teacher list, it's not easy to control, or because your ability is still shallow)

	Student form:
		(In the student list,
		The first field is a foreign key, which refers to the personnel table,
		Then, the second key is the class of the student)
		
	Personnel type table:
		(1: Teacher, 0: Student)
		
	Dormitory repair application form:
		(Repair time, repair content and repair remarks)
		
	Repair status table:
		(Whether it has been repaired, whether it has been reported for repair, etc)
}

Continue to study SpringBoot. By chance, I saw on the video screen that using MyBatis Plus can save a lot of spl statements. Such a sentence reminds me of my previous sql generator. Naturally, I found a quick start video of MyBatis Plus.

MyBatisPlus(V: 2.2)

MyBatis Plus also started working without watching for a day or two, or doing school projects while watching.

When using MyBatisPlus, I encountered the first problem:

Process paging
Page number

I'm always used to knocking a pile of data into the database first. Anyway, it's the development environment, and then check it out and display it;
Known:

Mybatis Plus in
	org.apache.ibatis.session.RowBounds Class is used to handle paging
	There are subclasses under it:
		com.baomidou.mybatisplus.plugins.pagination.Pagination; //But we don't use this,
	Then go to its subclass:
		com.baomidou.mybatisplus.plugins.Page<T>; //In fact, I believe that many people will choose to use this one here
	But when I'm really doing pagination.....
	Because I use Servlet Page styles learned during the period:
	Up to 5 pages per page:
	1
	1 2 
	1 2 3 
	1 2 3 4 
	1 2 3 4 5 
	2 3 4 5 6
	3 4 5 6 7
	(When there is a previous page is,Show previous button,And,Next page button exists,The next page button is displayed)
			[1],2,3,4,5 [next page]
	[previous page] 1,[2],3,4,5 
			1,2,[3],4,5

In the previous JSP code, these values were calculated by JSTL's < C: set / >,
But this code is pasted directly from the teacher's document,
The teacher in this tutorial deliberately left a hole, that is, he only considered the situation that the total number of pages is less than or equal to 10:
When the total number of pages exceeds 10, there will be a Bug

	<%--Start of page number output--%>
	 <c:choose> 
	 	<%--Case 1: if the total page number is less than or equal to 5, the range of page numbers is: 1-Total page number--%> 
	 	<c:when test="${ requestScope.page.pageTotal <= 5 }"> 
		 	<c:set var="begin" value="1"/> 
	 		<c:set var="end"value="${requestScope.page.pageTotal}"/>
	 	 </c:when>
	 	 <%--Case 2: the total page number is greater than 5--%>
	 	 <c:when test="${requestScope.page.pageTotal > 5}">
		 <c:choose> 
			<%--Small case 1: the current page number is the first three: 1, 2 and 3. The page range is: 1-5.--%> 
		 	<c:when test="${requestScope.page.pageNo <= 3}"> 
		 		<c:set var="begin" value="1"/> 
				<c:set var="end" value="5"/>
			</c:when>
			<%--Small case 2: the current page number is the last 3, 8, 9 and 10. The page range is: total page number minus 4 - Total page number--%> 
			<c:when test="${requestScope.page.pageNo > requestScope.page.pageTotal-3}">
		  		<c:set var="begin" value="${requestScope.page.pageTotal-4}"/>
		   		<c:set var="end" value="${requestScope.page.pageTotal}"/> 
	   		</c:when>
	   		<%--Minor case 3:4, 5, 6, 7, page range: current page minus 2 - Current page number plus 2--%> 	
	   		<c:otherwise>
				<c:set var="begin" value="${requestScope.page.pageNo-2}"/>
		 		<c:set var="end" value="${requestScope.page.pageNo+2}"/>
		 	</c:otherwise>
		  </c:choose> 
	</c:when>
</c:choose>

<c:forEach begin="${begin}" end="${end}" var="i">
	 <c:if test="${i == requestScope.page.pageNo}"> [${i}] </c:if>
	  <c:if test="${i != requestScope.page.pageNo}">
	   <a href="${ requestScope.page.url }&pageNo=${i}">${i}</a> </c:if>
	    </c:forEach>
	    <%--End of page number output--%>
	     <%-- If it is already the last page, the next page and the last page will not be displayed --%> 
	     <c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}"> 
	    	 <a href="${ requestScope.page.url }&pageNo=${requestScope.page.pageNo+1}">next page</a>
	    	 <a href="${ requestScope.page.url }&pageNo=${requestScope.page.pageTotal}">Last </a>
	    </c:if> 
	    common ${ requestScope.page.pageTotal }Page, ${ requestScope.page.pageTotalCount }Records 
	   
	    To the first<input value="${param.pageNo}" name="pn" id="pn_input"/>page 
	    <input id="searchPageBtn" type="button" value="determine"> 
	    
	    <script type="text/javascript"> $(function () {
		     // Skip to the specified page number $("#searchpagebtn") click(function () {
		     	 var pageNo = $("#pn_input").val();
		     	 <%--var pageTotal = ${requestScope.page.pageTotal};--%> 
		     	 <%--alert(pageTotal);--%> 
		     	 // A location address bar object is provided in the javaScript language 
		     	 // It has an attribute called href It can get the address in the browser address bar 
		     	 // The href attribute is readable and writable
				location.href = "${pageScope.basePath}${
					 requestScope.page.url }&pageNo=" + pageNo; 
					 });
				 }); 
		</script>

But in thymeleaf, although it also has a th:with similar to < C: set / >,
But I tried many times and the results were the same. After setting the value of begin or end for the first time,
I don't remember whether it's an error or whether it's not changed to the final value like jsp. Anyway, it's useless;

Let's go back and analyze. In fact, what I always need are the begin and end attributes, which are used to output page numbers circularly,
Since the thymeleaf of the front end cannot be calculated, let the back end calculate the output

package com.lingDream.root.tool;

import com.baomidou.mybatisplus.plugins.Page;
import lombok.EqualsAndHashCode;

/**
 * In the first version, it imitates the logic in the JSP page above,
 * Directly, but later, the total number of pages was only more than 10,
 * A Bug will appear,
 * 
 * And here's the final revision,
 * Inherited from the page < T > object,
 * And added two properties
 * begin And end
 * 
 * @Author: Cool house Xiaoming
 * @CreateTime: 2021-03-15 13:27
 */
@EqualsAndHashCode(callSuper = true)
public class MyPage<T> extends Page<T> {
    private Long begin;
    private Long end;

    public Long getBegin() {
        //Default
        begin = super.getCurrent()-2L;

        // Last three pages
        if (super.getCurrent()>=super.getPages()-2) begin = super.getPages() -4;

        // First three pages
        if (super.getCurrent()<=3) begin = 1L;

        //The total number of pages in special cases is 4
        if (super.getPages() == 4) begin =1L;

        return begin;
    }


    public Long getEnd() {

        //Default
       end = super.getCurrent()+2L;
        // If it's the first three pages
        if (super.getCurrent()<=3) end = 5L;

        // If it is the last three pages
        if (super.getCurrent() >= super.getPages()-2) end = super.getPages();

        // The total number of special cases is 4
        if (super.getPages() == 4) end = 4L;

        return end;
    }

    public MyPage(int current, int size) {
        super(current, size);
    }
}

In fact, even so, it is still not perfect, because it shows only 5 pages per page,
But what if all I need is six pages per page? Seven?
Is there an algorithm that only needs to pass in, and you need to display several page numbers per page,
Then it can automatically calculate begin and end
Maybe so, but I didn't delve into it

Here, I take the value from the database using MyBatis Plus, and the paging is successfully displayed,

<!--region paging-->
<!--/*@thymesVar id="page" type="com.lingDream.root.tool.MyPage<T>"*/-->
<nav aria-label="Page navigation" class="col-md-offset-4 ">
    <ul class="pagination">
        <li th:if="${page.hasPrevious()}">
            <a th:href="|@{/}${limitPath}=1|">
                <span>home page</span>
            </a>
            <a th:href="|@{/}${limitPath}=${page.current - 1}|" aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
            </a>
        </li>
        <li th:each="i:${#numbers.sequence(page.begin,page.end)}">
            <span th:if="${i==page.current}">[[[${i}]]]</span>
            <span th:if="${i!=page.current}">
                <a th:href="|@{/}${limitPath}=${i}|" th:text="${i}"></a>
            </span>
        </li>
        <li th:if="${page.hasNext()}">
            <a aria-label="Next" th:href="|@{/}${limitPath}=${page.current + 1}|"><span aria-hidden="true">&raquo;</span></a>
            <a th:href="|@{/}${limitPath}=${page.pages}|"><span>Last </span></a>
        </li>
        <li>common[[${page.pages}]]page,[[${page.total}]]Records</li>
        <li class="page_nav_item">To the first<input th:value="${page.current}" name="pn" id="pn_input"/>page
            <input id="searchPageBtn" type="button" value="determine"></li>
    </ul>
</nav>
<script type="text/javascript">
    $(function () {
        // Skip to the specified page number
        $("#searchPageBtn").click(function () {
            let pageNo = $("#pn_input").val();
            location.href = "[[|@{/}${limitPath}|]]=" + pageNo.trim();
        });
    });
</script>
<!--endregion-->
Personalized customized display data

Next, I will display the data in different pages of the dormitory table, but I need to show how many people live in this dormitory.

The first scheme is queried twice (veto!)
	That is, first query the current dormitory, and then according to the current dormitory Id,Then find out the number of people in this dormitory
 The second scheme, modify Mapper.xml File (half success)
	{
		com.hrxy.dormitory.mapper.ManagerMapper.selectPage
	} 
	Original:
		Has been loaded by XML or SqlProvider,ignoring the injection of the SQL.
	Translation:
		Has been XML or SqlProvider Loading, ignored sQL Injection of.
 In this sentence: it is proved that the second scheme is feasible,
 	But the problem is that when paging queries, you need to limit #{start},#{pageSize}

At this point, I stuck for a long time. After consulting Baidu, I couldn't find the answer on the official website.
Then, I looked at the source code of MyBatisPlus (starting from the BaseMapper class, I looked it up in a carpet way),
Unfortunately, I can't fully understand the source code, so I can't find a solution,
But this time I read the source code, I didn't get nothing at all,
I found it in the source code
com. baomidou. mybatisplus. service. An interface of iservice,
And its implementation class:
com.baomidou.mybatisplus.service.impl.ServiceImpl

Unexpected gain: ServiceImpl
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
....
...
}

Next, put the pagination aside and Baidu will provide information about these two categories:

I see. It turns out that MyBatis Plus also does the same thing,
Put the Mapper layer, Service layer and public code into the parent class. Let subclasses Mapper and Service write a small amount or no code

But when I really rewrite my Service code and inherit from ServiceImpl Code:

public class DormitoryService extends ServiceImpl<DormitoryMapper<Dormitory>,Dormitory>{
	/*
	I always feel uncomfortable when I write this sentence down. I don't want to write it like this,
	I hope the effect is:
		public class DormitoryService extends ServiceImpl<Dormitory>{}
			After all, this is more concise
	*/
}

The original plan was to follow the solution of MyPage,
Make ServiceImpl inherit another layer and eliminate generic M
But when I really wrote it, I found that it was impossible! It's impossible

I thought it was just that my version was a little lower. I tried to upgrade MyBatis Plus to 3.0 Version of X,
But still, it hasn't changed,

Finally, we copied the code of ServiceImpl and renamed it MyService,

But at first, I didn't use the constructor as above, but I did the same

setBaseMapper(MyMapper<T> baseMapper){this.baseMapper = baseMapper}

This method, but it has been reporting errors
(
I can't remember clearly. The null pointer exception is reported: baseMapper is null,
It still matches multiple beans, resulting in conflicts, and the project fails to run successfully
)
Fortunately, I didn't give up. I replaced Setter injection with constructor injection. When subclasses inherit, they must implement its constructor.
Run successfully!

At this time, I understand that the so-called @ Autowired is executed in the way of set injection after constructing the method, and I choose to use the constructor and let the subclass define the generic < T >, which avoids this problem.

Recalling the SSM stage, if I chose constructor injection at that time,
Maybe you don't have to go around such a big circle!!!

The parameter limit solves the problem again

The first time I tried, I wrote the limit to death and ran it successfully, which also let me find the law

<select id="selectPage" resultType="Return type">
        select Required columns from Table name
        limit 1,2
        <!--
        Although there is no real explicit method sql Statement, but it can be inferred from the page result:
        MyBatis Plus Automatically in my limit 1,2 after,Again limit. 
        Final statement:
        select Required columns from Table name
        limit 1,2 limit Parameter 1, parameter 2
        -->
</select>

Then, the limit is deleted and the cheating is successfully realized

<select id="selectPage" resultType="Return type">
        select Required columns from Table name
</select>

So far, with this article
Click on the article title:
spring boot generic programming (for spring)

Maven sub module

Maven was also taught in this period because a classmate told me that, of course, it was just a hint,
In the end, it's still Baidu, but Baidu found it. It can't be used directly, but it needs to be changed by itself.

maven module division (personal style, imperfect, will be improved in the future)

Of course, I'm still not satisfied with such module division:

With the increase of modules, this folder must be smelly and long, but at least it is available at this stage, so we don't continue to study it deeply!

So far, the "school dormitory management system" seems to be over, and the source code, war package and document description are also handed over to the teacher.
(
It was not until I did another project that I found that tomcat could run in the local windows system with the war package of spring boot, but in linux, for some reason, the same version just couldn't run, but the project was handed over to the school teachers. After more than a month, they didn't start to deploy
)

Of course, when doing the school dormitory management system,
I won't forget to continue to improve my code warehouse

I also want to go back and continue to write my personal English word project

Continue to write "personal English words" project

When writing personal English words again, the first step is, of course, to divide the project into multiple Maven modules, which is much better and more convenient than before.
Of course, using Maven's features, when a project depends on another project, it will import the jar package it depends on together,
Therefore, the main configuration file only needs to add my warehouse dependency.

Reverse thinking, sharing pages

Pre Description: (according to my personal situation)
In the background management system, almost all controllers have request methods:
1: Paging request
(1) Request to new page
(2) Go to the modification page
(3) Add or modify results page


2: Personal previous situation
(1) The addition and modification of a class module are shared as one page
(2) The result pages of the addition and modification of all class modules are shared as one page


3: Knowledge preparation required
(1) Operation method of introducing page into thymeleaf
(2) Loading data into a Model object
(3) Get class name by reflection

BaseController interface

package com.lingDream.root.controller;

/**
 * @Author: Cool house Xiaoming
 * @CreateTime: 2021-04-21 10:37
 */
public interface BaseController<T> {
    @RequestMapping({"/getPage"})
    String getPage(Model model, String val, String filter, Integer thisPage);

    @RequestMapping(
        value = {"/add"},
        method = {RequestMethod.POST}
    )
    String add(HttpServletRequest request, T entity, Model model);

    @RequestMapping(
        value = {"/delById"},
        method = {RequestMethod.POST}
    )
    @ResponseBody
    String delById(HttpServletRequest request, T entity);

    @RequestMapping({"/updateFindById"})
    String updateFindById(HttpServletRequest request, T entity, Model model);

    @RequestMapping(
        value = {"/update"},
        method = {RequestMethod.POST}
    )
    String update(HttpServletRequest request, T entity, Model model);

    @RequestMapping({"/toInsertPage"})
    String toInsertPage(Model model);
}

Abstract class: MyController: defines construction rules and some common methods,
ControllerPageConfig: defined page path object

package com.lingDream.root.controller;

/**
 * @Author: Cool house Xiaoming
 * @CreateTime: 2021-04-21 10:37
 */
public abstract class MyController<T> implements BaseController<T> {
    @Autowired
    protected ControllerPageConfig controllerPageConfig;
    protected final BaseService<T> service;
    protected final String COMMENT;

    public MyController(BaseService<T> service, String COMMENT) {
        this.service = service;
        this.COMMENT = COMMENT + " →";
    }

    protected MyPage<T> getMyPage(Integer thisPage) {
        return this.service.selectPage(new MyPage(thisPage, 10), (Wrapper)null);
    }

    protected MyPage<T> getMyPage(Integer thisPage, Integer pageSize) {
        return this.service.selectPage(new MyPage(thisPage, pageSize), (Wrapper)null);
    }

    protected MyPage<T> getMyPage(Integer thisPage, Wrapper<T> wrapper) {
        return this.service.selectPage(new MyPage(thisPage, 10), wrapper);
    }
	
	/**
	 *${title} in the page does not need the last "→"
	 * Otherwise it would be awkward to show
	 * <title th:text="${title}"></title>
	 */
    protected void setTitle(Model model) {
        String substring = this.COMMENT.substring(0, this.COMMENT.indexOf(" →"));
        model.addAttribute("title", substring);
    }

    protected void setRequestURL(HttpServletRequest request, Model model) {
        String requestURL = request.getRequestURL().toString();
        String[] split = requestURL.split("/");
        String classRequestMapping = split[split.length - 2];
        model.addAttribute("toPage", classRequestMapping);
    }

    protected Wrapper<T> getWrapper(String val, String filter) {
        Wrapper<T> wrapper = new EntityWrapper();
        wrapper.like(filter, val);
        return wrapper;
    }
	
	/**
	 * The: val and filter used in paging are used as parameters of fuzzy query
	 */
    protected void setPageTitleAndPartAddress(Model model, String val, String filter) {
        this.setTitle(model);
        String partAddress = this.getClass().getSimpleName();
        //Is the address of the page part, and obtains the lowercase class name through reflection
        partAddress = StringUtils.lowFirstChar(partAddress.substring(0, partAddress.length() - 10));
        model.addAttribute("partAddress", partAddress);
        String limitPath = partAddress + "/getPage?";
        if (!Objects.isNull(val)) {
            limitPath = limitPath + "val=" + val + "&";
        }

        if (!Objects.isNull(filter)) {
            limitPath = limitPath + "filter=" + filter + "&";
        }

        limitPath = limitPath + "thisPage";
        model.addAttribute("limitPath", limitPath);
    }
}

Abstract class ControllerImpl: implement BaseController

package com.lingDream.root.controller;

/**
 * @Author: Cool house Xiaoming
 * @CreateTime: 2021-04-21 10:37
 */
public abstract class ControllerImpl<T> extends MyController<T> {
    public ControllerImpl(BaseService<T> service, String COMMENT) {
        super(service, COMMENT);
    }

    public String getPage(Model model, String val, String filter, Integer thisPage) {
        if (thisPage == null) {
            thisPage = 1;
        }

        Wrapper<T> wrapper = this.getWrapper(val, filter);
        MyPage<T> myPage = this.getMyPage(thisPage, wrapper);
        model.addAttribute("page", myPage);
        this.setPageTitleAndPartAddress(model, val, filter);
        return this.controllerPageConfig.getListPage();
    }

    public String toInsertPage(Model model) {
	    /*Judge the length of information stored in the model,
	    Set the path (add / modify),
	    This method should be placed on the first line
	    */
        String path = model.asMap().size() == 0 ? "add" : "update";
        model.addAttribute("path", path);
        
        this.setTitle(model);
        String partAddress = this.getClass().getSimpleName();
        partAddress = partAddress.substring(0, partAddress.length() - 10);
        model.addAttribute("partAddress", StringUtils.lowFirstChar(partAddress));
        return this.controllerPageConfig.getInsertPage();
    }

    public String add(HttpServletRequest request, T entity, Model model) {
        super.setRequestURL(request, model);
        model.addAttribute("result", this.COMMENT + "Added successfully");
        if (!this.service.insert(entity)) {
            model.addAttribute("result", this.COMMENT + "Add failed");
        }

        return this.controllerPageConfig.getResultPage();
    }

    public String delById(HttpServletRequest request, T entity) {
        return this.service.deleteById((Serializable)entity) ? this.COMMENT + "Deleted successfully" : this.COMMENT + "Delete failed";
    }

    public String updateFindById(HttpServletRequest request, T entity, Model model) {
        T t = this.service.selectById((Serializable)entity);
        model.addAttribute("entity", t);
        return this.toInsertPage(model);
    }

    public String update(HttpServletRequest request, T entity, Model model) {
        super.setRequestURL(request, model);
        model.addAttribute("result", this.COMMENT + "Modified successfully");
        if (!this.service.updateById(entity)) {
            model.addAttribute("result", this.COMMENT + "Modification failed");
        }

        return this.controllerPageConfig.getResultPage();
    }
}

Real subclass (I)

package com.lingDream.llfEnglish.controller;

/**
 * @Author: Cool house Xiaoming
 * @CreateTime: 2021-04-21 10:37
 */
@Controller
@RequestMapping(value = "/chinese")
public class ChineseController extends ControllerImpl<Chinese> {
    public ChineseController(MyService<Chinese> service) {
        super(service, "Chinese word formation");
    }
}

Real subclass (2)

package com.lingDream.llfEnglish.controller;

/**
 * @Author: Cool house Xiaoming
 * @CreateTime: 2021-04-21 10:37
 */

@Controller
@RequestMapping(value = "/word")
public class WordController extends ControllerImpl<Word> {

    public WordController(MyService<Word> service) {
        super(service, "English words");
    }

}

Paged query page: list html

<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <th:block th:replace="admin/adminPublic/links.html"/>
    <title th:text="|${title}Administration|"></title>
</head>
<body>
<!--region main container-->
<div class="container-fluid">

    <!--region Top navigation menu-->
    <nav class="navbar navbar-inverse navbar-fixed-top" th:include="admin/adminPublic/adminHeader.html"></nav>
    <!--endregion-->

    <hr>

    <!--region Content area-->

    <div class="row">

        <!--region Left navigation bar-->
        <div class="col-md-2 sidebar sub-menu col-sm-3" th:include="admin/adminPublic/adminLeft.html"></div>
        <!--endregion-->

        <!--region Content display area-->
        <div class="col-md-offset-2 col-md-10 col-sm-9 col-sm-offset-3" style="padding: 20px">

            <!--region Navigator-->
            <ol class="breadcrumb">
                <li><a th:href="@{/toIndexPage}">home page</a></li>
                <li class="active" th:text="|${title}Administration|"></li>
            </ol>
            <!--endregion-->

            <!--region Fuzzy query-->
            <!--<div th:if="${showPart}" class="row"
                 th:include="|/admin/${partAddress}/list/search-wrap.html|"></div>-->
            <!--endregion-->

            <br/>

            <!--region Operation menu-->
            <ul class="breadcrumb">
                <li><a th:href="|@{/}${partAddress}/toInsertPage|" th:text="|newly added ${title}|"></a></li>
            </ul>
            <!--endregion-->

            <!--region form-->
            <!--region partAddress: It was acquired by reflection-->            
            <table th:include="|admin/${partAddress}_list.html|" class="table table-bordered table-striped table-hover table-condensed text-center"></table>
            <!--endregion-->

            <!--region Paging model-->
            <div th:include="|admin/adminPublic/adminLimit.html|">
            </div>
            <!--endregion-->

        </div>
        <!--endregion-->
    </div>

<!--endregion-->

   <!--region Bottom information-->
<div class="row">
    <div></div>
</div>
<!--endregion-->

</div>
<!--endregion-->
</body>
</html>

Added or modified page: inser html

<!DOCTYPE html>
<html lang="zh-CH" xmlns:th="http://www.thymeleaf.org">
<head>
    <th:block th:replace="admin/adminPublic/links.html"/>
    
    <title th:text="|${'add'.equals(path)?'newly added':'modify'}${title}|"></title>
</head>
<body>
<!--region main container-->
<div class="container-fluid">
    <!--region Top navigation menu-->
    <nav class="navbar navbar-inverse navbar-fixed-top" th:include="admin/adminPublic/adminHeader.html"></nav>
    <!--endregion-->

    <hr>
    <!--region Content area-->

    <div class="row">
        <!--region Left navigation bar-->
        <div class="col-md-2 sidebar" th:include="admin/adminPublic/adminLeft.html"></div>
        <!--endregion-->

        <!--region Content display area-->
        <div class="col-md-offset-2 col-md-10" style="padding:50px 100px 0">

            <!--region Navigator-->
            <ol class="breadcrumb">
                <li><a th:href="@{/toIndexPage}">home page</a></li>
                <li><a th:href="|@{/}${partAddress}/getPage?thisPage = 1|" th:text="|${title}Administration|"></a></li>
                <li class="active" th:text="|${'add'.equals(path)?'newly added':'modify'}${title}|"></li>
            </ol>
            <!--endregion-->

            <hr>

            <!--region form -->
            <form th:action="|@{/}${partAddress}/${path}|" method="post" class="form-horizontal" role="form">
                <th:block th:include="|admin/${partAddress}_add.html|"/>
                <div class="form-group">
                    <div class="col-md-offset-1 col-md-2">
                        <input value="Submit" type="submit" class="btn btn-primary form-control">
                    </div>
                    <div class="col-md-2">
                        <input onClick="history.go(-1)" type="button" class="btn btn-info form-control" value="return"/>
                    </div>
                </div>
            </form>
            <!--endregion-->
        </div>
        <!--endregion-->
    </div>

    <!--endregion-->

    <!--region Bottom information-->
    <div class="row">
        bottom
    </div>
</div>
<!--endregion-->
<!--endregion-->
</body>
</html>

Result page: addresult html

<!DOCTYPE html>
<html lang="zh-CN"  xmlns:th="http://www.thymeleaf.org">
<head>
    <links th:replace="admin/adminPublic/links.html"></links>
    <link th:href="@{/css/style.css}" type="text/css" rel="stylesheet">
    <title>result</title>
</head>
<body>
<div class="one">
    <!-- #region Two -->
    <div class="two">
        <!-- #region prompt text -- >
        <div class="tx">
            <p>
                <span th:text="${result}" style="font-size: 40px"></span>
            </p>
        </div>
        <!-- #endregion prompt text -- >
        <div class="wi"></div>
        <!-- #region but -->
        <div class="but_left">
            <button class="button" style="vertical-align:middle"><span><a
                    th:href="|@{/}${toPage}/toInsertPage|">Return to the add page</a> </span></button>
        </div>
        <div class="but_right">
            <button class="button" style="vertical-align:middle"><span><a th:href="|@{/}${toPage}/getPage?thisPage=1|">Return to list page</a> </span>
            </button>
        </div>
    </div>
    <!-- #endregion but-->
</div>
<!-- #endregion Two -->
</body>
</html>

Reduced code: chinese_list

<tr>
    <th>Serial number</th>
    <th>Chinese word formation</th>
    <th>Chinese remarks</th>
    <th>operation</th>
</tr>
<!--/*@thymesVar id="item" type="com.lingDream.llfEnglish.entity.Chinese"*/-->
<tr th:each="item:${page.records}">
    <td th:text="${itemStat.count}"></td>
    <td th:text="${item.chineseInfo}"></td>
    <td th:text="${item.chineseComment}"></td>
    <td>
        <a class="link-update"
           th:href="|@{/}${partAddress}/updateFindById?chineseId=${item.chineseId}|">modify</a>
        <a th:onclick="|delAjax({chineseId:${item.chineseId}})|" href="#"Class =" link del "> delete</a>
    </td>
</tr>

Reduced code chinese_add

<!--region Chinese word formation Id-->
<div class="form-group" th:if="${entity!=null}">
    <div class="col-md-2">
        <label>Chinese word formation Id</label>
    </div>
    <div class="col-md-5">
        <input type="hidden" name="chineseId" class="form-control"
               th:value="${entity?.chineseId}">
        <div th:text="${entity?.chineseId}"></div>
    </div>
</div>
<!--endregion-->

<!--region Chinese word formation information -->
<div class="form-group">
    <div class="col-md-2">
        <label for="chineseInfo">
            <i class="text-danger h3">*</i>Chinese word formation information:
        </label>
    </div>
    <div class="col-md-5">
        <input id="chineseInfo" class="form-control"
               name="chineseInfo"
               th:value="${entity?.chineseInfo}" size="50" type="text">
    </div>
</div>
<!--endregion-->

<!--region Remarks on Chinese word formation-->
<div class="form-group">
    <div class="col-md-2"><label for="chineseComment">Remarks on Chinese word formation:</label></div>
    <div class="col-md-5">
        <textarea name="chineseComment" id="chineseComment" class="form-control" cols="30"
                  rows="10" placeholder="Please enter Chinese words and comments"
                  th:text="${entity?.chineseComment}"></textarea>
    </div>
</div>
<!--endregion-->

word_list,word_add: I don't want to stick

Custom profile

# general act
lingDream:
  controller:
    listPage: admin/index/list
    insertPage: admin/index/insert
    resultPage: admin/adminPublic/addResult
    aspect: true
  service:
    aspect:
      update: true
      insert: true
#General configuration file of SpringBoot

#Activate development environment
#
spring:
  profiles:
    active: dev

#Activate test environment
#spring.profiles.active=test

#Activate production environment
#spring:
#  profiles:
#    active: product


In a conversation with my teacher, I heard that some companies use iframe to split pages, but now I have found a new world.
It can't be said that iframe is bad, but the iframe method is simpler, but later maintenance is not very convenient in my opinion,
But I found that the early project construction is really not an easy thing, but the later expansion is very easy.

github & linux

The study of github and linux may be traced back to before jdbc, but it has always been used with the mentality of trying to play,
So there has been no progress. If you encounter problems, check Baidu

Topics: Java Programmer