基于Maven+ SpringMVC+ MyBatis +Druid+MySql员工管理系统新手教程


基于Maven+ SpringMVC+ MyBatis +Druid+MySql员工管理系统新手教程

前言

百度上搜这个还能跳出来个100+RMB的课程。这课有好多人看。我看了一下目录,发现大概比我的系统全面。但是,如果你只是要使用这些工具搭建一个员工管理系统,看我的就够了。虽然我只是个新手,但是我实现了,并且尽我所能讲清楚代码。

项目目标

我们搭建的员工管理系统是要有员工,部门和员工,部门的关系和对前三者在应用场景下的增删改查。

具体来说,对于员工和部门的增删改查是较简单的。只需注意增时要保证员工或部门不存在,删时保证员工或部门存在,改时保证员工或部门存在并且查时能用名字或id查找。对于两者关系的增删改查稍微变了些东西。查的时候通过部门名字查找所有该部门的员工信息,包括员工个数并且这些员工和部门要存在且id一致。

工具介绍

Maven是用来下各种各样jar包的。

SpringMVC

这是个设计框架。主要由模块层(Model),视图层(View)和控制层(controller)组成。

模块层封装了数据与进行数据进行处理的代码,是实际经行数据处理的地方,也是与数据库交互的地方。我的代码里包括Service,ServiceImpl和Mapper来处理数据。其实,在写这个项目时,大可不必分这么多层,会显得徒增代码量,但是这是个要养成的好习惯,至少要知道分层思想。

视图层负责将应用显示给用户和显示模型的状态。这主要是前端干的事,和我后端无瓜。我们的视图由Navicat解决。

控制层负责视图和模型之间的交互,把用户的请求发送到对应的模型上,并把模型的改变及时反应到视图上。

Mysql

这是个数据库。结构从上到下依次是数据库(database)和表(table)

Mybatis

我们java程序中存储的数据是对象,怎么把对象转化成mysql中的表呢?靠的就是mybatis。

Druid

这是一个数据传输时的连接池。用这个是为了数据传输时加速与省消耗。以前每次连接数据库都要建立联系,就像每次回家,都要买辆车;如果用连接池,它会把我们数据传输时用的放进去,要用时从里面拿就行了,就像共享单车。

具体的点之后和代码一起补充。

我使用的是IDEA(编程),Postman(调用接口)和Navicat(mysql数据可视化)。

其中Navicat还提供直接用Mysql命令操作数据库的功能。菜单里点击Query,再点击newQuery,就能直接写Mysql语句来验证程序里的命令是否正确。非常方便。

开始制作

创建Mysql表

如果你是在本地玩,需要在本地先起一个Mysql服务。我下了个5开头的版本。

https://dev.mysql.com/downloads/file/?id=506120

在Navicat上连接Mysql。先创建个Mysql的数据库。记住你的数据库名,之后要用。

Pom.xml

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

org.springframework.boot

spring-boot-starter-parent

2.5.2

com.vue

mybatis_druid_mysql

0.0.1-SNAPSHOT

mybatis_druid_mysql

mybatis_druid_mysql

1.8

org.springframework.boot

spring-boot-starter-data-jpa

org.mybatis.spring.boot

mybatis-spring-boot-starter

org.springframework.boot

spring-boot-starter-web

mysql

mysql-connector-java

runtime

org.mybatis.spring.boot

mybatis-spring-boot-starter

1.3.2

com.alibaba

druid-spring-boot-starter

1.1.14

org.projectlombok

lombok

org.springframework.boot

spring-boot-maven-plugin

启动类

@Service

@SpringBootApplication

@MapperScan("/com.mybatis_druid_mysql.demo.Mapper")

publicclassDemoApplication{

publicstaticvoidmain(String[]args){

SpringApplication.run(DemoApplication.class,args);

   }

}

与以往不同,多了个@MapperScan。它在用到java配置时登记Mybatis的Mapper接口。

Druid连接

就像上文提及,druid像共享单车。那么我们就要规定各项指标,比如有多少单车,每条车可以骑多久等等。

application.properties配置文件

