【项目实战】 图书信息管理系统(Maven,mybatis)(第一个自己独立完成的项目)

一、实验目的

题目七 图书信息管理系统

1 功能描述

设计一个图书信息管理系统,使之具有新建图书信息、显示、插入、删除、查询和排序等功能。

2 具体设计要求

图书信息包括:图书编号、书名、作者名、出版单位、出版时间、价格等。

系统以菜单方式工作:

① 图书信息录入(要求图书信息用文件保存)

② 图书信息浏览

③ 插入图书信息

④ 查询(可以有多种查询方式,如按书名查询、按作者名查询、按价格查询等);

⑤ 排序(可以按图书编号排序、按书名排序、按出版时间排序、按价格排序等);

⑥ 修改图书信息

⑦ 删除图书信息

二、项目概况

1.总述

此项目为图书信息管理系统,是一个采用了mysql+mybatis框架+java编写的maven项目

2. 技术栈选择

Mysql,mybatis

3.环境介绍

数据库:mysql8.0

框架:mybatis

项目结构:maven3.0

语言:Java

Jdk版本:jdk11.0.5(Jdk8.0以上)

编写的IDE:IDEA 2020.01

依赖jar包:
在这里插入图片描述

4. 功能概述

该图书信息管理系统实现了便捷的图书信息管理,利用命令行操作的方式让用户的操作更为简洁。

本系统提供Sql和noSql两种运行模式。

Sql模式直接对mysql数据库进行操作,便于数据的持久化和规范化,让数据能够更加便捷高效,同时可以存储大量数据,便于进行大数据的管理,如果你想真正用此系统管理你的信息,建议采用此种模式。

noSql模式是把数据载入内存中,优点是速度快,但缺点也很明显,在面对大量数据的情况下显得有些力不从心,此模式建议在数据量小的情况下使用。

两种模式都支持以下功能:

在这里插入图片描述

5.功能结构

在这里插入图片描述

6.项目文件结构

在这里插入图片描述

三、数据结构描述

1.实体类Book(持久化层)

在这里插入图片描述

2.Sql模式下的数据库结构

在这里插入图片描述

库名:library

表名:books

字段名代表含义数据类型格式
id图书编号INT主键,PK,not null
title书名VARCHAR(20)not null
name作者名VARCHAR(20)not null
publisher出版商VARCHAR(20)
time出版时间DATE
price价格DECIMAL(7,4)

3.Sql模式下的Mapper映射(接口结构)

在这里插入图片描述

4.noSql模式下的数据结构

采用LinkedList<Book>来维护图书信息
在这里插入图片描述

四、程序模块描述

该项目大致分为两个一级模块,分别在两个java文件中
在这里插入图片描述

各个文件内用分别有若干个二级模块

1.图书信息录入(通过文件录入)模块

在这里插入图片描述

2. 图文信息浏览模块

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cjHHTSx8-1592834754185)(media/image13.png)]{width="3.058333333333333in" height="0.25in"}

3. 插入模块

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ijayrnyv-1592834754186)(media/image14.png)]{width="3.3in" height="0.3in"}

4. 查询模块

在这里插入图片描述

其下按查询条件有分为若干个三级模块

5. 排序模块

在这里插入图片描述

其下按排序条件有分为若干个三级模块

6. 更新模块

7. 删除模块

8. 写出模块

至于各模块的功能,见名知意,在此就不一一赘述了。

而对于各个模块的返回值和参数,容我卖个关子,此内容将在下个模块中讲解!

五、主要功能模块的算法流程图

1.Sql模式下的算法流程(以查找为例)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Do8u8qto-1592834754194)(media/image20.png)]{width="5.763194444444444in" height="6.290972222222222in"}

2. 各个模块间的连接算法流程

"套娃"式的连接方式

何为"套娃"?

可以理解为类似递归的连接方式。

与递归有什么不同?

如果采用递归,就要把低层级的模块套在高层级的模块里。这样子虽然连接了各个模块,达到了类似菜单的效果,但有以下两个较为致命的缺点:

  1. 当运行完一个模块后,该模块就运行结束,无法做到循环操作,这与实验要求不符

  2. 由于该项目是一个系统,各模块间要不断退出重进,循环多次的递归会造成堆栈不断压缩,有堆栈溢出风险

    那我们该如何做呢?

    对于第一个缺点,我采用了一个死循环来解决;对于第二个缺点,我的思路就是把改变模块重复调用的时机和位置,让它既能达到效果,又不会导致堆栈溢出

    基本思路:

    每个函数都有一个整数类型的返回值,只要返回1,就说明该级模块需要退回上一级;返回0则说明不需要,即留在当前模块。

    而是留在当前模块还是返回上一级模块,由该模块(记为模块3)的上一级(记为模块2)控制,如果模块3返回一了,就在模块2的上一级(记为模块1)再次调用模块2,即可做到返回上一层;而如果模块3返回0则在模块2再次循环调用,直至模块返回1
    这样做不仅能实现功能,而且能避免多次"套娃"导致堆栈溢出的风险

    在这里插入图片描述

六、代码清单

1.项目结构全览

在这里插入图片描述

2. 实体类Book

ackage com.dreamchaser.domain;

import java.math.BigDecimal;
import java.util.Date;

/**
 * books
 * 
 * @author 金昊霖
 */
public class Book {

    /** 图书编号 */
    private Integer id;

    /** 图书名称 */
    private String title;

    /** 作者姓名 */
    private String name;

    /** 出版社 */
    private String publisher;

    /** 出版时间 */
    private Date time;

    /** 价格 */
    private BigDecimal price;

    public Book() {
    }

    /**
     * 用于清空对象里的数据
     */
    public void clear(){
        this.id=null;
        this.title=null;
        this.name=null;
        this.publisher=null;
        this.time=null;
        this.price=null;
    }

    public Book(Integer id, String title, String name, String publisher, Date time, BigDecimal price) {
        this.id = id;
        this.title = title;
        this.name = name;
        this.publisher = publisher;
        this.time = time;
        this.price = price;
    }

    public Book(BigDecimal price) {
        this.price = price;
    }

    /**
     * 获取图书编号
     * 
     * @return 图书编号
     */
    public Integer getId() {
        return this.id;
    }

    /**
     * 设置图书编号
     * 
     * @param id
     *          图书编号
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * 获取图书名称
     * 
     * @return 图书名称
     */
    public String getTitle() {
        return this.title;
    }

    /**
     * 设置图书名称
     * 
     * @param title
     *          图书名称
     */
    public void setTitle(String title) {
        this.title = title;
    }

    /**
     * 获取作者姓名
     * 
     * @return 作者姓名
     */
    public String getName() {
        return this.name;
    }

    /**
     * 设置作者姓名
     * 
     * @param name
     *          作者姓名
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取出版社
     * 
     * @return 出版社
     */
    public String getPublisher() {
        return this.publisher;
    }

    /**
     * 设置出版社
     * 
     * @param publisher
     *          出版社
     */
    public void setPublisher(String publisher) {
        this.publisher = publisher;
    }

    /**
     * 获取出版时间
     * 
     * @return 出版时间
     */
    public Date getTime() {
        return this.time;
    }

    /**
     * 设置出版时间
     * 
     * @param time
     *          出版时间
     */
    public void setTime(Date time) {
        this.time = time;
    }

    /**
     * 获取价格
     * 
     * @return 价格
     */
    public BigDecimal getPrice() {
        return this.price;
    }

    /**
     * 设置价格
     * 
     * @param price
     *          价格
     */
    public void setPrice(BigDecimal price) {
        this.price = price;
    }

  
}

3.Mapper映射

①接口类 BooKMapper

package com.dreamchaser.mapper;

import com.dreamchaser.domain.Book;

import java.util.List;
import java.util.Map;

