基于Mybatis的图书管理系统

本项目是基于Mybatis+JUL+Lombok+Maven的图书管理系统(带单元测试)

成果示范

项目需求:

  • 在线录入学生信息和书籍信息

  • 在线录入借阅信息

  • 查询书籍信息列表

  • 查询学生信息列表

  • 查询借阅信息列表

  • 完整的日志系统

  • Maven打包项目jar并在桌面运行

设计数据库

建立数据库(book_manage)

字符集要修改,否则可能出现乱码

创建表

学生表(student):

书籍表(book)

书籍借阅信息(borrow)

设置外键

注意,外键设置在borrow表,可以理解为一个二维表。

设置删除外键的触发器

注意,触发器设置在book和student表。

被设计触发器的表并不是外键表,而是进行操作的表。

我设计一个书籍信息表,设计一个学生表,设计一个学生借书表,外键存在于学生借书表(理解为二维表),触发器存在于学生表和书籍信息表(理解为一维表)。

student表中触发器的命令是:

1
DELETE FROM borrow where sid = old.sid

可以看到触发器中是delete,触发器本身的触发条件的定义也是delete,二者本身其实毫无关系。该语句联系到外部的”删除”,可以解释为

1
2
在删除学生表中的信息之前,要删除借阅表中进行约束的外键,因为进行的是删除操作,所以要去old表中寻找sid
(从借阅表中删除你删除的sid对应的那行外键)

另一侧book表同理

进行测试

我们写一个学生,写一本书,写一个借阅关系。

发现删除学生或者书后,borrow对应的借阅行也随之删除。触发器设置成功。

下面我们实现需求1:在线录入书籍信息,学生信息。

创建Maven项目

pom.xml中写入依赖

(Mybatis+jdbc+JUnit+Lombok)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!--pom.xml-->
<dependencies>

<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>

<!--JUnit5-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>

<!--JDBC-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>

<!--Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>

</dependencies>


配置后进行Maven加载

测试

项目运行成功。

IDEA连接数据库

测试Insert功能

Mybatis配置文件mybatis-config.xml

在默认的文件夹中创建mybatis文件mybatis-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/book_manage"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>

报错1:显示dtd注册

解决方法:在设置中添加"http://mybatis.org/dtd/mybatis-3-config.dtd"到默认dtd中

定义实体类(Lombok)

student

1
2
3
4
5
6
7
8
9
10
11
12
13
package book.manage.entity;

import lombok.Data;

@Data
public class Student {
int sid;
final String name;
final String sex;
final int grade;
}


Book

1
2
3
4
5
6
7
8
9
10
11
12
13
package book.manage.entity;

import lombok.Data;

@Data
public class Book {
int bid;
final String title;
final String desc;
final double price;
}


我们发现数据库中的enum,在实体类中我们定义的是String

数据库中的decimal,我们定义的是double

由此可见并不需要完全对应,只要能让数值进行大致对应即可。

配置mybatis.xml中的mapper

在mybatis-config.xml中进行配置mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/book_manage"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="book.manage.mapper.BookMapper"/>
</mappers>
</configuration>

问题1:我记得有一个作用整个包,就小写的是什么来着?

答:是mybatis-config.xml中可以对实体类整个包进行默认读取,mapper.xml中可以直接使用别名以来连接接口方法和实体类进行映射关系的构造。

问题2:使用接口注解是什么实现流程?使用mapper.xml是什么操作流程?

①接口注解:用resources方法读取mybatis配置文件,可以知道mapper接口文件的位置,然后opensession相当于打开了一次会话,读取mapper相当于打开了一个映射关系合集。

②mapper配置文件:使用简单的IO方法读取mybatis配置文件。配置文件读取了mapper.xml映射规则将接口和实体类进行连接。Main调用接口,接口调用实体类按照之前设定的关系进行连接。

创建接口(用注解代替mapper.xml)

1
2
3
4
5
6
7
8
9
10
11
package book.manage.mapper;

import book.manage.entity.Student;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;

public interface BookMapper {
@Insert("insert into student(name,sex,grade) value(#{name},#{sex},#{grade})")
int addStudent(Student student);
}

Main进行测试

SqlSessionfactorybuilder(读取mybatis.xml)→factory

factory.opensession()→sqlssion

sqlssion.getmapper(mapper)→mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package book.manage;