server.port=18001

spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.druid.url=(1)

spring.datasource.druid.username=root

spring.datasource.druid.password=password

spring.datasource.druid.initial-size=5

spring.datasource.druid.max-active=20

spring.datasource.druid.min-idle=5

spring.datasource.druid.max-wait=60000

spring.datasource.druid.timeBetweenEvictionRunsMillis=60000

spring.datasource.druid.minEvictableIdleTimeMillis=300000

spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize=200

第一行是我们程序的端口。

(1) 这里是你的mysql数据库的url,形如:jdbc:mysql://127.0.0.1/db_example?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false

其中,db_example是数据库的名字。?后面代表一些参数。

第五行开始就是一些各种各样的druid设置。

怎么样让程序调用上这些配置呢?

packagecom.mybatis_druid_mysql.demo.Druid;

importcom.alibaba.druid.pool.DruidDataSource;

importorg.springframework.beans.factory.annotation.Value;

importorg.springframework.boot.SpringBootConfiguration;

importorg.springframework.context.annotation.Bean;

importorg.springframework.context.annotation.PropertySource;

importorg.springframework.transaction.annotation.EnableTransactionManagement;

importjavax.sql.DataSource;

@SpringBootConfiguration

@PropertySource(value="classpath:/application.properties")

@EnableTransactionManagement

publicclassDruid{

@Value("${spring.datasource.druid.username}")

privateStringuserName;

@Value("${spring.datasource.druid.password}")

privateStringpassword;

@Value("${spring.datasource.druid.driver-class-name}")

privateStringdriverName;

@Value("${spring.datasource.druid.url}")

privateStringurl;

@Value("${spring.datasource.druid.initial-size}")

privateintinitialSize;

@Value("${spring.datasource.druid.max-active}")

privateintmaxActive;

@Value("${spring.datasource.druid.min-idle}")

privateintminIdle;

@Value("${spring.datasource.druid.max-wait}")

privateintmaxWait;

@Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")

privateinttimeBetweenEvictionRunsMillis;

@Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")

privateintminEvictableIdleTimeMillis;

@Value("${spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize}")

privateintmaxPoolPreparedStatementPerConnectionSize;

@Bean(name="datasource")

publicDataSourcedataSource(){//由druid-spring-boot-starter实现springboot提供的datasource接口

DruidDataSourcedataSource=null;

try{

dataSource=newDruidDataSource();

dataSource.setUsername(userName);

dataSource.setPassword(password);

dataSource.setUrl(url);

dataSource.setDriverClassName(driverName);

dataSource.setInitialSize(initialSize);

dataSource.setMaxActive(maxActive);

dataSource.setMinIdle(minIdle);

dataSource.setMaxWait(maxWait);

dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);

dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);

dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);

}catch(Exceptione){

e.printStackTrace();

       }

returndataSource;

   }

}

@SpringBootConfiguration注释是一个类级别的注释,它指示此类提供了应用程序配置。

@PropertySource(value = "classpath:/application.properties")注释是一个读取properties配置文件属性的注释,塔指示了配置文件地址。

@EnableTransactionManagement注释是一个事务管理注解。

@Value注释是一个从属性源取值的注释,它后面写注释源的名称。

上面代码设置了dataSource。之后就会交给druid-spring-boot-starter这个依赖处理。

这样,Druid就连接完成啦!

Entity创建

实体(Entity)定义了我们数据的基本结构。比如,我们这里要做3张表:员工信息表,部门信息表和员工部门关系表。

Employee

@Entity(name="employee")

@Data

publicclassEmployee{

@Id

Longid;

Stringname;

Stringsex;

Stringaddress;

}

@Entity(name = "employee")注释说明这是一个实体,而且名字叫employee。

@Data注释说明这是一个数据,它代替了普通的getter和setter。

@Id注释说明这是这个实体的主键(primary key)。

Department

@Entity(name="department")

@Data

publicclassDepartment{

@Id

Longdepartment_id;

Stringdepartment_name;

}

Connection

@Entity(name="connection")

@Data

