前言
大三暑假有个毕业实习,是企业的人来我们学校带我们做项目,为期三周。
前半部分是教我们一些框架的基本使用,后面几天就是组成小组利用前面学习的知识开发一个商城项目。感觉和课设其实没有差的太多。
学习到的框架有 mybatis、springmvc、spring、springboot、shiro、mybatis-plus,还在一开始学习了如何建立 maven 项目。
2020 年 7 月 15 日,我们小组完成了答辩。在之前因为要学习框架、做项目,甚至中间夹杂着几场考试,所以抽不出空来写博客,现在结束了,我现在通过 git 提交记录以及幕布学习笔记、工作日报等记录来尝试还原这一次经历以及学习到的技术。
一开始想的是本系列先整理学习笔记,最后对本次项目进行总结。不过碍于时间不足,可能会直接将以前的旧项目改为 springboot 架构,实习最终完成的商城项目就不进行分析了,项目开源链接:SpringBootMall
笔者也是刚刚才学习这些东西,所以如果有问题可以给我留言。
仓库链接
本文的练习项目已在码云开源,链接如下:
导入方式见本系列的第一篇文章。
mybatis 框架的基本使用
mybatis 官网文档
什么是 mybatis
下面是 mybatis 官网的说明:
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
它可以将 sql 语句和业务代码分开,从而方便修改。
本学期我做的另外几个课设中,sql 语句都是直接写在代码中的,类似于下面这样:
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
|
public static boolean addExam(ExamBean exam) { SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd"); String date =sdf.format(exam.getDate()); String applyDate = sdf.format(exam.getApplyDate()); String sql = String.format("insert into " + "exam(exam_id,term_id,course_id,exam_wl,exam_date,location,period,teacherNeed,exam_status,`comment`,teacher_id,apply_date,refuse_reason)" + "values('%s','%s','%s',%d,'%s','%s','%s',%d,'%s','%s','%s','%s','%s');" ,exam.getExamNo() ,exam.getTerm() ,exam.getCourseNo() ,exam.getWorkload() ,date ,exam.getLocation() ,exam.getTime() ,exam.getNeed() ,exam.getStatus() ,exam.getComment() ,exam.getApplierNo() ,applyDate ,exam.getRefuseReason() );
boolean success = databaseBean.executeUpdate(sql); if(!success) return false;
String[] classNoList = exam.getClassNo(); if(classNoList!=null) { for(String classNo :classNoList) { sql = String.format("insert into examclasses(exam_id,class_id)" + "values('%s','%s');" , exam.getExamNo(),classNo); success = databaseBean.executeUpdate(sql); if(!success) return false; } }
InvigilatorBean[] invigilatorList = exam.getInvigilator(); if(invigilatorList!=null) { boolean isMain = true; for(InvigilatorBean invigilator:invigilatorList) { success = InvigilatorBean.arrange(exam.getExamNo(), invigilator.getStaffNo(), isMain); isMain=false; if(!success) return false; } }
return true; }
|
这是考务管理系统的 ExamBean 类中的内容(jsp+servlet+javabean 的架构,将业务逻辑写在了 javabean 中),可以看到,它需要手动进行日期的转换、多表查询的 sql 语句的拼接,而且 sql 语句是直接写在代码里面的,如果需要改动 sql 语句,会非常麻烦。
想要连接数据库,还需要自己写一个 DatabaseBean 来将数据库的用户名、密码、驱动名写在里面,提供获取数据库连接的类方法。
而使用了 mybatis 之后,sql 语句写在专门的 xml 文件当中,代码和 sql 语句分开,更方便管理。下文会展示如何简单地使用它,不作深入讲解(哈哈,因为更深的我也还不会)。
配置 mybatis
添加 mybatis 依赖
前往maven 中央仓库的官网,搜索 mybatis,选择版本,并将其依赖的配置代码添加到pom.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 26 27 28 29 30 31 32 33 34
| <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>com.test</groupId> <artifactId>TestMaven</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> </dependencies> </project>
|
文件保存后,等待 eclipse 把这个依赖自动下载到本地仓库,就可以使用 mybatis 了。
新建 mybatis 配置文件
在工程的src/main/resources
目录下新建一个 xml 文件mybatis-config.xml
,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?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="dev"> <environment id="dev"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/test" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/StudentMapper.xml"/> </mappers> </configuration>
|
里面的驱动名(mysql5 以后的驱动名变了,请注意。本例中的 mysql 是 5.1 版本的)、url、用户名和密码都改成自己的。
在同目录下新建一个文件夹mapper
,用于存放 SQL 映射文件。
增删改查举例
以教务管理系统的学生信息的增删改查为例。本地 mysql 的test
数据库中的student
表中存放着学生信息,grade
表中存放着班级信息。
为了便于讲解,本例中的代码和上文给出的码云仓库的代码可能会有所不同。
目录结构一览
- src/main/java
- com.test
- bean
- Student.java:用于存储学生信息的 javabean
- utils
- MybatisUtils.java:用于获取 SqlSession 的工具类
- dao
- StudentMapper.java:用于处理对学生信息的
- src/main/resource
- mapper
- StudentMapper.xml:SQL 映射文件,SQL 语句写在这里
- mybatis-config.xml:mybatis 配置文件
新建包
在你自己的工程包下(这里以com.test
为例),新建一个bean
包,存放的就是 javabean,用于将数据库的数据封装为类。
在其中新建一个学生 javabean,名为Student
,其属性与数据库的学生信息表的字段一一对应(比如表中的sname
字段对应 javabean 的sname
属性)。
utils 包存放一些工具类或者说是公共类。在此包下新建MybatisUtils
类,内容如下:
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 com.test.utils;
import java.io.IOException; import java.io.Reader;
import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MybatisUtils {
private static SqlSessionFactory factory=null;
static{ Reader reader; try { reader = Resources.getResourceAsReader("mybatis-config.xml"); factory = new SqlSessionFactoryBuilder().build(reader); } catch (IOException e) { e.printStackTrace(); } }
public static SqlSession getSqlSession(){ return factory.openSession(); }
public static void close(SqlSession sqlSession){ if(sqlSession!=null){ sqlSession.close(); } }
}
|
这里涉及到 mybatis 如何进行 sql 映射,来看看官网文档是怎么说的:
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。
SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。
而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
这个类在静态块里面读取了我们刚刚写的配置文件,并将其传给SqlSessionFactoryBuilder
以创建SqlSessionFactory
对象。
这个类的主要作用就是读取 mybatis 配置文件,并提供获取SqlSession
对象的静态方法。我们可以使用SqlSession
对象的方法,来使用 sql 映射文件中的 sql 语句。
再新建一个名为dao
的包,它的含义为 DAO(Data Access Object)数据访问对象,它是这个项目的数据访问层,业务代码通过它对数据库进行访问,有了这一层抽象,业务逻辑和数据访问就分开了。
dao
包中新建一个StudentMapper
类,它用来和 SQL 映射文件产生联系。它的代码如下:
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
| package com.test.dao;
import java.io.IOException;
import org.apache.ibatis.session.SqlSession;
import com.bean.Student; import com.utils.MybatisUtils;
public class StudentMapper {
public static int save(Student student) throws IOException{ SqlSession session = MybatisUtils.getSqlSession(); int flag = session.insert("saveStudent",student); session.commit(); MybatisUtils.close(session); return flag; }
public static int update(Student student) throws IOException{ SqlSession session = MybatisUtils.getSqlSession(); int flag = session.update("updateStudent",student); session.commit(); MybatisUtils.close(session); return flag; }
public static int delete(int id) throws IOException{ SqlSession session = MybatisUtils.getSqlSession(); int flag = session.delete("deleteStudent",id); session.commit(); MybatisUtils.close(session); return flag; }
}
|
可以看到,这里面调用了SqlSession
对象的三个方法insert
、update
和delete
,并且第一个参数是一个字符串,第二个参数就是操作的对象,操作对象中打包了用于增删改数据库的参数。
由于查询相比增删改要复杂一些,所以先写增删改的内容,等会儿再加上查询的代码。
操作完成后,使用 commit 方法进行事务提交,并关闭 SqlSession 对象。
那么第一个参数是什么呢?是 SQL 映射文件里面的 sql 语句的标识,接下来来看看 sql 映射文件吧~
新建 sql 映射文件
在刚刚新建的mapper
文件夹中新建一个 xml 文件StudentMapper.xml
,大体框架如下
1 2 3 4 5 6 7
| <?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"> <mapper namespace="com.test.dao.StudentMapper">
</mapper>
|
其中,mapper 标签的namespace
的属性值为与此文件绑定的映射类的包括类名在内的包路径,eclipse 按住 ctrl+左键可以跳转到对应的类(如果没有跳转说明可能是拼写错误)
在这里编写 sql 语句:
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 mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.dao.StudentMapper"> <insert id="saveStudent"> insert into student(sname) values(#{sname}) </insert> <update id="updateStudent"> update student set sname=#{sname} where sid=#{sid} </update> <delete id="deleteStudent"> delete from student where sid=#{sid} </delete>
</mapper>
|
其中,标签对应的是 sql 语句所进行的操作,id 则是上文中调用SqlSession
对象的方法时所传入的第一个字符串参数,在 sql 语句中夹杂着这样的内容:#{sname}
,它在映射后会作为一个占位符,类似 JDBC 的预编译。mybatis 会将传入的第二个 Object 参数的属性设置进去。
增改操作传入的都是Student
对象,这个对象的属性就包含sname
。
这就是增删改操作,可以新建一个测试类来测试一下
测试用建表语句
为了简单起见,删除了一些字段
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
| CREATE DATABASE IF NOT EXISTS test;
USE test;
# 删除表 DROP TABLE IF EXISTS student; DROP TABLE IF EXISTS grade;
# 创建班级表 CREATE TABLE grade ( gid INT AUTO_INCREMENT primary key, gname char(30), deleted INT DEFAULT 0# 删除标记 );
# 创建学生信息表 create table student ( sid INT AUTO_INCREMENT primary key, snum char(30),# 学号 sname char(30),# 姓名 deleted INT DEFAULT 0,# 删除标记 gid INT, FOREIGN KEY(gid) REFERENCES grade(gid) );
INSERT INTO grade(gname) VALUES ('软件工程六班'), ('软件工程七班');
insert into student(snum,sname,gid) values ('2017901006','张三',1), ('2017901007','李四',2), ('2017901008','王五',1), ('2017901009','赵六',2), ('2017901026','张三',1);
|
增删改测试
示例测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.test; import com.bean.Student; import com.dao.StudentMapper;
public class TestStudentCRUD {
public static void main(String[] args){ try { Student student = new Student(); student.setSname("张三"); int flag=0; flag = StudentMapper.insert(student);
System.out.println(flag); } catch (Exception e) { e.printStackTrace(); }
} }
|
如果插入成功,那么可以看到控制台输出了 1,这是影响的行数。如果是 0,那么就是插入失败,看一下报错信息,检查一下数据库用户名和密码是否正确,检查一下 javabean 和数据库字段对应情况
查询操作
再创建一个班级信息的 bean,字段如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.bean; import java.io.Serializable; import java.util.*;
public class Grade implements Serializable {
private static final long serialVersionUID = 1L;
private Integer gid; private String gname; private List<Student> list = new ArrayList<Student>(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.bean;
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private Integer sid; private String sname; private Grade grade; }
|
下面的代码都是在StudentMapper.xml
中写的。
单表查询
最简单的方式是单表查询,也就是仅仅查询一个表中的字段,不涉及另一个表。
以下的 sql 映射可以实现单表查询,不过有一定的限制,那就是查询结果中的字段名必须和 resultType 所指定的 javabean 的属性名一一对应,比如说数据库表中的字段名为sname
,那么com.bean.Student
这个 javabean 中的字段也必须有一个sname
,否则会报错。
1 2 3 4 5 6 7 8 9 10 11
| <?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"> <mapper namespace="com.dao.StudentMapper"> <select id="selectStudent" resultType="com.bean.Student" > select * from student </select>
</mapper>
|
结果集映射
如果数据库表字段名和 javabean 的属性名不一样,那么可以使用结果集映射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?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"> <mapper namespace="com.dao.StudentMapper"> <select id="selectStudent" resultMap="StudentMapper" > select * from student </select> <resultMap type="com.bean.Student" id="StudentMapper"> <id property="sid" column="ID"/> <result property="sname" column="Sname"/> </resultMap>
</mapper>
|
select
标签中的resultMap
属性指定所使用的resultMap
标签(结果集标签),resultMap
标签由id
属性所指定。
结果集标签中嵌套了id
标签和result
标签,前者指定结果集中的主键,而后者则是其他普通类型的属性,它们的property
属性对应 javabean 的属性名,column
对应数据库表的列名也就是字段名。
多表查询
如果要实现多表查询,那么就必须使用上述的结果集映射了。多表查询即涉及到多个表的字段的查询,需要进行连接操作。
在本例中,学生表中有一个班级字段,指明学生属于哪个班级。在查询学生的班级名时,需要连接学生表和班级表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?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"> <mapper namespace="com.dao.StudentMapper"> <select id="selectStudent" resultMap="StudentMapper" > select s.*,g.* from student s left join grade g on s.gid= g.gid </select> <resultMap type="com.bean.Student" id="StudentMapper"> <id property="sid" column="sid"/> <result property="sname" column="sname"/> <association property="grade" javaType="com.bean.Grade"> <id property="gid" column="gid"/> <result property="gname" column="gname"/> </association> </resultMap>
</mapper>
|
这里多了一个association
标签,它用于把结果集中的其他表的字段映射到本 javabean 的字段,本例中就是将查询结果中的gid
和gname
字段映射到Student
类的grade
对象的对应属性中。
一对多查询
除了表的字段和类的属性的一一映射之外,有时还需要进行一对多的映射。
比如班级类中有一个列表,想要将所有属于该班级的学生都加入到这个列表当中,那么可以按照上文的方式新建对应的GradeMapper
类和GradeMapper.xml
,在映射文件中这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?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"> <mapper namespace="com.dao.GradeMapper"> <select id="getStudentListById" resultMap="GradeMapper"> select grade.*,student.* from grade left join student on grade.gid=student.gid where grade.gid=#{gid} </select> <resultMap type="com.bean.Grade" id="GradeMapper"> <id property="gid" column="gid" /> <result property="gname" column="gname" /> <collection property="list" ofType="com.bean.Student"> <id property="sid" column="sid"/> <result property="sname" column="sname"/> </collection> </resultMap> </mapper>
|
用的是collection
标签,它的ofType
属性是集合类中所装元素的类型,其他的和上面差不多