题目七 图书信息管理系统
1 功能描述
设计一个图书信息管理系统,使之具有新建图书信息、显示、插入、删除、查询和排序等功能。
2 具体设计要求
图书信息包括:图书编号、书名、作者名、出版单位、出版时间、价格等。
系统以菜单方式工作:
① 图书信息录入(要求图书信息用文件保存)
② 图书信息浏览
③ 插入图书信息
④ 查询(可以有多种查询方式,如按书名查询、按作者名查询、按价格查询等);
⑤ 排序(可以按图书编号排序、按书名排序、按出版时间排序、按价格排序等);
⑥ 修改图书信息
⑦ 删除图书信息
此项目为图书信息管理系统,是一个采用了mysql+mybatis框架+java编写的maven项目
Mysql,mybatis
数据库:mysql8.0
框架:mybatis
项目结构:maven3.0
语言:Java
Jdk版本:jdk11.0.5(Jdk8.0以上)
编写的IDE:IDEA 2020.01
该图书信息管理系统实现了便捷的图书信息管理,利用命令行操作的方式让用户的操作更为简洁。
本系统提供Sql和noSql两种运行模式。
Sql模式直接对mysql数据库进行操作,便于数据的持久化和规范化,让数据能够更加便捷高效,同时可以存储大量数据,便于进行大数据的管理,如果你想真正用此系统管理你的信息,建议采用此种模式。
noSql模式是把数据载入内存中,优点是速度快,但缺点也很明显,在面对大量数据的情况下显得有些力不从心,此模式建议在数据量小的情况下使用。
两种模式都支持以下功能:
库名: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) | 无 |
各个文件内用分别有若干个二级模块
其下按查询条件有分为若干个三级模块
其下按排序条件有分为若干个三级模块
至于各模块的功能,见名知意,在此就不一一赘述了。
而对于各个模块的返回值和参数,容我卖个关子,此内容将在下个模块中讲解!
"套娃"式的连接方式
何为"套娃"?
可以理解为类似递归的连接方式。
与递归有什么不同?
如果采用递归,就要把低层级的模块套在高层级的模块里。这样子虽然连接了各个模块,达到了类似菜单的效果,但有以下两个较为致命的缺点:
当运行完一个模块后,该模块就运行结束,无法做到循环操作,这与实验要求不符
由于该项目是一个系统,各模块间要不断退出重进,循环多次的递归会造成堆栈不断压缩,有堆栈溢出风险
那我们该如何做呢?
对于第一个缺点,我采用了一个死循环来解决;对于第二个缺点,我的思路就是把改变模块重复调用的时机和位置,让它既能达到效果,又不会导致堆栈溢出
基本思路:
每个函数都有一个整数类型的返回值,只要返回1,就说明该级模块需要退回上一级;返回0则说明不需要,即留在当前模块。
而是留在当前模块还是返回上一级模块,由该模块(记为模块3)的上一级(记为模块2)控制,如果模块3返回一了,就在模块2的上一级(记为模块1)再次调用模块2,即可做到返回上一层;而如果模块3返回0则在模块2再次循环调用,直至模块返回1
这样做不仅能实现功能,而且能避免多次"套娃"导致堆栈溢出的风险
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;
}
}
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);
}
<?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 注:<指的是<,在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 <=#{time}
</if>
<if test="price != null and price !=''">
AND b.price <=#{price}
</if>
</where>
</select>
<!-- 查询小于条件的结果 注:<指的是<,在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>
用于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;
}
}
用于展示传入的数据
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<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("-------------------------------------------------------------------------");
}
}
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<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对象
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();
}
}
用于整个项目的驱动
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);
}
}
}
集成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模式
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);
}
}
配置数据库的路径,用户密码等
dbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/library?serverTimezone=UTC
jdbc.username=root
jdbc.password=jinhaolin
配置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框架
<?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>
<?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
**/ *.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml
**/ *.properties</include>
</includes>
</resource>
</resources>
</build>
</project>
注:因为我数据库里的时钟设置的是UTC
所以存储的日期比我输入的日期提早一天
NoSql模式与Sql模式区别只是底层实现原理不同,其测试效果是几乎一致的,在此,就只展现该模式的部分功能
其余功能就不展示测试效果图了,其效果和Sql模式是一致的。
实话说,在调试的过程中我遇到了很多问题,也查阅了很多技术博客,这之中有些确实能解决问题,而有些则是查阅了很多博客,尝试了很多方法还是没能解决问题,或者说出现了另一个问题,对此,我也渐渐摸索出了一套查找解决bug的方法,收获还是蛮大的。
以下列举了我调试中的几个主要问题和解决过程。
我一开始是采用递归循环的方式,但仔细一想,不对!经过我不断调试改进,最终用"套娃"的方式解决了问题,详细思路可以回到 第五部分、主要功能模块的算法流程图 去查看。
整个项目累计代码总量超过千行,如果代码之间逻辑不清晰,关系复杂,那么这个项目调试,后期维护将变得举步维艰。
那么如何做到有条不紊,井然有序呢?
首先要做到项目结构清晰,可以像下面这样,实体类和实体类放在一起,工具类单独放在一起,资源配置文件放在一起,做到项目结构条理清晰。
其次,要做到代码封装抽象,具体做法就是把复用性高的代码抽离出来,封装成一个工具类,比如我要每个模块都有打印图书信息结果的需求,那么我们完全可以把它封装起来,比如这里我就把它封装成Displayer工具类,其内部有两个方法,如下图:
可以看到Displayer类有两个方法,但方法名其实都是show,只是针对不同情况进行重载,让方法用起来更加方便。这里还有个小细节,就是方法都是采用静态的方式,因为这里并没有要初始化的数据,所以采用静态,这样可以让代码调用的时候并不需要实例化即可调用其内的方法,让工具类使用起来就更加方便。
上图是我在调试遇到的一个问题,可以看到程序报了Mapped Statements collection does not contain value for xxx的异常,这很明显是mybatis框架报的异常,通过报错信息大概猜测是mybatis XXX容器内不包含我写的Mapper(因为那时候我还不知道Mapped Statement是什么东西),然后我就无脑将这段报错信息贴到百度上搜,确实有很多博客记录了此错误及解决方法,我截了一个下来,如图:
但实际上我按照博客上一个个去做,并没有解决问题,这时候我已经花了一个下午时间去查找,问题没解决,倒是把mybatis框架复习了一遍。
苦思之下,我开始逐步调试,以下是我的思考过程:
因为问题肯定出在mybatis框架上,所以我逐步调试,但是呢,我又不懂mybatis源码,看得云里雾里。不过我之前自学Java的时候,跟着视频写过一个类似mybatis的框架------SORM框架(不过功能肯定没mybatis框架复杂,是个小型版的框架),做完后学了其他知识后,自己又回头帮它迭代优化了一下,增加了新的功能,优化了结构。
这段经历让我能大概理解mybatis框架的一些行为,比如在这个地方我就注意到了mappedStatement对象size为0。这时我就猜测这应该是框架本身并没有读取到我写的sql语句,那是由什么造成的呢?
这时候我就开始测试,不用接口类的方式(因为创建实体类也是mybatis框架底层做的),为了缩小问题的范围,我们采用原始的方式(但不是原生jdbc),发现还是这个错误,然后我开始怀疑mapper注册问题
这里我原本是采用包扫描的方式注册,然后我开始尝试用指定路径文件方式去注册
然后异常变了,说找不到这个文件
好家伙,之前包扫描的时候报的是Mapped Statements collection does not contain value for xxx,现在直接报没找到这个文件!
这时候我就开始思考为什么?
为什么我用包扫描的方式就不报错呢?而用具体的文件路径就报错呢?
真的是包扫描时找到了xml文件而具体文件路径没找到吗?
不对,不是这样的,换个角度讲,包扫描没扫描到,会报错吗?不会,那问题区间缩小,很可能就是因为xml文件路径的问题。而其他配置文件是找到了的,不然它根本不会提示找不到(路径是写在mybatis-config.xml文件里的),既然我们确定了问题所在,这时候我们就需要尝试改变路径
这时候我再去查博客,搜索的不是异常信息,而是配置文件的路径该怎么写?
在搜索的过程中我逐渐意识到我的项目结构可能与别人不同,所以我在搜索时加了Maven限定词,好家伙,不搜不知道,一搜我找到了原因所在。
原来Maven项目编译时会把文件全都输出到Target文件夹下面
而默认情况下配置文件只会把resource文件夹下面的配置文件输出,这就造成Java文件夹下面的Mapper文件根本不会输出到target里,这样当然就找不到了,于是我修改了Maven项目中核心配置文件pom.xml信息,加入了下面的配置
然后呢?
还是找不到…
本着不抛弃不放弃的精神,我开始关注target文件夹的文件结构
什么,居然有两个com.dreamchser,这是为什么呢?
然后我开始测试加百度,然后发现了IDEA的神坑之处------当我们创建一个包时,com.dreamchser和com/dreamchaser是不同的!
com.dreamchaser就是指第二个圈里的包,com/dreamchaser指的是第一个圈里的包
.和/的差别真的是坑死我我了!
我仔细思考了下,之前查询博客的时候,确实有博客提到idea中创建包时/和.是不一样,但当时我以为我的mapper是被读取进去了,所以没在意,只是检查了其他部分,知道后面调试运行底层源码时MappedStatement这个对象的size=0,通过字面意思猜测mybatis实际上是没有读取进去的,进而开始了这方面的排查,最终找到了原因。
如果用使用动态代理改造CRUD的方式,用接口实现,这意味着接口路径要和xml中那么namespace中的值一致,而在mybatis配置中mapper注册的时候路径要写的是被打包进target/classes下的路径,注意.和/ 的区别
这次经历让我明白了该如何去解决问题。要解决问题的前提就是要知道问题的原因,需要定位问题,而不是报个错就盲目复制粘贴报错报错信息去搜博客,这确实可能会让你解决问题,但是有很大几率是你搜遍了网上的解决方式也没有解决问题,因为通常一个框架的同一个异常其实是有很多原因,你就会像无头苍蝇那样乱转,运气好可能会解决问题,运气不好就会到处碰壁。
此部分另外已提交,就不在此赘述了
要求写出算法思想和代码
编写三个函数分别实现高精度加法、减法和乘法运算。在主函数中输入任意两个很大的正整数,可根据菜单提示,反复选择相应的操作进行计算。
菜单: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;
}
}
}
花了三天时间写代码,一天时间写实验报告,总计四天的努力,累计超过千行的代码(确切是1632行,没错,我真的算了!),虽然过程艰辛,但是结果令人满意。
这个项目是我第一个独立完成的项目!说真的,很多东西你看似会了,但是真正到自己去做项目的时候,会发现很多问题,这个不会,那个不会,最后还是要靠百度解决,毕竟面向百度编程这句话不是可不是白讲的。
在这个过程中其实我也学到了很多,尤其是关于mybatis框架和Maven的认识更加深入了。而且这个过程中我渐渐形成了一套属于自己代码风格和编程习惯,而且我对于如何去定位查找解决bug也有了更加清晰的认识。
在这个过程中,我也认识到了自己的很多不足,未来我也要更加扎实的学习,更要尝试去多做项目,这样才能将知识技术化为内在,才能做到真正的融汇贯通,游刃有余!
谨以此记,共勉!
软件工程1902 金昊霖
如果对此项目有什么疑惑或者建议,欢迎在评论区评论。