# 图书馆管理系统 **Repository Path**: kk90/library ## Basic Information - **Project Name**: 图书馆管理系统 - **Description**: 该项目是一个适合刚接触mybatis框架的初学者的练手项目,结构简单,清晰!且该项目有配套的介绍博客,易于上手。博客地址:https://blog.csdn.net/qq_46101869/article/details/106910497 - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 7 - **Created**: 2022-01-22 - **Last Updated**: 2022-01-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 一、实验目的 **题目七 图书信息管理系统** 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包:** ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190414_046ebda4_7594071.png) ## 4. 功能概述 **该图书信息管理系统实现了便捷的图书信息管理,利用命令行操作的方式让用户的操作更为简洁。** **本系统提供Sql和noSql两种运行模式。** **Sql模式直接对mysql数据库进行操作,便于数据的持久化和规范化,让数据能够更加便捷高效,同时可以存储大量数据,便于进行大数据的管理,如果你想真正用此系统管理你的信息,建议采用此种模式。** **noSql模式是把数据载入内存中,优点是速度快,但缺点也很明显,在面对大量数据的情况下显得有些力不从心,此模式建议在数据量小的情况下使用。** **两种模式都支持以下功能:** ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190414_b2586b8b_7594071.png) ## 5.功能结构 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190414_7f14f464_7594071.png) ## 6.项目文件结构 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190414_69964580_7594071.png) # 三、数据结构描述 ## 1.实体类Book(持久化层) ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190414_c74992f4_7594071.png) ## 2.Sql模式下的数据库结构 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190414_e1dbb944_7594071.png) 库名: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映射(接口结构) ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190415_083be35a_7594071.png) ## 4.noSql模式下的数据结构 **采用LinkedList\来维护图书信息** ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190415_fc731991_7594071.png) # 四、程序模块描述 该项目大致分为两个一级模块,分别在两个java文件中 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190415_00b1a97c_7594071.png) 各个文件内用分别有若干个二级模块 ## 1.图书信息录入(通过文件录入)模块 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190415_19c01543_7594071.png) ## 2. 图文信息浏览模块 ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cjHHTSx8-1592834754185)(media/image13.png)\]{width="3.058333333333333in" height="0.25in"}](https://images.gitee.com/uploads/images/2020/0728/190415_84f1e0b8_7594071.png) ## 3. 插入模块 ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ijayrnyv-1592834754186)(media/image14.png)\]{width="3.3in" height="0.3in"}](https://images.gitee.com/uploads/images/2020/0728/190415_7d3bcdd8_7594071.png) ## 4. 查询模块 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190415_b1724a9d_7594071.png) 其下按查询条件有分为若干个三级模块 ## 5. 排序模块 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190415_8eea029a_7594071.png) 其下按排序条件有分为若干个三级模块 ## 6. 更新模块 ![](https://images.gitee.com/uploads/images/2020/0728/190415_839d7c2f_7594071.png) ## 7. 删除模块 ![](https://images.gitee.com/uploads/images/2020/0728/190415_70215f1d_7594071.png) ## 8. 写出模块 ![](https://images.gitee.com/uploads/images/2020/0728/190415_dfb2a017_7594071.png) **至于各模块的功能,见名知意,在此就不一一赘述了。** **而对于各个模块的返回值和参数,容我卖个关子,此内容将在下个模块中讲解!** # 五、主要功能模块的算法流程图 ## 1.Sql模式下的算法流程(以查找为例) ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Do8u8qto-1592834754194)(media/image20.png)\]{width="5.763194444444444in" height="6.290972222222222in"}](https://images.gitee.com/uploads/images/2020/0728/190415_228f1ae8_7594071.png) ## 2. 各个模块间的连接算法流程 **"套娃"式的连接方式** **何为"套娃"?** 可以理解为类似递归的连接方式。 **与递归有什么不同?** 如果采用递归,就要把低层级的模块套在高层级的模块里。这样子虽然连接了各个模块,达到了类似菜单的效果,但有以下两个较为致命的缺点: 1. 当运行完一个模块后,该模块就运行结束,无法做到循环操作,这与实验要求不符 2. 由于该项目是一个系统,各模块间要不断退出重进,循环多次的递归会造成堆栈不断压缩,有堆栈溢出风险 **那我们该如何做呢?** 对于第一个缺点,我采用了一个死循环来解决;对于第二个缺点,我的思路就是把改变模块重复调用的时机和位置,让它既能达到效果,又不会导致堆栈溢出 **基本思路:** **每个函数都有一个整数类型的返回值,只要返回1,就说明该级模块需要退回上一级;返回0则说明不需要,即留在当前模块。** **而是留在当前模块还是返回上一级模块,由该模块(记为模块3)的上一级(记为模块2)控制,如果模块3返回一了,就在模块2的上一级(记为模块1)再次调用模块2,即可做到返回上一层;而如果模块3返回0则在模块2再次循环调用,直至模块返回1\ 这样做不仅能实现功能,而且能避免多次"套娃"导致堆栈溢出的风险** ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190415_3445fdcf_7594071.png) # 六、代码清单 ## 1.项目结构全览 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190415_a81ae686_7594071.png) ## 2. 实体类Book ```java 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 ```java package com.dreamchaser.mapper; import com.dreamchaser.domain.Book; import java.util.List; import java.util.Map; public interface BookMapper { public List selectSmaller(Map map); public List selectAll(); public List selectBigger(Map map); public List findBookByCondition(Map map); public List findBooksById(int id); public void insertBook(Map map); public void insertBooks(List books); public void updateBookById(Map map); public void updateBooks(List> list); public void deleteBookById(int id); public void deleteBooksByIds(List ids); /** * @param map 要注意这里的map中只能有一个对象 * @return */ public List findBookByConditionO(Map map); } ``` ### ②BookMapper.xml ```java b.id, b.title, b.name, b.publisher, b.time, b.price INSERT INTO books ( id, title, name, publisher, time, price ) VALUES ( #{id}, #{title}, #{name}, #{publisher}, #{time}, #{price} ) INSERT INTO books ( id, title, name, publisher, time, price ) VALUES ( #{item.id}, #{item.title}, #{item.name}, #{item.publisher}, #{item.time}, #{item.price} ) UPDATE books title = #{title}, name = #{name}, publisher = #{publisher}, time = #{time}, price = #{price} WHERE id = #{id} UPDATE books title = #{item.title}, name = #{item.name}, publisher = #{item.publisher}, time = #{item.time}, price = #{item.price} WHERE id = #{item.id} DELETE FROM books WHERE id = #{id} DELETE FROM books WHERE id IN #{id} ``` ## 4.工具类 {#工具类 .list-paragraph} ### ①日期工具类 DateUtil 用于Date和字符串之间的转换 ```java 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 用于展示传入的数据 ```java 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 list = new LinkedList<>(); // list.add(book); // show(list); // } public static void show(List 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 ```java 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 txt文件内容封装成一个list返回,如果文件不存在就返回null */ public static List readTxtFile(String path){ File file=new File(path); List 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 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对象 ```java 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 用于整个项目的驱动 ```java 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模式 ```java 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 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 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 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 books = bookMapper.findBookByConditionO(map); Displayer.show(books); } private static void selectOrderByTime() { map.clear(); map.put("time", "1"); List books = bookMapper.findBookByConditionO(map); Displayer.show(books); } private static void selectOrderByTitle() { map.clear(); map.put("title", "1"); List books = bookMapper.findBookByConditionO(map); Displayer.show(books); } private static void selectOrderById() { map.clear(); map.put("id", "1"); List 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 books = bookMapper.selectBigger(map); Displayer.show(books); } private static void selectByPrice() { System.out.println("请输入你要查询的价格:"); map.clear(); map.put("price", scanner.nextInt()); List books = bookMapper.findBookByCondition(map); Displayer.show(books); } private static void selectByPriceS() { System.out.println("请输入你要查询的价格:"); map.clear(); map.put("price", new BigDecimal(scanner.nextInt())); List 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 books = bookMapper.findBookByCondition(map); Displayer.show(books); } private static void selectByTitle() { System.out.println("请输入你要查询的书籍名称:"); map.clear(); scanner.nextLine(); map.put("title", scanner.nextLine()); List books = bookMapper.findBookByCondition(map); Displayer.show(books); } } ``` ### ③noSql模块类 集成noSql模式 ```java 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 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 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 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 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 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 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 result = new LinkedList<>(); for (Book book1 : list) { if (book1.getTitle().equals(scanner.nextLine())) { result.add(book1); } } Displayer.show(result); } } ``` ## 6.配置文件 {#配置文件 .list-paragraph} ### ①db.properties 配置数据库的路径,用户密码等 ```java 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 ```java 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框架 ```java ``` ### ④pom.xml ```java 4.0.0 org.example library 1.0-SNAPSHOT war 图书信息管理系统 Maven Webapp http://www.example.com UTF-8 1.7 1.7 junit junit 4.11 test org.mybatis mybatis 3.5.4 mysql mysql-connector-java 8.0.18 图书信息管理系统 maven-clean-plugin 3.1.0 maven-resources-plugin 3.0.2 maven-compiler-plugin 3.8.0 maven-surefire-plugin 2.22.1 maven-war-plugin 3.2.2 maven-install-plugin 2.5.2 maven-deploy-plugin 2.8.2 org.apache.maven.plugins maven-compiler-plugin 8 8 src/main/java **/*.xml **/*.properties src/main/resources **/*.xml **/*.properties ``` # 七、部分测试结果展示 ## 1.Sql模式 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190415_47268b9e_7594071.png) ### ①录入信息: ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190416_ece63291_7594071.png) ### ②图书信息浏览 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0728/190416_e2d3a1a0_7594071.png) ### ③插入图书信息 ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fjBQlm3d-1592834754202)(media/image26.png)\]{width="5.766666666666667in" height="4.794444444444444in"}](https://images.gitee.com/uploads/images/2020/0728/190416_f8a86f8b_7594071.png) ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aH4ifBAq-1592834754203)(media/image27.png)\]{width="5.768055555555556in" height="4.034027777777778in"}](https://images.gitee.com/uploads/images/2020/0728/190416_e62a3cd8_7594071.png) 注:因为我数据库里的时钟设置的是UTC ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2gnHtt4j-1592834754204)(media/image28.png)\]{width="5.558333333333334in" height="0.25in"}](https://images.gitee.com/uploads/images/2020/0728/190416_2601fa16_7594071.png) 所以存储的日期比我输入的日期提早一天 ### ④查询,以按价格查询(小于)为例 ![](https://images.gitee.com/uploads/images/2020/0728/190416_2f5452a6_7594071.png) ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MgOCow8g-1592834754206)(media/image30.png)\]{width="4.525in" height="6.2in"}](https://images.gitee.com/uploads/images/2020/0728/190416_38670e53_7594071.png) ### ⑤排序,以按价格排序为例 ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BO1iCREb-1592834754206)(media/image31.png)\]{width="5.766666666666667in" height="6.126388888888889in"}](https://images.gitee.com/uploads/images/2020/0728/190416_d10f5238_7594071.png) ### ⑥修改图书信息 ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-isAdUUHz-1592834754208)(media/image32.png)\]{width="5.768055555555556in" height="5.129861111111111in"}](https://images.gitee.com/uploads/images/2020/0728/190416_cec1352f_7594071.png) ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ELFMT4xw-1592834754208)(media/image33.png)\]{width="5.768055555555556in" height="4.222916666666666in"}](https://images.gitee.com/uploads/images/2020/0728/190416_8915f7c2_7594071.png) ### ⑦删除图书信息 ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kcMsEHdY-1592834754209)(media/image34.png)\]{width="4.25in" height="4.183333333333334in"}](https://images.gitee.com/uploads/images/2020/0728/190416_8b51e0f8_7594071.png) ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jga4kSbf-1592834754210)(media/image35.png)\]{width="5.763194444444444in" height="4.084722222222222in"}](https://images.gitee.com/uploads/images/2020/0728/190416_13193cd2_7594071.png) ### ⑧导出为文件 ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-emBgVxoe-1592834754210)(media/image36.png)\]{width="4.133333333333334in" height="3.941666666666667in"}](https://images.gitee.com/uploads/images/2020/0728/190416_d135d40a_7594071.png) ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MlGT7mHX-1592834754212)(media/image37.png)\]{width="3.683333333333333in" height="4.016666666666667in"}](https://images.gitee.com/uploads/images/2020/0728/190416_7fd167a7_7594071.png) ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4QIAIPtj-1592834754213)(media/image38.png)\]{width="5.756944444444445in" height="3.3340277777777776in"}](https://images.gitee.com/uploads/images/2020/0728/190416_f0d7b5f5_7594071.png) ## 2.noSql模式 **NoSql模式与Sql模式区别只是底层实现原理不同,其测试效果是几乎一致的,在此,就只展现该模式的部分功能** ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gyhdh4AS-1592834754213)(media/image39.png)\]{width="3.675in" height="3.2583333333333333in"}](https://images.gitee.com/uploads/images/2020/0728/190416_d838b0aa_7594071.png) ### ①插入图书信息 ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bbcx53IR-1592834754214)(media/image40.png)\]{width="5.7652777777777775in" height="4.627777777777778in"}](https://images.gitee.com/uploads/images/2020/0728/190417_0e732f88_7594071.png) ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vi6EX8P0-1592834754215)(media/image41.png)\]{width="5.763194444444444in" height="3.5729166666666665in"}](https://images.gitee.com/uploads/images/2020/0728/190417_8abce627_7594071.png) **其余功能就不展示测试效果图了,其效果和Sql模式是一致的。** # 八、调试过程中的主要问题、难点以及解决过程 **实话说,在调试的过程中我遇到了很多问题,也查阅了很多技术博客,这之中有些确实能解决问题,而有些则是查阅了很多博客,尝试了很多方法还是没能解决问题,或者说出现了另一个问题,对此,我也渐渐摸索出了一套查找解决bug的方法,收获还是蛮大的。** **以下列举了我调试中的几个主要问题和解决过程。** ## 1.如何组织各个模块? **我一开始是采用递归循环的方式,但仔细一想,不对!经过我不断调试改进,最终用"套娃"的方式解决了问题,详细思路可以回到 第五部分、主要功能模块的算法流程图 去查看。** ## 2.如何让整个项目有条不紊,井然有序? **整个项目累计代码总量超过千行,如果代码之间逻辑不清晰,关系复杂,那么这个项目调试,后期维护将变得举步维艰。** **那么如何做到有条不紊,井然有序呢?** **首先要做到项目结构清晰,可以像下面这样,实体类和实体类放在一起,工具类单独放在一起,资源配置文件放在一起,做到项目结构条理清晰。** ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eb1kZj8d-1592834754216)(media/image42.png)\]{width="4.166666666666667in" height="5.916666666666667in"}](https://images.gitee.com/uploads/images/2020/0728/190417_604f1e16_7594071.png) **其次,要做到代码封装抽象,具体做法就是把复用性高的代码抽离出来,封装成一个工具类,比如我要每个模块都有打印图书信息结果的需求,那么我们完全可以把它封装起来,比如这里我就把它封装成Displayer工具类,其内部有两个方法,如下图:** ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UfhixQRw-1592834754217)(media/image43.png)\]{width="5.7659722222222225in" height="4.5055555555555555in"}](https://images.gitee.com/uploads/images/2020/0728/190417_84127a32_7594071.png) **可以看到Displayer类有两个方法,但方法名其实都是show,只是针对不同情况进行重载,让方法用起来更加方便。这里还有个小细节,就是方法都是采用静态的方式,因为这里并没有要初始化的数据,所以采用静态,这样可以让代码调用的时候并不需要实例化即可调用其内的方法,让工具类使用起来就更加方便。** ## 3.Maven项目,Mybatis框架,IDEA 的坑 ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FFHKe28f-1592834754218)(media/image44.png)\]{width="5.949305555555555in" height="3.3833333333333333in"}](https://images.gitee.com/uploads/images/2020/0728/190417_08f4e327_7594071.png) **上图是我在调试遇到的一个问题,可以看到程序报了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"}](https://images.gitee.com/uploads/images/2020/0728/190417_67e498a7_7594071.png) **但实际上我按照博客上一个个去做,并没有解决问题,这时候我已经花了一个下午时间去查找,问题没解决,倒是把mybatis框架复习了一遍。** **苦思之下,我开始逐步调试,以下是我的思考过程:** **因为问题肯定出在mybatis框架上,所以我逐步调试,但是呢,我又不懂mybatis源码,看得云里雾里。不过我之前自学Java的时候,跟着视频写过一个类似mybatis的框架------SORM框架(不过功能肯定没mybatis框架复杂,是个小型版的框架),做完后学了其他知识后,自己又回头帮它迭代优化了一下,增加了新的功能,优化了结构。** ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xgD3bEPv-1592834754219)(media/image46.png)\]{width="5.7652777777777775in" height="2.5256944444444445in"}](https://images.gitee.com/uploads/images/2020/0728/190417_a871e575_7594071.png) **这段经历让我能大概理解mybatis框架的一些行为,比如在这个地方我就注意到了mappedStatement对象size为0。这时我就猜测这应该是框架本身并没有读取到我写的sql语句,那是由什么造成的呢?** ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lhLJdZAA-1592834754220)(media/image47.png)\]{width="7.3493055555555555in" height="4.134722222222222in"}](https://images.gitee.com/uploads/images/2020/0728/190417_a470d805_7594071.png) **这时候我就开始测试,不用接口类的方式(因为创建实体类也是mybatis框架底层做的),为了缩小问题的范围,我们采用原始的方式(但不是原生jdbc),发现还是这个错误,然后我开始怀疑mapper注册问题** ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-935VOvTl-1592834754221)(media/image48.png)\]{width="3.7333333333333334in" height="0.2916666666666667in"}](https://images.gitee.com/uploads/images/2020/0728/190417_1fb71ef3_7594071.png) **这里我原本是采用包扫描的方式注册,然后我开始尝试用指定路径文件方式去注册** ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xOySgCVz-1592834754221)(media/image49.png)\]{width="5.716666666666667in" height="0.375in"}](https://images.gitee.com/uploads/images/2020/0728/190417_e5174f1b_7594071.png) **然后异常变了,说找不到这个文件** **好家伙,之前包扫描的时候报的是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"}](https://images.gitee.com/uploads/images/2020/0728/190417_12e9c13d_7594071.png) **这时候我再去查博客,搜索的不是异常信息,而是配置文件的路径该怎么写?** **在搜索的过程中我逐渐意识到我的项目结构可能与别人不同,所以我在搜索时加了Maven限定词,好家伙,不搜不知道,一搜我找到了原因所在。** **原来Maven项目编译时会把文件全都输出到Target文件夹下面** ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c5SmxUUw-1592834754223)(media/image51.png)\]{width="3.2083333333333335in" height="2.625in"}](https://images.gitee.com/uploads/images/2020/0728/190417_51ab271a_7594071.png) **而默认情况下配置文件只会把resource文件夹下面的配置文件输出,这就造成Java文件夹下面的Mapper文件根本不会输出到target里,这样当然就找不到了,于是我修改了Maven项目中核心配置文件pom.xml信息,加入了下面的配置** ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z36E0Xmk-1592834754224)(media/image52.png)\]{width="4.45in" height="3.558333333333333in"}](https://images.gitee.com/uploads/images/2020/0728/190417_286398ac_7594071.png) **然后呢?** **还是找不到\...\.....** **本着不抛弃不放弃的精神,我开始关注target文件夹的文件结构** ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nPRweG5A-1592834754225)(media/image53.png)\]{width="4.216666666666667in" height="4.65in"}](https://images.gitee.com/uploads/images/2020/0728/190417_ab9b400f_7594071.png) **什么,居然有两个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"}](https://images.gitee.com/uploads/images/2020/0728/190417_44d17d5b_7594071.png) **如果用使用动态代理改造CRUD的方式,用接口实现,这意味着接口路径要和xml中那么namespace中的值一致,而在mybatis配置中mapper注册的时候路径要写的是被打包进target/classes下的路径,注意.和/ 的区别** ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N0EWl5HT-1592834754228)(media/image55.png)\]{width="5.767361111111111in" height="1.9770833333333333in"}](https://images.gitee.com/uploads/images/2020/0728/190417_d78c8a6a_7594071.png) **这次经历让我明白了该如何去解决问题。要解决问题的前提就是要知道问题的原因,需要定位问题,而不是报个错就盲目复制粘贴报错报错信息去搜博客,这确实可能会让你解决问题,但是有很大几率是你搜遍了网上的解决方式也没有解决问题,因为通常一个框架的同一个异常其实是有很多原因,你就会像无头苍蝇那样乱转,运气好可能会解决问题,运气不好就会到处碰壁。** # 九.必做题和附加题 ## 1.必做题 此部分另外已提交,就不在此赘述了 ## 2. 附加题 ### ①题目要求 要求写出算法思想和代码 编写三个函数分别实现高精度加法、减法和乘法运算。在主函数中输入任意两个很大的正整数,可根据菜单提示,反复选择相应的操作进行计算。 菜单:1、输入任意两个正整数 2、高精度加法 3、高精度减法 4、高精度乘法 0、退出 ### ②算法思想 我们知道正常的类型是无法存储这种大数值的,这里我们采用两个String来存储两个正整数,然后模拟我们平常计算加减乘除的过程来写代码,对每一位分别处理,最终得到我们想要的结果。 ### ③代码 ```java 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"}](https://images.gitee.com/uploads/images/2020/0728/190418_efe3490e_7594071.png) ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gxU7w2uK-1592834754230)(media/image57.png)\]{width="2.4833333333333334in" height="2.2583333333333333in"}](https://images.gitee.com/uploads/images/2020/0728/190418_4eb06518_7594071.png) ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-exS3HWy1-1592834754230)(media/image58.png)\]{width="2.4916666666666667in" height="2.5416666666666665in"}](https://images.gitee.com/uploads/images/2020/0728/190418_fe069d8d_7594071.png) ![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wzznsxKt-1592834754230)(media/image59.png)\]{width="3.591666666666667in" height="2.375in"}](https://images.gitee.com/uploads/images/2020/0728/190418_573f9af6_7594071.png) # 十、短学期实践的心得体会 **花了三天时间写代码,一天时间写实验报告,总计四天的努力,累计超过千行的代码(确切是1632行,没错,我真的算了!),虽然过程艰辛,但是结果令人满意。** **这个项目是我第一个独立完成的项目!说真的,很多东西你看似会了,但是真正到自己去做项目的时候,会发现很多问题,这个不会,那个不会,最后还是要靠百度解决,毕竟面向百度编程这句话不是可不是白讲的。** **在这个过程中其实我也学到了很多,尤其是关于mybatis框架和Maven的认识更加深入了。而且这个过程中我渐渐形成了一套属于自己代码风格和编程习惯,而且我对于如何去定位查找解决bug也有了更加清晰的认识。** **在这个过程中,我也认识到了自己的很多不足,未来我也要更加扎实的学习,更要尝试去多做项目,这样才能将知识技术化为内在,才能做到真正的融汇贯通,游刃有余!** 谨以此记,共勉! 软件工程1902 金昊霖 如果对此项目有什么疑惑或者建议,欢迎在评论区评论。