public interface BookMapper {
    public List<Book> selectSmaller(Map<String,Object> map);
    public List<Book> selectAll();
    public List<Book> selectBigger(Map<String,Object> map);
    public List<Book> findBookByCondition(Map<String,Object> map);
    public List<Book> findBooksById(int id);
    public void insertBook(Map<String,Object> map);
    public void insertBooks(List<Book> books);
    public void updateBookById(Map<String,Object> map);
    public void updateBooks(List<Map<String,Object>> list);
    public void deleteBookById(int id);
    public void deleteBooksByIds(List<Integer> ids);
    /**
     * @param map 要注意这里的map中只能有一个对象
     * @return
     */
    public List<Book> findBookByConditionO(Map<String,Object> map);
}

②BookMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- books 路径不是/,而是.!!!-->
<mapper namespace="com.dreamchaser.mapper.BookMapper">
    <!-- 字段映射 -->
    <resultMap id="booksMap" type="Book">
        <!--设置主键,提高mybatis性能-->
        <id property="id" column="id"/>
    </resultMap>



    <!-- 表查询字段 -->
    <sql id="allColumns">
        b.id, b.title, b.name, b.publisher, b.time, b.price
    </sql>

    <!-- 查询小于条件的结果 多条会自动打包成list  注:&lt;指的是<,在xml中<是非法的 -->
    <select id="selectSmaller" parameterType="map" resultMap="booksMap">
        SELECT
        <include refid="allColumns"/>
        from books b
        <where>
            <if test="time != null and time!=''">
                AND b.time &lt;=#{time}
            </if>
            <if test="price != null and price !=''">
                AND b.price &lt;=#{price}
            </if>
        </where>
    </select>

    <!-- 查询小于条件的结果   注:&lt;指的是<,在xml中<是非法的 -->
    <select id="selectBigger" parameterType="map" resultMap="booksMap">
        SELECT
        <include refid="allColumns"/>
        from books b
        <where>
            <if test="time != null and time!=''">
                AND b.time >=#{time}
            </if>
            <if test="price != null and price !=''">
                AND b.price >=#{price}
            </if>
        </where>
    </select>
    <!-- 查询所有数据 -->
    <select id="selectAll" resultMap="booksMap">
        SELECT
        <include refid="allColumns" />
        FROM books b
    </select>
    
    <!-- 根据条件参数查询数据列表 -->
    <select id="findBookByCondition" resultMap="booksMap" parameterType="map">
        SELECT
        <include refid="allColumns" />
        FROM books b WHERE 1 = 1
        <if test="title != null and title != ''">
            AND b.title LIKE CONCAT('%', #{title}, '%')
        </if>
        <if test="name != null and name != ''">
            AND b.name LIKE CONCAT('%', #{name}, '%')
        </if>
        <if test="publisher != null and publisher != ''">
            AND b.publisher LIKE CONCAT('%', #{publisher}, '%')
        </if>
        <if test="time != null">
            AND b.time = #{time}
        </if>
        <if test="price != null">
            AND b.price = #{price}
        </if>
    </select>
    
    <!-- 根据主键查询数据 -->
    <select id="findBooksById" resultMap="booksMap" parameterType="int">
        SELECT
        <include refid="allColumns" />
        FROM books b WHERE b.id =#{id}
    </select>
    
    <!-- 插入数据 -->
    <insert id="insertBook" parameterType="map">
        INSERT INTO books (
            id, title, name, publisher, time, price
        ) VALUES (
            #{id},
            #{title},
            #{name},
            #{publisher},
            #{time},
            #{price}
        )
    </insert>
    
    <!-- 批量插入数据 -->
    <insert id="insertBooks" parameterType="list">
        INSERT INTO books (
            id, title, name, publisher, time, price
        ) VALUES
        <foreach collection="list" index="index" item="item" separator=",">
            (
                #{item.id},
                #{item.title},
                #{item.name},
                #{item.publisher},
                #{item.time},
                #{item.price}
            )
        </foreach>
    </insert>
    
    <!-- 修改数据 -->
    <update id="updateBookById" parameterType="map">
        UPDATE books
        <set>
            <if test="title != null">
                title = #{title},
            </if>
            <if test="name != null">
                name = #{name},
            </if>
            <if test="publisher != null">
                publisher = #{publisher},
            </if>
            <if test="time != null">
                time = #{time},
            </if>
            <if test="price != null">
                price = #{price}
            </if>
        </set>
        WHERE id = #{id}
    </update>
    
    <!-- 批量修改数据 -->
    <update id="updateBooks" parameterType="list">
        <foreach collection="list" index="index" item="item" separator=";">
            UPDATE books
            <set>
                <if test="item.title != null">
                    title = #{item.title},
                </if>
                <if test="item.name != null">
                    name = #{item.name},
                </if>
                <if test="item.publisher != null">
                    publisher = #{item.publisher},
                </if>
                <if test="item.time != null">
                    time = #{item.time},
                </if>
                <if test="item.price != null">
                    price = #{item.price}
                </if>
            </set>
            WHERE id = #{item.id}
        </foreach>
    </update>
    
    <!-- 根据主键删除数据 -->
    <delete id="deleteBookById" parameterType="int">
        DELETE FROM books WHERE id = #{id}
    </delete>
    
    <!-- 根据主键批量删除数据 -->
    <delete id="deleteBooksByIds" parameterType="list">
        DELETE FROM books WHERE id IN
        <foreach collection="list" index="index" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>

    <!-- 根据条件排序查询-->
    <select id="findBookByConditionO" resultMap="booksMap" parameterType="map">
        SELECT
        <include refid="allColumns" />
        FROM books b WHERE 1 = 1
        <if test="title != null and title != ''">
            ORDER BY title
        </if>
        <if test="name != null and name != ''">
            ORDER BY name
        </if>
        <if test="time != null">
            ORDER BY time
        </if>
        <if test="price != null">
            ORDER BY price
        </if>
    </select>
</mapper>

4.工具类 {#工具类 .list-paragraph}

①日期工具类 DateUtil

用于Date和字符串之间的转换

package com.dreamchaser.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 日期工具类,用于date和字符串之间的转换
 * @author 金昊霖
 */
public class DateUtil {
    public static String dateToString(Date date,String format){
        return new SimpleDateFormat(format).format(date);
    }
    public static Date stringToDate(String date,String format){
        try {
            return new SimpleDateFormat(date).parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}

②展示工具类 Displayer

用于展示传入的数据

package com.dreamchaser.util;

import com.dreamchaser.domain.Book;
import java.util.List;

/**
 * 显示类,用于展示传入的数据
 *
 * @author 金昊霖
 */
public class Displayer {
//    public static void main(String[] args) {
//        Book book = new Book(123, "狂人日记", "鲁迅", "追梦出版社", new Date("2020/6/18"),
//                new BigDecimal("30.06"));
//        List<Book> list = new LinkedList<>();
//        list.add(book);
//        show(list);
//    }


    public static void show(List<Book> list) {
        System.out.println("--------------------------------------------------------------------------");
        System.out.println("| 图书编号    书名           作者名     出版单位       出版时间            价格  |");
        for (Book book : list) {
            String date="";
            if (book.getTime()!=null){
                date=DateUtil.dateToString(book.getTime(),"yyyy-MM-dd");
            }
            System.out.println("| " + book.getId() + "       " + book.getTitle() + "         " + book.getName() + "     " +
                    book.getPublisher() + "     "+date+ "      " + book.getPrice() + " |");
        }
        System.out.println("--------------------------------------------------------------------------");
    }

    public static void show(Book book) {
        System.out.println("-------------------------------------------------------------------------");
        System.out.println("| 图书编号    书名           作者名     出版单位       出版时间            价格 |");


        String date="";
        if (book.getTime()!=null){
            date=DateUtil.dateToString(book.getTime(),"yyyy-MM-dd");
        }
        System.out.println("| " + book.getId() + "       " + book.getTitle() + " " + book.getName() + "     " +
                book.getPublisher() + "     "+date+ "      " + book.getPrice() + " |");

        System.out.println("-------------------------------------------------------------------------");
    }
}

③文件工具类 FileUtil

package com.dreamchaser.util;

import com.dreamchaser.domain.Book;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 读取文件工具类
 */
public class FileUtil {
    /**
     * 读取txt文件内容
     * @param path 文件路径
     * @return List<String> txt文件内容封装成一个list返回,如果文件不存在就返回null
     */
    public static List<String> readTxtFile(String path){
        File file=new File(path);
        List<String> list=new ArrayList<>();
        if (file.exists()&&file.isFile()){
            BufferedReader reader=null;
            try {
                reader=new BufferedReader(new FileReader(file));
                String line=null;
                while ((line=reader.readLine())!=null){
                    list.add(line);
                }

                return list;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    //最后将输入流关闭
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
        return null;
    }

    public static boolean writeFile(List<Book> list,String path){
        File file=new File(path);
        if (file.exists()&&file.isFile()){
            try {
                BufferedWriter bufferedWriter=new BufferedWriter(new FileWriter(file));
                for (Book book:list){
                    bufferedWriter.write(book.getId()+"    ");
                    bufferedWriter.write(book.getTitle()+"    ");
                    bufferedWriter.write(book.getName()+"    ");
                    bufferedWriter.write(book.getPublisher()+"    ");
                    bufferedWriter.write(book.getTime()+"    ");
                    bufferedWriter.write(book.getPrice()+"    ");
                    bufferedWriter.newLine();
                    bufferedWriter.flush();
                    bufferedWriter.close();
                    return true;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        return false;
    }

④SqlSession工具类

用于返回所需的SqlSession对象

package com.dreamchaser.util;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

/**
 * SqlSession工具类
 * 用于产生sqlSession
 */
public class SqlSessionUtil {
    public SqlSession getSqlSession(){
        // 指定全局配置文件
        String resource = "mybatis-config.xml";
        // 读取配置文件
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 构建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory.openSession();
    }
}

5.模块类 {#模块类 .list-paragraph}

①总驱动类 Driver

用于整个项目的驱动

package com.dreamchaser;


import java.util.Scanner;

/**
 * 驱动类
 * 主要负责统一Sql和NoSql两个模式
 * 为了便捷,功能之间采用“套娃”的方式连接(实质就是避免递归调用造成堆栈溢出)
 * 套娃的基本思路:每个函数都有一个整数类型的返回值,只要返回1,就能返回上一级(实现原理,由该函数(记为函数1)的
 * 上一级(记为函数2)控制,如果函数1返回一了,就在函数2的上一级(记为函数3)再次调用函数2,即可做到返回上一层;
 * 而如果函数1返回0则在函数2再次循环调用,直至函数返回1)
 * 这样做不仅能实现功能,而且能避免多次“套娃”导致堆栈溢出的风险
 * @author 金昊霖
 */
public class Driver {

    static Scanner scanner = new Scanner(System.in);

    public  static void main(String[] args) {

        while (true) {
            if (choosePattern(1) == 1) {
                break;
            }
        }
    }
    /**
     * 一级模块
     * 选择模式模块
     * i 用于递归时判断其是否是第一次来还是输入错误来的
     *
     * @return 用于判断函数状态,0表示继续呆在这层,1表示退回上一层
     */
    private  static int choosePattern(int i) {
        if (i == 1) {
            System.out.println("\n\n\n||||图书信息管理系统||||       \n");
            System.out.println("技术栈选择:mysql+mybatis+java");
            System.out.println("作者:软工1902 金昊霖\n");
            System.out.println("请选择存储模式:");
            System.out.println("1.mysql存储(持久化规范数据存储)");
            System.out.println("2.简单运存存储(如要数据持久化,则需导出文件)");
            System.out.println("3.退出该系统\n\n");
            System.out.println("请输入你的选择(序号):");
        }

        switch (scanner.nextInt()) {
            case 1:
                //这样做既能使其能返回上一级,而且想留在此级时不会造成“无限套娃”,即堆栈不断压缩的情况
                while (true) {
                    if (PatternSql.chooseFunction(1) == 1) {
                        return 0;
                    }
                }
            case 2:
                while (true) {
                    if (PatternNoSql.chooseFunction(1) == 1) {
                        return 0;
                    }
                }
            case 3:
                return 1;
            default:
                System.out.println("抱歉,输入的是非法字符,请重新输入:");
                //这里是采用递归,暂时没办法,如不采用会很麻烦
                return choosePattern(0);
        }
    }
}

②SqlPattern模块类

集成Sql模式

package com.dreamchaser;

import com.dreamchaser.mapper.BookMapper;
import com.dreamchaser.domain.Book;
import com.dreamchaser.util.DateUtil;
import com.dreamchaser.util.Displayer;
import com.dreamchaser.util.FileUtil;
import com.dreamchaser.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

import java.math.BigDecimal;
import java.util.*;

/**
 * Sql模式
 */
public class PatternSql {
    static Scanner scanner = new Scanner(System.in);
    /**
     * mybatis中的sql
     */
    static SqlSession sqlSession = new SqlSessionUtil().getSqlSession();
    /**
     * 获取mybatis为我们创建好的Mapper对象
     */
    static BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
    /**
     * 空map,用于记录数据,每次方法加载时都要清空
     */
    static Map<String, Object> map = new HashMap<>();
    /**
     * 测试xml到底有没有载入
     * 这里就不删了,留作纪念
     * @param args
     */
//    public static void main(String[] args) {
        PatternSql patternSql=new PatternSql();
//        SqlSession sqlSession =new SqlSessionUtil().getSqlSession();
//        List list = sqlSession.selectList("com.dreamchaser.mapper.BookMapper.selectAll");
//        Displayer.show(list);
//    }

    /**
     * 二级模块
     *
     * @param i 用于递归时判断其是否是第一次来还是输入错误来的
     * @return 用于返回上一级, 返回1就表示需要返回上一级,返回0则说明继续执行
     */
    public static int chooseFunction(int i) {
        if (i == 1) {
            System.out.println("\n\n功能:");
            System.out.println("1.图书信息录入");
            System.out.println("2.图书信息浏览");
            System.out.println("3.插入图书信息");
            System.out.println("4.查询");
            System.out.println("5.排序");
            System.out.println("6.修改图书信息");
            System.out.println("7.删除图书信息");
            System.out.println("8.导出为文件");
            System.out.println("9.返回模式选择");
            System.out.println("\n\n请输入你需要选择的功能(序号):");
        }

        while (true) {
            switch (scanner.nextInt()) {
                case 1:
                    saveFile();
                    return 0;
                case 2:
                    selectAll();
                    return 0;
                case 3:
                    addOneBook();
                    return 0;
                case 4:
                    while (true) {
                        if (chooseSelect(1) == 1) {
                            return 0;
                        }
                    }
                case 5:
                    while (true) {
                        if (chooseOrder(1) == 1) {
                            return 0;
                        }
                    }
                case 6:
                    updateBook();
                    return 0;
                case 7:
                    deleteOne();
                    return 0;
                case 8:
                    writeToFile();
                    return 0;
                case 9:
                    return 1;
                default:
                    System.out.println("抱歉,输入的是非法字符,请重新输入:");
                    return chooseFunction(0);
            }
        }
    }

    private static void deleteOne() {
        System.out.println("请输入你要删除书籍的图书编号:");
        bookMapper.deleteBookById(scanner.nextInt());
        System.out.println("删除成功!");
    }

    private static void updateBook() {
        map.clear();
        System.out.println("请输入你要更新的书籍图书编号:");
        map.put("id", scanner.nextInt());
        scanner.nextLine();
        System.out.println("是否要修改其图书名称(若是,请输入其名称,若否,则输入“否”):");
        String title = scanner.nextLine();
        if (!title.equals("否")) {
            map.put("title", title);
        }
        System.out.println("是否要修改其作者姓名(若是,请输入其姓名,若否,则输入“否”):");
        String name = scanner.nextLine();
        if (!name.equals("否")) {
            map.put("name", name);
        }
        System.out.println("是否要修改其出版社(若是,请输入其出版社名称,若否,则输入“否”):");
        String publisher = scanner.nextLine();
        if (!publisher.equals("否")) {
            map.put("publisher", publisher);
        }
        System.out.println("是否要修改其出版时间(若是,请输入其时间,请以“2020/10/06”的形式输入时间,若否,则输入“否”):");
        String time = scanner.nextLine();
        if (!time.equals("否")) {
            map.put("time", new Date(time));
        }
        System.out.println("是否要修改其价格(若是,请输入其价格,若否,则输入“否”):");
        String price = scanner.nextLine();
        if (!price.equals("否")) {
            map.put("price", new BigDecimal(price));
        }
        bookMapper.updateBookById(map);
        System.out.println("更新成功!");
    }
    private static void writeToFile() {
        System.out.println("请输入你要保存的文件名(如:dreamchaser.txt):");
        scanner.nextLine();
        Boolean flag=FileUtil.writeFile(bookMapper.selectAll(),scanner.nextLine());
        if (flag){
            System.out.println("保存成功!");
        }else {
            System.out.println("保存失败,请确认输入的文件路径是否正确!");
        }

    }

    private static void saveFile() {
        System.out.println("请输入要录入的文件(txt,且需每一行都是一条记录)路径(绝对路径或者相对路径皆可):");
        //把回车吃掉
        scanner.nextLine();
        List<String> list = FileUtil.readTxtFile(scanner.nextLine());
        String[] strings=null;
        if (list!=null){
            for (String s:list){
                strings=s.split(" |\n");
                map.clear();
                map.put("id",strings[0]);
                map.put("title",strings[1]);
                map.put("name",strings[2]);
                map.put("publisher",strings[3]);
                map.put("time", DateUtil.stringToDate(strings[4],"yyyy/MM/dd"));
                map.put("price",new BigDecimal(strings[5]));
                bookMapper.insertBook(map);
            }
            System.out.println("录入成功");
        }else {
            System.out.println("文件未找到或者文件不符合要求。请重新输入!");
        }

    }

    private static void selectAll() {
        List<Book> books = bookMapper.selectAll();
        Displayer.show(books);
    }

    private static void addOneBook() {
        map.clear();
        System.out.println("请输入你要插入书籍的图书编号:");
        map.put("id", scanner.nextInt());
        scanner.nextLine();
        System.out.println("请输入你要插入书籍的图书名称:");
        String title = scanner.nextLine();
        map.put("title", title);
        System.out.println("请输入你要插入书籍的作者姓名:");
        String name = scanner.nextLine();
        map.put("name", name);
        System.out.println("请输入你要插入书籍的出版社(若是,请输入其出版社名称,若否,则输入“否”):");
        String publisher = scanner.nextLine();
        if (!publisher.equals("否")) {
            map.put("publisher", publisher);
        }
        System.out.println("请输入你要插入书籍的出版时间(若是,请输入其时间,请以“2020/10/06”的形式输入时间,若否,则输入“否”):");
        String time = scanner.nextLine();
        if (!time.equals("否")) {
            map.put("time", new Date(time));
        }
        System.out.println("请输入你要插入书籍的价格(若是,请输入其价格,若否,则输入“否”):");
        String price = scanner.nextLine();
        if (!price.equals("否")) {
            map.put("price", new BigDecimal(price));
        }
        bookMapper.insertBook(map);
        System.out.println("插入成功");
    }

    /**
     * 排序模块
     * 三级模块
     *
     * @param i
     * @return
     */
    private static int chooseOrder(int i) {
        if (i == 1) {
            System.out.println("\n\n排序:");
            System.out.println("1.按图书编号排序");
            System.out.println("2.按书名排序");
            System.out.println("3.按出版时间排序");
            System.out.println("4.按价格排序等");
            System.out.println("5.返回功能选择");
            System.out.println("\n\n请输入你需要的排序方式(序号):");
        }

        while (true) {
            switch (scanner.nextInt()) {
                case 1:
                    selectOrderById();
                    return 0;
                case 2:
                    selectOrderByTitle();
                    return 0;
                case 3:
                    selectOrderByTime();
                    return 0;
                case 4:
                    selectOrderByPrice();
                    return 0;
                case 5:
                    return 1;
                default:
                    System.out.println("抱歉,输入的是非法字符,请重新输入:");
                    return chooseFunction(0);
            }
        }
    }

    private static void selectOrderByPrice() {
        map.clear();
        map.put("price", "1");
        List<Book> books = bookMapper.findBookByConditionO(map);
        Displayer.show(books);
    }

    private static void selectOrderByTime() {
        map.clear();
        map.put("time", "1");
        List<Book> books = bookMapper.findBookByConditionO(map);
        Displayer.show(books);
    }

    private static void selectOrderByTitle() {
        map.clear();
        map.put("title", "1");
        List<Book> books = bookMapper.findBookByConditionO(map);
        Displayer.show(books);
    }

    private static void selectOrderById() {
        map.clear();
        map.put("id", "1");
        List<Book> books = bookMapper.findBookByConditionO(map);
        Displayer.show(books);
    }

    /**
     * 查询模块
     * 三级模块
     *
     * @param i
     * @return
     */
    private static int chooseSelect(int i) {
        if (i == 1) {
            System.out.println("\n\n查询:");
            System.out.println("1.按书名查询");
            System.out.println("2.按作者名查询");
            System.out.println("3.按价格查询(小于)");
            System.out.println("4.按价格查询(等于)");
            System.out.println("5.按价格查询(大于)");
            System.out.println("6.返回模式选择");
            System.out.println("\n\n请输入你需要的查询方式(序号):");
        }

        while (true) {
            switch (scanner.nextInt()) {
                case 1:
                    selectByTitle();
                    return 0;
                case 2:
                    selectByName();
                    return 0;
                case 3:
                    selectByPriceS();
                    return 0;
                case 4:
                    selectByPrice();
                    return 0;
                case 5:
                    selectByPriceB();
                    return 0;
                case 6:
                    return 1;
                default:
                    System.out.println("抱歉,输入的是非法字符,请重新输入:");
                    return chooseFunction(0);
            }
        }
    }

    private static void selectByPriceB() {
        System.out.println("请输入你要查询的价格:");
        map.clear();
        map.put("price", scanner.nextInt());
        List<Book> books = bookMapper.selectBigger(map);
        Displayer.show(books);
    }

    private static void selectByPrice() {
        System.out.println("请输入你要查询的价格:");
        map.clear();
        map.put("price", scanner.nextInt());
        List<Book> books = bookMapper.findBookByCondition(map);
        Displayer.show(books);
    }

    private static void selectByPriceS() {
        System.out.println("请输入你要查询的价格:");
        map.clear();
        map.put("price", new BigDecimal(scanner.nextInt()));
        List<Book> books = bookMapper.selectSmaller(map);
        Displayer.show(books);
    }

    private static void selectByName() {
        System.out.println("请输入你要查询的作者姓名:");
        map.clear();
        //因为后面是nextLine,而之前是nextInt,会算上回车键,所以得先用nextLine吃掉回车
        scanner.nextLine();
        map.put("name", scanner.nextLine());
        List<Book> books = bookMapper.findBookByCondition(map);
        Displayer.show(books);
    }

    private static void selectByTitle() {
        System.out.println("请输入你要查询的书籍名称:");
        map.clear();
        scanner.nextLine();
        map.put("title", scanner.nextLine());
        List<Book> books = bookMapper.findBookByCondition(map);
        Displayer.show(books);
    }

}

③noSql模块类

集成noSql模式

package com.dreamchaser;

import com.dreamchaser.domain.Book;
import com.dreamchaser.util.DateUtil;
import com.dreamchaser.util.Displayer;
import com.dreamchaser.util.FileUtil;

import java.math.BigDecimal;
import java.util.*;

public class PatternNoSql {
    static Scanner scanner = new Scanner(System.in);
    /**
     * 维护的链表
     */
    static List<Book> list = new LinkedList<>();
    static Book book = new Book();



    /**
     * 二级模块
     *
     * @param i 用于递归时判断其是否是第一次来还是输入错误来的
     * @return 用于返回上一级, 返回1就表示需要返回上一级,返回0则说明继续执行
     */
    public static int chooseFunction(int i) {
        if (i == 1) {
            System.out.println("\n\n功能:");
            System.out.println("1.图书信息录入");
            System.out.println("2.图书信息浏览");
            System.out.println("3.插入图书信息");
            System.out.println("4.查询");
            System.out.println("5.排序");
            System.out.println("6.修改图书信息");
            System.out.println("7.删除图书信息");
            System.out.println("8.导出为文件");
            System.out.println("9.返回模式选择");
            System.out.println("\n\n请输入你需要选择的功能(序号):");
        }

        while (true) {
            switch (scanner.nextInt()) {
                case 1:
                    saveFile();
                    return 0;
                case 2:
                    selectAll();
                    return 0;
                case 3:
                    addOneBook();
                    return 0;
                case 4:
                    while (true) {
                        if (chooseSelect(1) == 1) {
                            return 0;
                        }
                    }
                case 5:
                    while (true) {
                        if (chooseOrder(1) == 1) {
                            return 0;
                        }
                    }
                case 6:
                    updateBook();
                    return 0;
                case 7:
                    deleteOne();
                    return 0;
                case 8:
                    writeToFile();
                    return 0;
                case 9:
                    return 1;
                default:
                    System.out.println("抱歉,输入的是非法字符,请重新输入:");
                    return chooseFunction(0);
            }
        }
    }

    private static void writeToFile() {
        System.out.println("请输入你要保存的文件名(如:dreamchaser.txt):");
        scanner.nextLine();
        Boolean flag=FileUtil.writeFile(list,scanner.nextLine());
        if (flag){
            System.out.println("保存成功!");
        }else {
            System.out.println("保存失败,请确认输入的文件路径是否正确!");
        }
    }

    private static void deleteOne() {
        System.out.println("请输入你要删除书籍的图书编号:");
        int id = scanner.nextInt();
        boolean flag = list.removeIf(a -> a.getId() == id);
        if (flag) {
            System.out.println("删除成功!");
        } else {
            System.out.println("未找到相应的图书!");
        }
    }

    private static void updateBook() {
        book.clear();
        System.out.println("请输入你要更新的书籍图书编号:");
        book.setId(scanner.nextInt());
        scanner.nextLine();
        System.out.println("是否要修改其图书名称(若是,请输入其名称,若否,则输入“否”):");
        String title = scanner.nextLine();
        if (!title.equals("否")) {
            book.setTitle(title);
        }
        System.out.println("是否要修改其作者姓名(若是,请输入其姓名,若否,则输入“否”):");
        String name = scanner.nextLine();
        if (!name.equals("否")) {
            book.setName(name);
        }
        System.out.println("是否要修改其出版社(若是,请输入其出版社名称,若否,则输入“否”):");
        String publisher = scanner.nextLine();
        if (!publisher.equals("否")) {
            book.setPublisher(publisher);
        }
        System.out.println("是否要修改其出版时间(若是,请输入其时间,请以“2020/10/06”的形式输入时间,若否,则输入“否”):");
        String time = scanner.nextLine();
        if (!time.equals("否")) {
            book.setTime(new Date(time));
        }
        System.out.println("是否要修改其价格(若是,请输入其价格,若否,则输入“否”):");
        String price = scanner.nextLine();
        if (!price.equals("否")) {
            book.setPrice(new BigDecimal(price));
        }
        for (Book book1 : list) {
            if (book1.getId().equals(book.getId())) {
                list.remove(book1);
                list.add(book);
            }
        }
        System.out.println("更新成功!");
    }

    private static void saveFile() {
        System.out.println("请输入要录入的文件(txt,且需每一行都是一条记录)路径(绝对路径或者相对路径皆可):");
        //把回车吃掉
        scanner.nextLine();
        List<String> list1 = FileUtil.readTxtFile(scanner.nextLine());
        String[] strings = null;
        if (list1 != null) {
            for (String s : list1) {
                strings = s.split(" |\n");
                book.clear();
                book.setId(Integer.parseInt(strings[0]));
                book.setTitle(strings[1]);
                book.setName(strings[2]);
                book.setPublisher(strings[3]);
                book.setTime(DateUtil.stringToDate(strings[4], "yyyy/MM/dd"));
                book.setPrice(new BigDecimal(strings[5]));
                list.add(book);
            }
            System.out.println("录入成功");
        } else {
            System.out.println("文件未找到或者文件不符合要求。请重新输入!");
        }

    }

    private static void selectAll() {
        Displayer.show(list);
    }

    private static void addOneBook() {
        book.clear();
        System.out.println("请输入你要插入书籍的图书编号:");
        book.setId(scanner.nextInt());
        scanner.nextLine();
        System.out.println("请输入你要插入书籍的图书名称:");
        String title = scanner.nextLine();
        book.setTitle(title);
        System.out.println("请输入你要插入书籍的作者姓名:");
        String name = scanner.nextLine();
        book.setName(name);
        System.out.println("请输入你要插入书籍的出版社(若是,请输入其出版社名称,若否,则输入“否”):");
        String publisher = scanner.nextLine();
        if (!publisher.equals("否")) {
            book.setPublisher(publisher);
        }
        System.out.println("请输入你要插入书籍的出版时间(若是,请输入其时间,请以“2020/10/06”的形式输入时间,若否,则输入“否”):");
        String time = scanner.nextLine();
        if (!time.equals("否")) {
            book.setTime(new Date(time));
        }
        System.out.println("请输入你要插入书籍的价格(若是,请输入其价格,若否,则输入“否”):");
        String price = scanner.nextLine();
        if (!price.equals("否")) {
            book.setPrice(new BigDecimal(price));
        }
        list.add(book);
        System.out.println("插入成功");
    }

    /**
     * 排序模块
     * 三级模块
     *
     * @param i
     * @return
     */
    private static int chooseOrder(int i) {
        if (i == 1) {
            System.out.println("\n\n排序:");
            System.out.println("1.按图书编号排序");
            System.out.println("2.按书名排序");
            System.out.println("3.按出版时间排序");
            System.out.println("4.按价格排序等");
            System.out.println("5.返回功能选择");
            System.out.println("\n\n请输入你需要的排序方式(序号):");
        }

        while (true) {
            switch (scanner.nextInt()) {
                case 1:
                    selectOrderById();
                    return 0;
                case 2:
                    selectOrderByTitle();
                    return 0;
                case 3:
                    selectOrderByTime();
                    return 0;
                case 4:
                    selectOrderByPrice();
                    return 0;
                case 5:
                    return 1;
                default:
                    System.out.println("抱歉,输入的是非法字符,请重新输入:");
                    return chooseFunction(0);
            }
        }
    }

    private static void selectOrderByPrice() {
        /**
         * 把 x -> System.out.println(x) 简化为 System.out::println 的过程称之为 eta-conversion
         * 把 System.out::println 简化为 x -> System.out.println(x) 的过程称之为 eta-expansion
         * 范式:
         * 类名::方法名
         * 方法调用
         *
         * person -> person.getAge();
         * 可以替换成
         * Person::getAge
         *
         * x -> System.out.println(x)
         * 可以替换成
         * System.out::println
         * out是一个PrintStream类的对象,println是该类的方法,依据x的类型来重载方法
         * 创建对象
         *
         * () -> new ArrayList<>();
         * 可以替换为
         * ArrayList::new
         */
        list.sort(Comparator.comparing(Book::getPrice));
        Displayer.show(list);
    }

    private static void selectOrderByTime() {
        list.sort(Comparator.comparing(Book::getTime));
        Displayer.show(list);
    }

    private static void selectOrderByTitle() {
        list.sort(Comparator.comparing(Book::getTitle));
        Displayer.show(list);
    }

    private static void selectOrderById() {
        list.sort(Comparator.comparing(Book::getId));
        Displayer.show(list);
    }

    /**
     * 查询模块
     * 三级模块
     *
     * @param i
     * @return
     */
    private static int chooseSelect(int i) {
        if (i == 1) {
            System.out.println("\n\n查询:");
            System.out.println("1.按书名查询");
            System.out.println("2.按作者名查询");
            System.out.println("3.按价格查询(小于)");
            System.out.println("4.按价格查询(等于)");
            System.out.println("5.按价格查询(大于)");
            System.out.println("6.返回模式选择");
            System.out.println("\n\n请输入你需要的查询方式(序号):");
        }

        while (true) {
            switch (scanner.nextInt()) {
                case 1:
                    selectByTitle();
                    return 0;
                case 2:
                    selectByName();
                    return 0;
                case 3:
                    selectByPriceS();
                    return 0;
                case 4:
                    selectByPrice();
                    return 0;
                case 5:
                    selectByPriceB();
                    return 0;
                case 6:
                    return 1;
                default:
                    System.out.println("抱歉,输入的是非法字符,请重新输入:");
                    return chooseFunction(0);
            }
        }
    }

    private static void selectByPriceB() {
        System.out.println("请输入你要查询的价格:");
        List<Book> result = new LinkedList<>();
        for (Book book1 : list) {
            if (book1.getPrice().compareTo(new BigDecimal(scanner.nextInt())) == 1) {
                result.add(book1);
            }
        }
        Displayer.show(result);
    }

    private static void selectByPrice() {
        System.out.println("请输入你要查询的价格:");
        List<Book> result = new LinkedList<>();
        for (Book book1 : list) {
            if (book1.getPrice().compareTo(new BigDecimal(scanner.nextInt())) == 0) {
                result.add(book1);
            }
        }
        Displayer.show(result);
    }

    private static void selectByPriceS() {
        System.out.println("请输入你要查询的价格:");
        List<Book> result = new LinkedList<>();
        for (Book book1 : list) {
            if (book1.getPrice().compareTo(new BigDecimal(scanner.nextInt())) == -1) {
                result.add(book1);
            }
        }
        Displayer.show(result);
    }

    private static void selectByName() {
        System.out.println("请输入你要查询的作者姓名:");
        //因为后面是nextLine,而之前是nextInt,会算上回车键,所以得先用nextLine吃掉回车
        scanner.nextLine();
        List<Book> result = new LinkedList<>();
        for (Book book1 : list) {
            if (book1.getName().equals(scanner.nextLine())) {
                result.add(book1);
            }
        }
        Displayer.show(result);
    }

    private static void selectByTitle() {
        System.out.println("请输入你要查询的书籍名称:");
        scanner.nextLine();
        List<Book> result = new LinkedList<>();
        for (Book book1 : list) {
            if (book1.getTitle().equals(scanner.nextLine())) {
                result.add(book1);
            }
        }
        Displayer.show(result);
    }
}

6.配置文件 {#配置文件 .list-paragraph}

①db.properties

配置数据库的路径,用户密码等

dbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/library?serverTimezone=UTC
jdbc.username=root
jdbc.password=jinhaolin

②log4j.properties

配置log4j

log4j.rootCategory=DEBUG, CONSOLE,LOGFILE






log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=-%p-%d{yyyy/MM/dd HH:mm:ss,SSS}-%l-%L-%m%n


log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=E:/axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=-%p-%d{yyyy/MM/dd HH:mm:ss,SSS}-%l-%L-%m%n

③mybatis-config.xml

配置mybatis框架

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <properties resource="db.properties"/>

    <!--用于开启缓存,优化查询速度,但基本没什么用,效果很不明显。
    优化查询速度一般采取的措施:
    1.构建索引
    2.使用redio缓存数据库
    3.使用搜索引擎(一般用于电商项目)-->


    <typeAliases>
        <!--方式一:为类起别名-->
<!--        <typeAlias type="com.dreamchaser.domain.User" alias="user"/>-->
        <!--方式二:使用package起别名,该包下的类别名是mybatis自动为我们取好的,就是类名(不区分大小写,但最好按照约定
        俗成的标准去写)-->
        <package name="com.dreamchaser.domain"/>
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--注册中心,指定mapper映射文件-->
    <mappers>
        <!--方式一:单独注册-->
<!--        <mapper resource="com/dreamchaser/dao/UserDao.xml"/>-->
<!--        <mapper resource="com.dreamchaser.mapper/BookMapper.xml"/>-->
        <!--方式二:使用接口的全路径注册-->
        <!--方式三:批量注册,该包下的所有mapper映射文件自动注册(通常采用此种方法注册)-->
        <mapper resource="com.dreamchaser/mapper/BookMapper.xml"/>
<!--        <package name="com.dreamchaser.mapper"/>-->
    </mappers>
</configuration>

④pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>library</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>图书信息管理系统 Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.4</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.18</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>图书信息管理系统</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
      <plugins>
          <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-compiler-plugin</artifactId>
              <configuration>
                  <source>8</source>
                  <target>8</target>
              </configuration>
          </plugin>
      </plugins>

      <resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
          <include>**/*.properties</include>
        </includes>
      </resource>
      <resource>
        <directory>src/main/resources</directory>
        <includes>
          <include>**/*.xml</include>
          <include>**/*.properties</include>
        </includes>
      </resource>
    </resources>
  </build>
</project>

七、部分测试结果展示

1.Sql模式

在这里插入图片描述

①录入信息:

在这里插入图片描述

②图书信息浏览

在这里插入图片描述

③插入图书信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fjBQlm3d-1592834754202)(media/image26.png)]{width="5.766666666666667in" height="4.794444444444444in"}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aH4ifBAq-1592834754203)(media/image27.png)]{width="5.768055555555556in" height="4.034027777777778in"}

注:因为我数据库里的时钟设置的是UTC

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2gnHtt4j-1592834754204)(media/image28.png)]{width="5.558333333333334in" height="0.25in"}

所以存储的日期比我输入的日期提早一天

④查询,以按价格查询(小于)为例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MgOCow8g-1592834754206)(media/image30.png)]{width="4.525in" height="6.2in"}

⑤排序,以按价格排序为例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BO1iCREb-1592834754206)(media/image31.png)]{width="5.766666666666667in" height="6.126388888888889in"}

⑥修改图书信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-isAdUUHz-1592834754208)(media/image32.png)]{width="5.768055555555556in" height="5.129861111111111in"}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ELFMT4xw-1592834754208)(media/image33.png)]{width="5.768055555555556in" height="4.222916666666666in"}

⑦删除图书信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kcMsEHdY-1592834754209)(media/image34.png)]{width="4.25in" height="4.183333333333334in"}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jga4kSbf-1592834754210)(media/image35.png)]{width="5.763194444444444in" height="4.084722222222222in"}

⑧导出为文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-emBgVxoe-1592834754210)(media/image36.png)]{width="4.133333333333334in" height="3.941666666666667in"}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MlGT7mHX-1592834754212)(media/image37.png)]{width="3.683333333333333in" height="4.016666666666667in"}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4QIAIPtj-1592834754213)(media/image38.png)]{width="5.756944444444445in" height="3.3340277777777776in"}

2.noSql模式

NoSql模式与Sql模式区别只是底层实现原理不同,其测试效果是几乎一致的,在此,就只展现该模式的部分功能

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gyhdh4AS-1592834754213)(media/image39.png)]{width="3.675in" height="3.2583333333333333in"}

①插入图书信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bbcx53IR-1592834754214)(media/image40.png)]{width="5.7652777777777775in" height="4.627777777777778in"}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vi6EX8P0-1592834754215)(media/image41.png)]{width="5.763194444444444in" height="3.5729166666666665in"}

其余功能就不展示测试效果图了,其效果和Sql模式是一致的。

八、调试过程中的主要问题、难点以及解决过程

实话说,在调试的过程中我遇到了很多问题,也查阅了很多技术博客,这之中有些确实能解决问题,而有些则是查阅了很多博客,尝试了很多方法还是没能解决问题,或者说出现了另一个问题,对此,我也渐渐摸索出了一套查找解决bug的方法,收获还是蛮大的。

以下列举了我调试中的几个主要问题和解决过程。

1.如何组织各个模块?

我一开始是采用递归循环的方式,但仔细一想,不对!经过我不断调试改进,最终用"套娃"的方式解决了问题,详细思路可以回到 第五部分、主要功能模块的算法流程图 去查看。

2.如何让整个项目有条不紊,井然有序?

整个项目累计代码总量超过千行,如果代码之间逻辑不清晰,关系复杂,那么这个项目调试,后期维护将变得举步维艰。

那么如何做到有条不紊,井然有序呢?

首先要做到项目结构清晰,可以像下面这样,实体类和实体类放在一起,工具类单独放在一起,资源配置文件放在一起,做到项目结构条理清晰。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eb1kZj8d-1592834754216)(media/image42.png)]{width="4.166666666666667in" height="5.916666666666667in"}

其次,要做到代码封装抽象,具体做法就是把复用性高的代码抽离出来,封装成一个工具类,比如我要每个模块都有打印图书信息结果的需求,那么我们完全可以把它封装起来,比如这里我就把它封装成Displayer工具类,其内部有两个方法,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UfhixQRw-1592834754217)(media/image43.png)]{width="5.7659722222222225in" height="4.5055555555555555in"}

可以看到Displayer类有两个方法,但方法名其实都是show,只是针对不同情况进行重载,让方法用起来更加方便。这里还有个小细节,就是方法都是采用静态的方式,因为这里并没有要初始化的数据,所以采用静态,这样可以让代码调用的时候并不需要实例化即可调用其内的方法,让工具类使用起来就更加方便。

3.Maven项目,Mybatis框架,IDEA 的坑

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FFHKe28f-1592834754218)(media/image44.png)]{width="5.949305555555555in" height="3.3833333333333333in"}

上图是我在调试遇到的一个问题,可以看到程序报了Mapped Statements collection does not contain value for xxx的异常,这很明显是mybatis框架报的异常,通过报错信息大概猜测是mybatis XXX容器内不包含我写的Mapper(因为那时候我还不知道Mapped Statement是什么东西),然后我就无脑将这段报错信息贴到百度上搜,确实有很多博客记录了此错误及解决方法,我截了一个下来,如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3jIQMgpS-1592834754218)(media/image45.png)]{width="5.761805555555555in" height="3.923611111111111in"}

但实际上我按照博客上一个个去做,并没有解决问题,这时候我已经花了一个下午时间去查找,问题没解决,倒是把mybatis框架复习了一遍。

苦思之下,我开始逐步调试,以下是我的思考过程:

因为问题肯定出在mybatis框架上,所以我逐步调试,但是呢,我又不懂mybatis源码,看得云里雾里。不过我之前自学Java的时候,跟着视频写过一个类似mybatis的框架------SORM框架(不过功能肯定没mybatis框架复杂,是个小型版的框架),做完后学了其他知识后,自己又回头帮它迭代优化了一下,增加了新的功能,优化了结构。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xgD3bEPv-1592834754219)(media/image46.png)]{width="5.7652777777777775in" height="2.5256944444444445in"}

这段经历让我能大概理解mybatis框架的一些行为,比如在这个地方我就注意到了mappedStatement对象size为0。这时我就猜测这应该是框架本身并没有读取到我写的sql语句,那是由什么造成的呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lhLJdZAA-1592834754220)(media/image47.png)]{width="7.3493055555555555in" height="4.134722222222222in"}

这时候我就开始测试,不用接口类的方式(因为创建实体类也是mybatis框架底层做的),为了缩小问题的范围,我们采用原始的方式(但不是原生jdbc),发现还是这个错误,然后我开始怀疑mapper注册问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-935VOvTl-1592834754221)(media/image48.png)]{width="3.7333333333333334in" height="0.2916666666666667in"}

这里我原本是采用包扫描的方式注册,然后我开始尝试用指定路径文件方式去注册

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xOySgCVz-1592834754221)(media/image49.png)]{width="5.716666666666667in" height="0.375in"}

然后异常变了,说找不到这个文件

好家伙,之前包扫描的时候报的是Mapped Statements collection does not contain value for xxx,现在直接报没找到这个文件!

这时候我就开始思考为什么?

为什么我用包扫描的方式就不报错呢?而用具体的文件路径就报错呢?

真的是包扫描时找到了xml文件而具体文件路径没找到吗?

不对,不是这样的,换个角度讲,包扫描没扫描到,会报错吗?不会,那问题区间缩小,很可能就是因为xml文件路径的问题。而其他配置文件是找到了的,不然它根本不会提示找不到(路径是写在mybatis-config.xml文件里的),既然我们确定了问题所在,这时候我们就需要尝试改变路径

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OzSr4kNB-1592834754222)(media/image50.png)]{width="5.759722222222222in" height="3.2402777777777776in"}

这时候我再去查博客,搜索的不是异常信息,而是配置文件的路径该怎么写?

在搜索的过程中我逐渐意识到我的项目结构可能与别人不同,所以我在搜索时加了Maven限定词,好家伙,不搜不知道,一搜我找到了原因所在。

原来Maven项目编译时会把文件全都输出到Target文件夹下面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c5SmxUUw-1592834754223)(media/image51.png)]{width="3.2083333333333335in" height="2.625in"}

而默认情况下配置文件只会把resource文件夹下面的配置文件输出,这就造成Java文件夹下面的Mapper文件根本不会输出到target里,这样当然就找不到了,于是我修改了Maven项目中核心配置文件pom.xml信息,加入了下面的配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z36E0Xmk-1592834754224)(media/image52.png)]{width="4.45in" height="3.558333333333333in"}

然后呢?

还是找不到…

本着不抛弃不放弃的精神,我开始关注target文件夹的文件结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nPRweG5A-1592834754225)(media/image53.png)]{width="4.216666666666667in" height="4.65in"}

什么,居然有两个com.dreamchser,这是为什么呢?

然后我开始测试加百度,然后发现了IDEA的神坑之处------当我们创建一个包时,com.dreamchser和com/dreamchaser是不同的!

com.dreamchaser就是指第二个圈里的包,com/dreamchaser指的是第一个圈里的包

.和/的差别真的是坑死我我了!

我仔细思考了下,之前查询博客的时候,确实有博客提到idea中创建包时/和.是不一样,但当时我以为我的mapper是被读取进去了,所以没在意,只是检查了其他部分,知道后面调试运行底层源码时MappedStatement这个对象的size=0,通过字面意思猜测mybatis实际上是没有读取进去的,进而开始了这方面的排查,最终找到了原因。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p0USVokk-1592834754227)(media/image54.png)]{width="5.759722222222222in" height="3.2402777777777776in"}

如果用使用动态代理改造CRUD的方式,用接口实现,这意味着接口路径要和xml中那么namespace中的值一致,而在mybatis配置中mapper注册的时候路径要写的是被打包进target/classes下的路径,注意.和/ 的区别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N0EWl5HT-1592834754228)(media/image55.png)]{width="5.767361111111111in" height="1.9770833333333333in"}

这次经历让我明白了该如何去解决问题。要解决问题的前提就是要知道问题的原因,需要定位问题,而不是报个错就盲目复制粘贴报错报错信息去搜博客,这确实可能会让你解决问题,但是有很大几率是你搜遍了网上的解决方式也没有解决问题,因为通常一个框架的同一个异常其实是有很多原因,你就会像无头苍蝇那样乱转,运气好可能会解决问题,运气不好就会到处碰壁。

九.必做题和附加题

1.必做题

此部分另外已提交,就不在此赘述了

2. 附加题

①题目要求

要求写出算法思想和代码

编写三个函数分别实现高精度加法、减法和乘法运算。在主函数中输入任意两个很大的正整数,可根据菜单提示,反复选择相应的操作进行计算。

菜单:1、输入任意两个正整数

2、高精度加法

3、高精度减法

4、高精度乘法

0、退出

②算法思想

我们知道正常的类型是无法存储这种大数值的,这里我们采用两个String来存储两个正整数,然后模拟我们平常计算加减乘除的过程来写代码,对每一位分别处理,最终得到我们想要的结果。

③代码

package com.dreamchaser;

import java.util.Scanner;

public class Main {
    static Scanner scanner = new Scanner(System.in);
    static String s1 = "";
    static String s2 = "";

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            if (choosePattern(1) == 1) {
                break;
            }
        }
    }

    private static int choosePattern(int i) {
        if (i == 1) {
            System.out.println("\n\n1.输入任意两个正整数");
            System.out.println("2.高精度加法");
            System.out.println("3.高精度减法");
            System.out.println("4、高精度乘法");
            System.out.println("0、退出");
            System.out.println("\n\n请输入你的选择(序号):");
        }
        switch (scanner.nextInt()) {
            case 1:
                //吃掉回车
                scanner.nextLine();
                print();
                return 0;
            case 2:
                add();
                return 0;
            case 3:
                delete();
                return 0;
            case 4:
                multiplication();
                return 0;
            case 5:
                return 1;
            default:
                System.out.println("抱歉,输入的是非法字符,请重新输入:");
                return choosePattern(0);
        }
    }

    private static void multiplication() {
        //表示进位
        int i, j, k;
        int[] c = new int[202];
        s1 = new StringBuilder(s1).reverse().toString();
        s2 = new StringBuilder(s2).reverse().toString();
        for (i = 0; i < s1.length(); i++) {
            for (j = 0; j < s2.length(); j++) {
                c[i + j] += (s1.charAt(i) - 48) * (s2.charAt(j) - 48);
            }
        }
        for (k = 1; k <= s1.length() + s2.length(); k++) {
            c[k] += c[k - 1] / 10;
            c[k - 1] %= 10;
        }

        while (c[k] == 0 && k >= 1) {
            k--;
        }
        for (; k >= 0; k--) {
            System.out.print(c[k]);
        }
        System.out.println();

    }

    private static void delete() {
        //表示进位
        int i, j, r = 0, k = 0;
        boolean flag = true;
        int[] c = new int[101];
        for (i = s1.length() - 1, j = s2.length() - 1; j >= 0; i--, j--) {
            //两个位数相减再减去接的位数
            c[k++] = (s1.charAt(i) - s2.charAt(j) - r);
            //清零标记
            r = 0;
            if (c[k - 1] < 0) {
                c[k - 1] += 10;
                r = 1;
            }//如果是负数就借十,并标记
        }
        //剩下的继续减
        while (i >= 0) {
            //减去借的
            c[k++] = (s1.charAt(i) - '0' - r);
            //清零标记
            r = 0;
            //如果是负数就借十,并标记
            if (c[k - 1] < 0) {
                c[k - 1] += 10;
                r = 1;
            }
            i--;
        }
        //输出
        for (i = k - 1; i >= 0; i--) {
            //防止前导0输出的操作
            if (c[i] != 0 || flag) {
                System.out.print(c[i]);
                flag = true;
            }
        }
        //如果都没有输出,说明相减结果为0,应当输出0
        if (flag == false) {
            System.out.print(0);
        }
        System.out.println();

    }

    private static void add() {
        //表示进位
        int i, j, r = 0, k = 0;
        boolean flag = true;
        int[] c = new int[101];
        //从最低位相加,相加他们的公共部分,所以j>=0
        for (i = s1.length() - 1, j = s2.length() - 1; j >= 0; i--, j--) {
            //两个位数和进位的相加后取个位
            c[k++] = (r + s1.charAt(i) - '0' + s2.charAt(j) - '0') % 10;
            //记录进位
            r = (r + s1.charAt(i) - '0' + s2.charAt(j) - '0') / 10;
        }
        //再把剩下的继续加
        while (i >= 0) {
            //位数和进位的相加后取个位
            c[k++] = (r + s1.charAt(i) - '0') % 10;
            //记录进位
            r = (r + s1.charAt(i) - '0') / 10;
            i--;
        }
        //如果还有进位,进到最高位
        if (r != 0) {
            c[k++] = r;
        }
        //输出
        for (i = k - 1; i >= 0; i--) {
            //防止前导0输出的操作
            if (c[i] != 0 || flag) {
                System.out.print(c[i]);
                ;
                flag = true;
            }
        }
        if (flag == false) {
            System.out.print(0);
        }
        System.out.println();
    }

    private static void print() {
        System.out.println("请输入第一个整数:");
        s1 = scanner.nextLine();
        System.out.println("请输入第二个整数:");
        s2 = scanner.nextLine();
        //把大的字符串放前面,方便操作
        if (s1.length() < s2.length() || (s1.length() == s2.length() && s1.compareTo(s2) > 0)) {
            String temp = "";
            temp = s1;
            s1 = s2;
            s2 = temp;
        }
    }

}

④测试结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iKZlSIsN-1592834754228)(media/image56.png)]{width="2.533333333333333in" height="3.1416666666666666in"}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gxU7w2uK-1592834754230)(media/image57.png)]{width="2.4833333333333334in" height="2.2583333333333333in"}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-exS3HWy1-1592834754230)(media/image58.png)]{width="2.4916666666666667in" height="2.5416666666666665in"}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wzznsxKt-1592834754230)(media/image59.png)]{width="3.591666666666667in" height="2.375in"}

十、短学期实践的心得体会

花了三天时间写代码,一天时间写实验报告,总计四天的努力,累计超过千行的代码(确切是1632行,没错,我真的算了!),虽然过程艰辛,但是结果令人满意。

这个项目是我第一个独立完成的项目!说真的,很多东西你看似会了,但是真正到自己去做项目的时候,会发现很多问题,这个不会,那个不会,最后还是要靠百度解决,毕竟面向百度编程这句话不是可不是白讲的。

在这个过程中其实我也学到了很多,尤其是关于mybatis框架和Maven的认识更加深入了。而且这个过程中我渐渐形成了一套属于自己代码风格和编程习惯,而且我对于如何去定位查找解决bug也有了更加清晰的认识。

在这个过程中,我也认识到了自己的很多不足,未来我也要更加扎实的学习,更要尝试去多做项目,这样才能将知识技术化为内在,才能做到真正的融汇贯通,游刃有余!

谨以此记,共勉!

软件工程1902 金昊霖

如果对此项目有什么疑惑或者建议,欢迎在评论区评论。

------------------------2020.7.28------------------------------------
应评论要求,该项目已上传至码云
推荐我的一些其他博客:
适合初学Java的人自学的:用我的亲身经历来告诉你如何自学Java?
适合刚学完SpringBoot的练手项目:【项目实战】个人博客(SpringBoot,SSM,thymeleaf,Semantic UI)——从设计思路到成品

  • 532
    点赞
  • 3022
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 160
    评论
资源简介:SSM Java 项目集合 一、概述 在这个平台上,我们为大家带来了一系列的 JavaSSM(Spring + SpringMVC + MyBatis项目。这些项目旨在展示SSM框架在实际应用中的魅力,同时也为开发者提供了一个快速学习和实践的机会。通过下载和使用这些项目,您将能够深入了解SSM框架的核心概念、设计模式和最佳实践。 二、项目特点 实战性强:这些项目均来自实际业务场景多个领域,具有很强的实战性和参考价值。 技术先进:所有项目均采用最新的SSM框架版本,包括Spring 、SpringMVC 和MyBatis 等,确保技术的先进性和稳定性。 代码规范:项目代码严格按照行业标准和最佳实践进行编写,易于阅读和维护。 文档齐全:每个项目都配备了详细的开发文档和使用说明,方便您快速上手和定制开发。 三、适用人群 Java初学者:通过学习和实践这些项目,您将能够快速掌握SSM框架的基础知识和核心技术。 中高级开发者:这些项目将为您提供丰富的实战经验和灵感,帮助您提升技术水平和解决问题的能力。 项目经理和架构师:这些项目可以作为参考和模板,帮助您更好地规划和设计实际业务场景中的Java项目。 四、下载与使用 下载:所有项目均提供下载,您只需在平台上注册并登录即可获取。 安装与部署:每个项目都提供了详细的安装和部署指南,帮助您快速搭建和运行项目。 定制开发:您可以根据实际需求项目进行定制开发,扩展功能和优化性能。 五、结语 通过这一系列SSM Java项目的下载和学习,您将能够深入了解SSM框架的核心技术,提升自己的编程能力,并在实际业务场景中灵活应用。我们期待您能够通过这些项目获得更多的成长和进步!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 160
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

发光 如星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值