publicclassConnection{

@Id

Longconnection_id;

Longdepartment_id;

Longemployee_id;

Stringemployee_name;

Stringdepartment_name;

}

数据处理

从端口调用接口到实现数据的增上改查(CRUD),是怎么实现的呢?

我的总共个分为这么几层:Controller->Service->ServiceImpl->Mapper。正规一点还要加上一层Dao层。

Controller

@RestController

@RequestMapping("/jerry")

@Slf4j

publicclassController{

@Autowired

privateServiceservice;

@PostMapping(value="/insertEmployee",produces=MediaType.APPLICATION_JSON_UTF8_VALUE,

consumes=MediaType.APPLICATION_JSON_UTF8_VALUE)

publicEmployeeinsertEmployee(@RequestBodyEmployeeemployee){

if(service.countEmployee(employee)==0)

service.insertEmployee(employee);

returnemployee;

   }

@DeleteMapping("/deleteEmployee")

publicStringdeleteEmployee(@RequestParamStringid){

service.deleteEmployee(id);

return"deleted";

   }

@PutMapping(value="/updateEmployee",produces=MediaType.APPLICATION_JSON_UTF8_VALUE,

consumes=MediaType.APPLICATION_JSON_UTF8_VALUE)

publicEmployeeupdateEmployee(@RequestParamLongid,@RequestBodyEmployeeemployee){

employee.setId(id);

service.updateEmployee(employee);

log.info("result: {}",employee);//打印日志

returnemployee;

   }

@GetMapping(value="/searchEmployee",produces=MediaType.APPLICATION_JSON_UTF8_VALUE)

publicEmployeesearchEmployee(@RequestParam("id")Stringid){

returnservice.searchEmployee(id);

   }

@PostMapping(value="/addDepartment",produces=MediaType.APPLICATION_JSON_UTF8_VALUE,

consumes=MediaType.APPLICATION_JSON_UTF8_VALUE)

@ResponseBody

publicDepartmentaddDepartment(@RequestBodyDepartmentdepartment){

if(service.countDepartment(department)==0)

service.addDepartment(department);

returndepartment;

   }

@DeleteMapping("/deleteDepartment")

@ResponseBody

publicStringdeleteDepartment(@RequestParamLongdepartment_id){

service.deleteDepartment(department_id);

return"deleted";

   }

@GetMapping(value="/searchDepartment",produces=MediaType.APPLICATION_JSON_UTF8_VALUE)

publicDepartmentsearchDepartment(@RequestParam("department_id")Stringdepartment_id){

log.info("result: {}",service.searchDepartment(department_id));//打印日志

Departmentdepartment=service.searchDepartment(department_id);

returnservice.searchDepartment(department_id);

   }

@PutMapping(value="/updateDepartment",produces=MediaType.APPLICATION_JSON_UTF8_VALUE,

consumes=MediaType.APPLICATION_JSON_UTF8_VALUE)

@ResponseBody

publicDepartmentupdateDepartment(@RequestParamLongdepartment_id,@RequestBodyDepartmentdepartment){

department.setDepartment_id(department_id);

service.updateDepartment(department);

returndepartment;

   }

@RequestMapping(value="/addConnection",produces=MediaType.APPLICATION_JSON_UTF8_VALUE,

consumes=MediaType.APPLICATION_JSON_UTF8_VALUE)

@ResponseBody

publicConnectionaddConnection(@RequestBodyConnectionconnection){

if(service.countConnection(connection)==0&&(service.employeeExistAndIdCorrect(connection)==1

&&(service.departmentExistAndIdCorrect(connection)==1)))

service.addConnection(connection);

returnconnection;

   }

@RequestMapping("/deleteConnection")

@ResponseBody

publicStringdeleteConnection(Longconnection_id){

service.deleteConnection(connection_id);

return"deleted";

   }

@RequestMapping("/deleteEmployeeFromDepartment")

@ResponseBody

publicStringdeleteEmployeeFromDepartment(@RequestParam("employee_id")Longemployee_id,@RequestParam("department_id")Longdepartment_id){

service.deleteEmployeeFromDepartment(employee_id,department_id);

return"deleted";

   }

@RequestMapping("/updateConnection")

@ResponseBody

publicStringupdateConnection(@RequestBodyConnectionconnection,@RequestParamLongconnection_id){

connection.setConnection_id(connection_id);

service.updateConnection(connection);

return"updated";

   }

@RequestMapping("/searchEmployeeFromDepartment")

@ResponseBody

publicListsearchEmployeeFromDepartment(Stringdepartment_name){

returnservice.searchEmployeeFromDepartment(department_name);

   }

@RequestMapping("/searchDepartmentFromEmployee")

@ResponseBody

publicListsearchDepartmentFromEmployee(Stringemployee_name){

returnservice.searchDepartmentFromEmployee(employee_name);

   }

@RequestMapping("/countEmployeeFromDepartment")

@ResponseBody

publicLongcountEmployeeFromDepartment(Stringdepartment_name){

returnservice.countEmployeeFromDepartment(department_name);

   }

}