import book.manage.entity.Student;
import book.manage.mapper.BookMapper;
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 org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Main {
public static void main(String[] args) throws IOException {
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));
try(SqlSession session = factory.openSession(true)){
BookMapper mapper = session.getMapper(BookMapper.class);

System.out.println(mapper.addStudent(new Student("小明","男",2019)));
}
}
}

报错2:传入数据时候,由于lombok的构造参数,默认输入的是sid,

解决办法:①删除Student中的@AllArgsConstructor,再插入时就会与接口编写的sql语句进行对应②在接口中形参设置为Student student③给实体类的变量加上final(final加了就会去找没有sid的构造函数,因为数据库字段非空,sid可以自增)

报错3:进行Insert操作的测试时,没有读取到mybatis-comfig.xml文件

原因:读取resources文件的函数用错

1
2
3
4
5
6
//错误代码
new SqlSessionFactoryBuilder().build(new FileInputStream("mybatis-config.xml"));

//正确代码
new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));

new FileInputStream读取文件时必须加上详细的文件地址,Resources.getResourceAsReader可以直接读取resources文件夹里面的文件

结果

生效了一行

数据库结果

插入书籍信息同理

报错:插入数据失败

解决:

①书的描述不能使用”DESC“作为关键字,DESC时数据库排序的关键字,把DESC换成des成功插入。

②或者给sql语句中的desc加上单引号,变成’desc’,如果是关键字就要加上飘。

主要思路:

①创建实体类

②编写Mapper.java接口

③编写Main

优化sqlsession

用SqlUtil创建sqlsession

将创建sqlsession的过程单独放在一个工具类中,只需要在主函数中对mapper文件进行getmapper即可。此项目再次简化,将创建mapper的过程也放在SqlUtil过程中。

SqlSessionfactorybuilder(读取mybatis.xml)→factory

factory.opensession()→sqlssion

sqlssion.getmapper(mapper)→mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package book.manage.sql;

import book.manage.mapper.BookMapper;
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.util.function.Consumer;

