【项目实战】 图书信息管理系统(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)——从设计思路到成品

©️2020 CSDN 皮肤主题: 创作都市 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值