@RestController注释是一个连接了两种其他注释的注释,他们分别是@Controller@ResponseBody,前者告诉系统这是个控制类,并且暗含@Component能被系统扫描到;后者告诉系统这里返回的数据以网络反应体(web response body)展示的,不是普通的返回数据。

@RequestMapping("/jerry")注释是一个引导网络请求到请求处理类的注释,它定义了调用接口时的路径。

@Slf4j注释是一个来自lombok依赖的注释,他能打印日志。

@Autowired注释是一个自动导航的注释,它能把@Component注释下的类纳为己用。

@PostMapping注释是@RequestMapping的一种,它要求请求时用Post方法调用。这种方法通常用在增删改查中的

@DeleteMapping注释是@RequestMapping的一种,它要求请求时用Delete方法调用。这种方法通常用在增删改查中的

@PutMapping注释是@RequestMapping的一种,它要求请求时用Put方法调用。这种方法通常用在增删改查中的

@GetMapping注释是@RequestMapping的一种,它要求请求时用Get方法调用。这种方法通常用在增删改查中的

以上四者都能定义请求调用时的路径和输入输出的数据形式。比如Json形式,方便观察。

我代码中前面还是挺规范,到后面全是@RequestMapping,这不好。

@RequestParam@RequestBody都是定义网络请求参数名称,前者参数是普通参数(parameter),比如String或Long;后者参数是一个类。

我代码中前面还是挺规范,到后面全都漏了,这不好。

你可能注意到了,我的所有实体的属性都是小写,单词之间由下划线分开。比如department_name。这是因为如果不这么做,Mysql会自动把这些属性转化成表中的Title时,自动变小写,单词用下划线分开。按理说分开就分开,但是我试了一下,会引起一个我搞不懂的bug。于是就向Mysql妥协。

Service

这就是个接口类,由ServiceImpl实现。

@org.springframework.stereotype.Service

publicinterfaceService{

publicvoidinsertEmployee(Employeeemployee);

publicLongcountEmployee(Employeeemployee);

publicvoiddeleteEmployee(Stringid);

publicvoidupdateEmployee(Employeeemployee);

publicEmployeesearchEmployee(Stringid);

publicvoidaddDepartment(Departmentdepartment);

publicLongcountDepartment(Departmentdepartment);

publicvoiddeleteDepartment(Longdepartment_id);

publicDepartmentsearchDepartment(Stringdepartment_id);

publicvoidupdateDepartment(Departmentdepartment);

publicvoidaddConnection(Connectionconnection);

publicLongcountConnection(Connectionconnection);

publicvoiddeleteConnection(Longconnection_id);

publicvoidupdateConnection(Connectionconnection);

publicListsearchEmployeeFromDepartment(Stringdepartment_id);

publicListsearchDepartmentFromEmployee(Stringemployee_name);

publicLongcountEmployeeFromDepartment(Stringdepartment_name);

publicvoiddeleteEmployeeFromDepartment(Longemployee_id,Longdepartment_id);

publicLongemployeeExistAndIdCorrect(@Param("connection")Connectionconnection);

publicLongdepartmentExistAndIdCorrect(@Param("connection")Connectionconnection);

}