/**
* @author Wahoyu*
/
public class SqlUtil {

private SqlUtil(){}

private static SqlSessionFactory factory;
static {
try {
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void doSqlWork(Consumer<BookMapper> consumer){
try(SqlSession sqlSession = factory.openSession(true)){
BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
consumer.accept(bookMapper);
}
}
}


这样主函数就可以直接创建sqlsession,之间创建不同的mapper,针对mapper很多的情况非常方便。

Main主菜单格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package book.manage;


import java.io.IOException;
import java.util.Scanner;

public class Main {
public static void main(String[] args) throws IOException {
try (Scanner scanner = new Scanner(System.in)) {
while (true) {
System.out.println("************************* *");
System.out.println("1.录入学生信息");
System.out.println("2.录入书籍信息");
System.out.println("输入你想要执行的操作(输入其他任意数字退出):");
int input;
try {
input = scanner.nextInt();
}catch(Exception e){
return;
}
switch(input){
case 1:
break;
case 2:
break;
default:
return;
}
}
}
}
}


代码整理一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//Main
package book.manage;


import book.manage.entity.Student;
import book.manage.sql.SqlUtil;

import java.io.IOException;
import java.util.Scanner;

public class Main {
public static void main(String[] args) throws IOException {
try (Scanner scanner = new Scanner(System.in)) {
while (true) {
System.out.println("************************* *");
System.out.println("1.录入学生信息");
System.out.println("2.录入书籍信息");
System.out.println("输入你想要执行的操作(输入其他任意数字退出):");
int input;
try {
input = scanner.nextInt();
}catch(Exception e){
return;
}
//清理换行符
scanner.nextLine();
switch(input){
case 1:
addStudent(scanner);
break;
case 2:
break;
default:
return;
}
}
}
}
private static void addStudent(Scanner scanner){
System.out.println("请输入学生名字:");
String name = scanner.nextLine();
System.out.println("请输入学生的性别(男/女):");
String sex = scanner.nextLine();
System.out.println("请输入学生的年级:");
String grade = scanner.nextLine();
int g = Integer.parseInt(grade);

//创建学生对象
Student student = new Student(name,sex,g);
SqlUtil.doSqlWork(mapper -> {
int i = mapper.addStudent(student);
if(i>0){
System.out.println("录入成功!");
}else{
System.out.println("录入失败,请重试!");
}
});
}

}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//SqlUtil
package book.manage.sql;

import book.manage.mapper.BookMapper;
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.util.function.Consumer;

/***
@author Wahoyu*
/
public class SqlUtil {

private SqlUtil(){}

private static SqlSessionFactory factory;
static {
try {
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void doSqlWork(Consumer<BookMapper> consumer){
try(SqlSession sqlSession = factory.openSession(true)){
BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
consumer.accept(bookMapper);
}
}
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!--Mybatisconfig.xml-->
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/book_manage"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="book.manage.mapper.BookMapper"/>
</mappers>
</configuration>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//mapper.java
package book.manage.mapper;

import book.manage.entity.Book;
import book.manage.entity.Student;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;

public interface BookMapper {

@Insert("insert into student(name,sex,grade) value(#{name},#{sex},#{grade})")
int addStudent(Student student);

@Insert("insert into book(title,`desc`,price) value(#{title},#{desc},#{price})")
int addBook(Book book);

@Select("select * from student where sid = #{sid}")
Student getStudentBySid(int sid);

@Select("select* from book where bid = #{bid}")
Book getBookByBid(int bid);

}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Student
package book.manage.entity;

import lombok.Data;

@Data
public class Student {
int sid;
final String name;
final String sex;
final int grade;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
//book
package book.manage.entity;

import lombok.Data;

@Data
public class Book {
int bid;
final String title;
final String desc;
final double price;
}


开始配置日志系统

  • [x] 敏感操作进行日志输出,输出到文件

配置(日志配置)文件

logging.properties

1
2
3
4
5
6
7
8
9
10
11
#设置输出级别
#不用控制台打印,用文件打印
handlers= java.util.logging.FileHandler
.level= All

#命名输出文件
java.util.logging.FileHandler=console.log
#设置输出格式为默认
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter


主函数配置日志

1
2
3
//日志配置
LogManager manager = LogManager.getLogManager();
manager.readConfiguration(Resources.getResourceAsStream("logging.properties"));

报错1:不能使用log.info();

解决:在Main类上添加注解@Log

报错2:(.level)附近的日志配置文件书写格式错误

解决:ALL应该全部大写

报错3:输出到文件中的日志都是标签格式的

解决:单词打错,应该是handler,影响了日志文件格式的定义。

让日志追加打印

配置logging.properties

1
2
3
4
5
6
7
8
9
10
11
12
#设置输出级别
#不用控制台打印,用文件打印
handlers= java.util.logging.FileHandler
.level= INFO

#命名输出文件
java.util.logging.FileHandler.pattern=console.log
#设置输出格式为默认
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
#追加打印
java.util.logging.FileHandler.append=true

编写学生借书操作

1.编写菜单和提示输入界面

2.分析数据库表的设计

插入bid和sid即可

3.编写mapper

1
2
@Insert("isnert into borrow(sid,bid) values(#{sid},#{bid})")
int addBorrow(int sid,int bid);

4.编写Main中对应创建对象的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//借书
private static void addBorrow(Scanner scanner){
System.out.print("请输入书的编号:");
String a = scanner.nextLine();
int bid = Integer.parseInt(a);
System.out.print("请输入学号:");
String b = scanner.nextLine();
int sid = Integer.parseInt(b);

SqlUtil.doSqlWork(mapper -> {
int i = mapper.addBorrow(sid,bid);
if(i>0){
System.out.println("录入成功!");
log.info("添加一条借阅信息");
}
else{
System.out.println("录入失败,请重试!");
}
});
}
1
2
3
4
5
case 3:
try{addBorrow(scanner);}
catch(Exception e){
System.out.println("录入失败!(可能录入重复)");
break;}

5.完善main

提问:为什么借阅信息不需要实体类?

解答:后面进行查询就需要实体类了,查询的时候要把数据库中的东西拿出来,放在实体类中,通过实体类进行表达。

报错:sid没有找到,mapper中输入两个数,导致sid和bid没有对应上

解决:加注解表示哪个对用的是哪个

代码整理二

Main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package book.manage;


import book.manage.entity.Book;
import book.manage.entity.Student;
import book.manage.sql.SqlUtil;
import lombok.extern.java.Log;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.io.Resources;

import java.io.IOException;
import java.util.Scanner;
import java.util.logging.LogManager;

@Log
public class Main {
public static void main(String[] args) throws IOException {
try (Scanner scanner = new Scanner(System.in)) {
//日志配置
LogManager manager = LogManager.getLogManager();
manager.readConfiguration(Resources.getResourceAsStream("logging.properties"));

while (true) {
System.out.println("===============================================");
System.out.println("1.录入学生信息");
System.out.println("2.录入书籍信息");
System.out.println("3.添加借阅信息");
System.out.print("输入你想要执行的操作(输入其他任意数字退出):");
int input;
try {
input = scanner.nextInt();
}catch(Exception e){
return;
}
//清理换行符
scanner.nextLine();
switch(input){
case 1:
addStudent(scanner);
break;
case 2:
addBook(scanner);
break;
case 3:
try{addBorrow(scanner);}
catch(Exception e){
System.out.println("录入失败!(可能录入重复)");
break;}
default:
return;
}
}
}
}
//借书
private static void addBorrow(Scanner scanner){
System.out.print("请输入书的编号:");
String a = scanner.nextLine();
int bid = Integer.parseInt(a);
System.out.print("请输入学号:");
String b = scanner.nextLine();
int sid = Integer.parseInt(b);

SqlUtil.doSqlWork(mapper -> {
int i = mapper.addBorrow(sid,bid);
if(i>0){
System.out.println("录入成功!");
log.info("添加一条借阅信息");
}
else{
System.out.println("录入失败,请重试!");
}
});
}

//插入学生
private static void addStudent(Scanner scanner){
System.out.print("请输入学生名字:");
String name = scanner.nextLine();
System.out.print("请输入学生的性别(男/女):");
String sex = scanner.nextLine();
System.out.print("请输入学生的年级:");
String grade = scanner.nextLine();
int g = Integer.parseInt(grade);

//创建学生对象
Student student = new Student(name,sex,g);
SqlUtil.doSqlWork(mapper -> {
int i = mapper.addStudent(student);
if(i>0){
System.out.println("录入成功!");
log.info("新添加一条学生信息"+student);
}else{
System.out.println("录入失败,请重试!");
}
});
}
//插入书
private static void addBook(Scanner scanner){
System.out.print("请输入书名:");
String title = scanner.nextLine();
System.out.print("请输入书籍简介:");
String desc = scanner.nextLine();
System.out.print("请输入书的价格");
String price = scanner.nextLine();
double p= Double.parseDouble((price));

//创建书籍对象
Book book = new Book(title,desc,p);

//匿名内部类其实就是在new的时候,直接对接口或是抽象类的实现
//lambda表达式其实就是我们接口匿名实现的简化
//(传入doSqlWork方法的参数)->{实现接口方法的方法体}
SqlUtil.doSqlWork(mapper -> {
int i = mapper.addBook(book);
if(i>0){
System.out.println("录入成功!");
log.info("新添加了一条书籍信息"+book);
}else{
System.out.println("录入失败,请重试!");
}
});
}

}

SqlUtil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package book.manage.sql;

import book.manage.mapper.BookMapper;
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.util.function.Consumer;

/***
@author Wahoyu*
/
public class SqlUtil {

private SqlUtil(){}

private static SqlSessionFactory factory;

//静态代码块只执行一次,创建一次factory
static {
try {
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));
}
catch (IOException e) {
e.printStackTrace();
}
}

//Consumer是lambda表达式中的一个接口
public static void doSqlWork(Consumer<BookMapper> consumer){
try(SqlSession sqlSession = factory.openSession(true)){
BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
consumer.accept(bookMapper);
}
}
}

mapper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package book.manage.mapper;

import book.manage.entity.Book;
import book.manage.entity.Student;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

public interface BookMapper {

@Insert("insert into student(name,sex,grade) value(#{name},#{sex},#{grade})")
int addStudent(Student student);

@Insert("insert into book(title,`desc`,price) value(#{title},#{desc},#{price})")
int addBook(Book book);

@Select("select* from student where sid = #{sid}")
Student getStudentBySid(int sid);

@Select("select* from book where bid = #{bid}")
Book getBookByBid(int bid);

@Insert("insert into borrow(sid,bid) values(#{sid},#{bid})")
int addBorrow(@Param("sid")int sid,@Param("bid")int bid);

}

mybaits.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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>
<settings>
<!--配置日志管理系统-->
<!-- <setting name="logImpl" value="STDOUT_LOGGING"/>-->
<setting name="logImpl" value="NO_LOGGING" />
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/book_manage"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="book.manage.mapper.BookMapper"/>
</mappers>
</configuration>

logging.properties

1
2
3
4
5
6
7
8
9
10
11
12
#设置输出级别
#不用控制台打印,用文件打印
handlers= java.util.logging.FileHandler
.level= INFO

#命名输出文件
java.util.logging.FileHandler.pattern=console.log
#设置输出格式为默认
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
#追加打印
java.util.logging.FileHandler.append=true

Student

1
2
3
4
5
6
7
8
9
10
11
12
package book.manage.entity;

import lombok.Data;

@Data
public class Student {
int sid;
final String name;
final String sex;
final int grade;
}

Book

1
2
3
4
5
6
7
8
9
10
11
12
package book.manage.entity;

import lombok.Data;

@Data
public class Book {
int bid;
final String title;
final String desc;
final double price;
}

查询全部借阅信息

查询操作就需要创建实体类,封装成一个类,不然没办法查。

在接口中定义<List>实体类

前面的插入数据的时候可以用int 进行创建对象

封装一个查询对象

borrow实体类

1
2
3
4
5
6
7
8
9
10
11
package book.manage.entity;

@Data
public class Borrow {
int id;

//做个映射
Student student;
Book book;
}

使用TestMain进行测试

好处:

①不会真正对数据库进行操作

②不会真正对日志进行改写

TestMain.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.test;

import book.manage.sql.SqlUtil;
import org.junit.jupiter.api.Test;

public class MainTest {

@Test
public void test1(){
SqlUtil.doSqlWork(mapper -> {
mapper.getBorrowList().forEach(System.out::println);
});
}
}

报错1:“sex”无法与“女”进行对应

解决方法:①给book和student都加上@NoArgsConstructor,去掉成员属性中的final,加上一个不带主键的构造方法。

②给borrow加上@Data

测试结束后给主方法添加

1
2
3
4
5
6
7
8
private static void showBorrow(Scanner scanner){
//不输入直接显示全部
SqlUtil.doSqlWork(mapper ->{
mapper.getBorrowList().forEach(borrow -> {
System.out.println(borrow.getStudent().getName()+" -> "+borrow.getBook().getTitle());
});
});
}

查询全部的书籍和学生信息

编写mapper

1
2
3
4
5
@Select("select * from student")
List<Student> getStudentList();

@Select("select* from book")
List<Book> getBookList();

在TestMain中测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//三重借阅
@Test
public void test1(){
SqlUtil.doSqlWork(mapper -> {
mapper.getBorrowList().forEach(System.out::println);
});
}
@Test
public void test2(){
SqlUtil.doSqlWork(mapper -> {
mapper.getStudentList().forEach(System.out::println);
});
}
@Test
public void test3(){
SqlUtil.doSqlWork(mapper -> {
mapper.getBookList().forEach(System.out::println);
});
}

报错:在执行插入借阅关系后总是会默认打印出所有的借阅关系?

原因:因为在case3后面并没有执行finally{break;},在没有case4的时候,case3会自动结束,但是如果有了case 4,case 3就不会结束,自动运行case4。

疑问:为什么在查看所有借阅关系上面要明确对应出每一个人和书籍

原因:因为借阅关系存入的时候存的是学号,但是显示出来的是姓名和书名。所以我们要通过号来查到对象,然后通过对象获取到名字。

项目打包

打包成jar文件

在Maven中配置打包插件 (plugin放在build-plugins标签中)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>com.test.Main</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

JUnit5的插件也要拿过来,因为版本太新,尚未兼容

1
2
3
4
5
6
7
8
9
10
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<!-- JUnit 5 requires Surefire version 2.22.0 or higher -->
<version>2.22.0</version>
</plugin>
</plugins>
</build>

执行打包package

运行jar文件

1
java -jar 拖动文件到此

报错:无法加载主类com.test.Main

解决:属于路径错误问题。Main前面应该指定正确的路径,比如如果Main文件是package book.manage;

那么路径就应该写book.manege.Main

关于运行jar包的日志文件生成

windows默认在C:\Users\用户名\

后续相关优化

1.改变日志生成位置

如果想改的话可以改logging.properties文件中的

1
2
#命名输出文件
java.util.logging.FileHandler.pattern=C\\Users\\64570\\Desktop\\console.log

就会默认输出到桌面了。

2.给书籍价格加上小数点

原因:是书籍的数据库表格建立的问题,给price加上小数点就OK了


基于Mybatis的图书管理系统
http://wahoyu.xyz/2022/06/27/基于Mybatis-JUL-Lombok-Maven的图书管理系统(带单元测试)/
作者
Wahoyu
发布于
2022年6月27日
许可协议