@Service注释是一个指明该类是个独立服务层的注释,它是@Component的一种,能被扫描到

这里它变成@org.springframework.stereotype.Service这么长,是因为我的类名也是Service,系统为了区分,就写长了。

ServiceImpl

@org.springframework.stereotype.Service

@Transactional

publicclassServiceImplimplementsService{

@Autowired

privateMappermapper;

publicvoidinsertEmployee(Employeeemployee){

mapper.insertEmployee(employee);

   }

publicLongcountEmployee(Employeeemployee){

returnmapper.countEmployee(employee);

   }

publicvoiddeleteEmployee(Stringid){

mapper.deleteEmployee(id);

   }

publicvoidupdateEmployee(Employeeemployee){

mapper.updateEmployee(employee);

   }

publicEmployeesearchEmployee(Stringid){

returnmapper.searchEmployee(id);

   }

publicvoidaddDepartment(Departmentdepartment){

mapper.addDepartment(department);

   }

publicLongcountDepartment(Departmentdepartment){returnmapper.countDepartment(department);}

publicvoiddeleteDepartment(Longdepartment_id){

mapper.deleteDepartment(department_id);

   }

publicDepartmentsearchDepartment(Stringdepartment_id){

returnmapper.searchDepartment(department_id);

   }

publicvoidupdateDepartment(Departmentdepartment){

mapper.updateDepartment(department);

   }

publicvoidaddConnection(Connectionconnection){

mapper.addConnection(connection);

   }

publicLongcountConnection(Connectionconnection){returnmapper.countConnection(connection);}

publicvoiddeleteConnection(Longconnection_id){

mapper.deleteConnection(connection_id);

   }

publicvoidupdateConnection(Connectionconnection){

mapper.updateConnection(connection);

   }

publicListsearchDepartmentFromEmployee(Stringemployee_name){

returnmapper.searchDepartmentFromEmployee(employee_name);

   }

publicListsearchEmployeeFromDepartment(Stringdepartment_name){

returnmapper.searchEmployeeFromDepartment(department_name);

   }

publicLongcountEmployeeFromDepartment(Stringdepartment_name){

returnmapper.countEmployeeFromDepartment(department_name);

   }

publicvoiddeleteEmployeeFromDepartment(Longemployee_id,Longdepartment_id){

mapper.deleteEmployeeFromDepartment(employee_id,department_id);

   }

publicLongemployeeExistAndIdCorrect(@Param("connection")Connectionconnection){

returnmapper.employeeExistAndIdCorrect(connection);

   }

publicLongdepartmentExistAndIdCorrect(@Param("connection")Connectionconnection){

returnmapper.departmentExistAndIdCorrect(connection);

   }

}

@Transactional注释是一个为独立方法或一个类的转换属性的注释。

Mapper

使用Mysql命令,用于将对象与Mysql里的表打交道。

@org.apache.ibatis.annotations.Mapper

publicinterfaceMapper{

@Insert("insert into employee(name,sex,address) values(#{name},#{sex},#{address})")

@Options(useGeneratedKeys=true,keyColumn="id")//展示id

voidinsertEmployee(Employeeemployee);

@Select("select count(*) from employee where name = #{name}")

LongcountEmployee(Employeeemployee);

@Delete("delete from employee where id = #{id}")

voiddeleteEmployee(@Param("id")Stringid);

@Update("update employee set name = #{name},sex = #{sex},address = #{address} where id = #{id}")

@Options(useGeneratedKeys=true,keyColumn="id")

voidupdateEmployee(@Param("employee")Employeeemployee);

@Select("select * from employee where id = #{id}")

@Options(useGeneratedKeys=true,keyColumn="id")

EmployeesearchEmployee(@Param("id")Stringid);

@Insert("insert into department(department_name) values(#{department_name})")

@Options(useGeneratedKeys=true,keyProperty="department_id")

voidaddDepartment(Departmentdepartment);

@Select("select count(*) from department where department_name = #{department_name}")

LongcountDepartment(Departmentdepartment);

@Delete("delete from department where department_id = #{department_id}")

voiddeleteDepartment(@Param("department_id")Longdepartment_id);

@Select("select * from department where department_id = #{department_id}")

@Options(useGeneratedKeys=true,keyProperty="department_id")

DepartmentsearchDepartment(@Param("department_id")Stringdepartment_id);

@Update("update department set department_id = #{department_id}, department_name = #{department_name} where department_id = #{department_id}")

@Options(useGeneratedKeys=true,keyColumn="department_id",keyProperty="department_id")

voidupdateDepartment(@Param("department")Departmentdepartment);

@Insert("insert into connection(department_id,department_name,employee_id,employee_name)values(#{department_id},#{department_name},#{employee_id},#{employee_name})")

@Options(useGeneratedKeys=true,keyColumn="connection_id",keyProperty="connection_id")

voidaddConnection(Connectionconnection);

@Select("select count(*) from connection where department_name = #{department_name} and employee_name = #{employee_name}")

LongcountConnection(Connectionconnection);

@Delete("delete from connection where connection_id = #{connection_id}")

voiddeleteConnection(Longconnection_id);

@Delete("delete from connection where employee_id = #{employeeId} and department_id = #{department_id}")

voiddeleteEmployeeFromDepartment(@Param("employeeId")Longemployee_id,

@Param("department_id")Longdepartment_id);

@Update("update connection set employee_id = #{employee_id}, employee_id = #{employee_id}, department_id = #{department_id} , employee_name = #{employee_name} where connection_id = #{connection_id}")

@Options(useGeneratedKeys=true,keyColumn="connection_id",keyProperty="connection_id")

voidupdateConnection(Connectionconnection);

@Select("select * from connection where employee_name = #{employee_name}")

@Options(useGeneratedKeys=true,keyColumn="employee_name",keyProperty="employee_name")

ListsearchDepartmentFromEmployee(Stringemployee_name);

@Select("select * from connection where department_name = #{department_name}")

@Options(useGeneratedKeys=true,keyColumn="department_name",keyProperty="department_name")

ListsearchEmployeeFromDepartment(@Param("department_name")Stringdepartment_name);

@Select("select count(*) from connection where department_name = #{department_name}")

publicLongcountEmployeeFromDepartment(@Param("department_name")Stringdepartment_name);

@Select("select count(*) from employee where name = #{employee_name} and id = #{employee_id}")

publicLongemployeeExistAndIdCorrect(@Param("connection")Connectionconnection);

@Select("select count(*) from department where department_name = #{department_name} and department_id = #{department_id}")

publicLongdepartmentExistAndIdCorrect(@Param("connection")Connectionconnection);

}

以上括号内的语句就是Mysql命令,其相关意思网上多的很,我就不解释了。就算是新人也能通过字面意思与上文结合,猜知大概。

Navicat使用

其实这里应该与代码测试穿插着用,上文到这不是真正开始制作的顺序。

如果ENtity内写了表名,那么在最开始船舰的数据库下就会自动生成相应的表,以及该表的第一列;若没有,则手动生成。

在每次调用接口测试代码时,它会要求每张表都要有自增主键。于是,我们要右键表名,选择“Design Table”,选择相应主键,勾选自增(Auto-Increment)。

它刷新时间意不要用右键选择刷新,而要用底部的刷新键,或是重启Navicat。

注意事项

除了上文所提及的各种事项,我再整理一些修理bug的总结。

各种依赖包内的类别引用错了,很多都是同名的,注意选择你想要的。假如你和我一样对理论不熟悉,就要点开(“ctrl" + 左键)这些类一看究竟。有时他甚至不会一下显示这些同名的类,坑的我好惨。

写代码要规范,养成好习惯。有许多现在看来可写可不写的,那是因为我们程序功能简单。以后功能复杂起来,再不写规范,那就是一坨屎,不仅没人想看,自己过几天也不想看。

你可能感兴趣的:(基于Maven+ SpringMVC+ MyBatis +Druid+MySql员工管理系统新手教程)