请求方式(基于注解实现)
1.编写web.xml文件配置启动信息
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>ssm</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> </init-param> <init-param> <param-name>contextConfigLocation</param-name> <param-value>com.pyb.config.SpringConfig</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>ssm</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
-
文件作用分析
1.Web容器启动: 当Web服务器(如Tomcat)启动,并且加载部署在其中的Web应用时,它会读取web.xml文件来了解如何配置和初始化该应用。 2.解析DOCTYPE声明: Web容器检查web.xml文件的DOCTYPE声明,以确保其符合指定的DTD标准。这一步是验证配置文件格式正确性的过程。 3.创建Servlet实例: 根据<servlet>标签中的定义,Web容器创建名为ssm的Servlet实例。这里使用的是Spring MVC框架的核心控制器DispatcherServlet。 4.初始化Servlet: 在创建Servlet实例后,Web容器调用Servlet的init()方法进行初始化。 DispatcherServlet在初始化过程中会读取<init-param>参数,根据这些参数创建Spring的应用上下文。 它使用AnnotationConfigWebApplicationContext作为上下文类,意味着它将通过注解扫描的方式来查找并注册Bean。 上下文配置的位置被设置为com.pyb.config.SpringConfig,即具体的Java配置类。 5.加载上下文: DispatcherServlet初始化完成后,它会加载由SpringConfig类配置的Spring应用上下文,包括所有必要的Bean定义和服务配置。 6.Servlet加载顺序: 由于设置了<load-on-startup>值为1,所以这个Servlet会在Web应用启动时优先加载,保证应用可以立即响应请求。 7.映射URL模式: <servlet-mapping>定义了哪个Servlet应该处理哪些URL模式的请求。在这个例子中,/表示所有的请求都将由ssm Servlet处理,除了那些静态资源(如图片、CSS文件等),通常它们会有默认的Servlet处理。 8.处理HTTP请求: 一旦Web应用完全启动并且DispatcherServlet已经准备好,它就可以开始接收HTTP请求,并根据Spring MVC的路由规则将请求分发给相应的处理器或控制器进行处理。 处理器完成业务逻辑后,返回模型和视图信息给DispatcherServlet,再由它选择适当的视图技术(如JSP, Thymeleaf等)渲染页面,最终返回HTTP响应给客户端。
2.编写Java核心配置类文件
package com.pyb.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScans; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.EnableWebMvc; /** * @version 1.0 * @Author 彭彦彬 * @Date 2025/1/10 8:47 * @注释 */ @Configuration @ComponentScan("com.pyb") @EnableWebMvc @Import({MyBatisConfig.class,DataBaseConfig.class}) public class SpringConfig { }
-
该类作用
1配置类标识: 使用 @Configuration 注解标记该类为一个Spring配置类,意味着它包含用于定义应用上下文的Bean和其他配置信息。 2组件扫描: 通过 @ComponentScan("com.pyb") 自动扫描 com.pyb 包及其子包内的组件(如服务、控制器等),并将它们注册为Spring容器中的Bean。 3启用Web MVC支持: @EnableWebMvc 启用了Spring MVC的功能,自动配置了处理HTTP请求所需的各种组件。 4导入额外配置: 使用 @Import 注解导入了 MyBatisConfig 和 DataBaseConfig 配置类,以便将数据访问层和MyBatis框架的相关配置分离出来,保持配置的模块化和清晰度。
-
数据源配置类
package com.pyb.config; import com.zaxxer.hikari.HikariDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; import javax.sql.DataSource; /** * @version 1.0 * @Author 彭彦彬 * @Date 2025/1/10 9:10 * @注释 */ @PropertySource("classpath:db.properties") public class DataBaseConfig { @Value("${jdbc.password}") String password; @Value("${jdbc.username}") String username; @Value("${jdbc.url}") String url; @Value("${jdbc.driver}") String driver; @Bean public DataSource dataSource(){ HikariDataSource hikariDataSource=new HikariDataSource(); hikariDataSource.setUsername(username); hikariDataSource.setPassword(password); hikariDataSource.setJdbcUrl(url); hikariDataSource.setDriverClassName(driver); return hikariDataSource; } }
DataBaseConfig 类的主要作用是配置和提供一个高效的数据源给Spring应用。它读取外部化的数据库配置信息,并创建一个优化过的HikariCP连接池实例,这有助于提高数据库操作的性能和可靠性。同时,这种做法也使得数据库配置易于管理和更改,无需修改代码即可适应不同的环境(如开发、测试、生产)。
-
整和Mybatis配置类
package com.pyb.config; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import javax.sql.DataSource; import java.io.IOException; /** * @version 1.0 * @Author 彭彦彬 * @Date 2025/1/10 9:09 * @注释 */ @MapperScan("com.pyb.dao") public class MyBatisConfig { @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException { SqlSessionFactoryBean sqlSessionFactoryBean=new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml")); return sqlSessionFactoryBean; } }
总结来说,MyBatisConfig 类的作用是配置MyBatis框架,使其与Spring应用无缝集成。它实现了以下几点:
1.自动扫描并注册指定包下的所有Mapper接口。 2.创建并配置 SqlSessionFactoryBean,用于生成MyBatis的SQL会话。 3.指定MyBatis映射文件的位置,使得MyBatis能够找到并加载这些文件以执行SQL语句。
3.用以上整合好的配置类,编写对员工信息操作的业务接口
-
建立一张员工信息表
create table emp ( empno int auto_increment comment '雇员编号' primary key, ename varchar(20) null comment '雇员姓名', job varchar(9) null comment '表示工作职位', mgr int null comment '表示一个雇员的领导编号', hiredate datetime null comment '表示雇佣日期', sal double null comment '表示月薪,工资', comm double null comment '表示奖金或佣金', deptno int null );
-
编写实体类映射上面数据库表
@Data public class Emp { private int empno; private String ename; private String job; private int mgr; private double sal; private double comm; private int deptno; }
-
编写要操作员工数据的dao层接口
@Select("select *from emp") List<Emp>findAllUser(); @Insert("insert into emp(empno,ename,job,mgr,sal,comm,deptno)values (#{empno},#{ename},#{job},#{mgr},#{sal},#{comm},#{deptno})") int addEmp(Emp emp); @Delete("delete from emp") int deleteAll(); @Select("select *from emp where empno=#{empNo}") Emp selectEmpByNo(@Param("empNo")String empNo); @Select("select*from emp where ename like concat(#{name},'%')") List<Emp> empSByLike(@Param("name")String name); @Delete("delete from emp where empno=#{empNo}") int deleteEmpByNo(@Param("empNo")String empNo); List<Emp> selectEmpByPage(@Param("currentPage") int currentPage, @Param("pageSize") int pageSize);
-
接口分析
findAllUser(): 注解:@Select("select * from emp") 功能:查询所有员工记录。 返回值:返回一个包含所有员工信息的 List<Emp> 集合。 addEmp(Emp emp): 注解:@Insert("insert into emp(empno, ename, job, mgr, sal, comm, deptno) values (#{empno}, #{ename}, #{job}, #{mgr}, #{sal}, #{comm}, #{deptno})") 功能:向数据库中的emp表插入一条新的员工记录。 参数:接受一个 Emp 对象作为参数,该对象包含了要插入的新员工的所有属性。 返回值:返回受影响的行数,通常为1表示成功插入了一条记录。 deleteAll(): 注解:@Delete("delete from emp") 功能:删除emp表中所有的员工记录。 返回值:返回受影响的行数,即被删除的记录总数。 selectEmpByNo(@Param("empNo") String empNo): 注解:@Select("select * from emp where empno=#{empNo}") 功能:根据提供的员工编号查找特定的员工记录。 参数:接受一个名为empNo的字符串参数,代表要查找的员工编号。 返回值:返回匹配的 Emp 对象,如果未找到则可能返回null。 empSByLike(@Param("name") String name): 注解:@Select("select * from emp where ename like concat(#{name},'%')") 功能:模糊查询员工姓名,返回所有名字以给定字符串开头的员工。 参数:接受一个名为name的字符串参数,用于构建LIKE查询条件。 返回值:返回符合查询条件的 List<Emp> 集合。 deleteEmpByNo(@Param("empNo") String empNo): 注解:@Delete("delete from emp where empno=#{empNo}") 功能:根据提供的员工编号删除特定的员工记录。 参数:接受一个名为empNo的字符串参数,代表要删除的员工编号。 返回值:返回受影响的行数,即被删除的记录数。 selectEmpByPage(@Param("currentPage") int currentPage, @Param("pageSize") int pageSize): 功能:分页查询员工记录。 参数:接受两个整型参数,currentPage 表示当前页码,pageSize 表示每页显示的记录数。 返回值:返回对应页码和页面大小的 List<Emp> 集合。
4.编写前端代码
-
配置代理服务器
// 为本地 Spring Boot 应用创建一个代理规则 '/api': { target: 'http://localhost:8080', // Spring Boot 应用的地址 ws: true, // 是否代理WebSocket changeOrigin: true, // 是否改变源 rewrite: path => path.replace(/^\/api/, '') // 将 /api 前缀重写为空字符串 }
-
设置Axios实例来与后端通信
import axios from 'axios'; const apiClient = axios.create({ baseURL: '/api', // 这个路径会根据Vite配置自动代理到Spring Boot后端 withCredentials: false, // 如果不需要跨域认证,可以设置为false headers: { 'Content-Type': 'application/json', } }); export default apiClient;
-
用elementplus编写表格主键展示员工信息
<!-- 显示所有员工信息 --> <el-table :data="employees" style="width: 100%" stripe v-loading="loading"> <el-table-column prop="empno" label="员工编号" width="180"></el-table-column> <el-table-column prop="ename" label="姓名" width="180"></el-table-column> <el-table-column prop="job" label="职位"></el-table-column> <el-table-column prop="mgr" label="上级"></el-table-column> <el-table-column prop="sal" label="薪资"></el-table-column> <el-table-column prop="comm" label="奖金"></el-table-column> <el-table-column prop="deptno" label="部门编号"></el-table-column> <el-table-column label="操作" width="300"> <template #default="scope"> <el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button> <el-button size="small" type="primary" @click="dialogVisible = true">新增员工</el-button> </template> </el-table-column> </el-table>
-
定义响应式变量接收数据展示表格
const employees = ref([]);
-
发送请求,获取分页数据
// 获取分页员工信息 const fetchEmployees = async () => { loading.value = true; try { const response = await apiClient.get('/emp/page', { params: { currentPage: currentPage.value, pageSize: pageSize.value } }); employees.value = response.data.content || response.data; total.value = response.data.totalElements || response.data.length; error.value = null; } catch (err) { error.value = '获取员工列表失败,请稍后再试'; ElMessage.error(error.value); console.error('API请求失败:', err.response ? err.response.data : err.message); } finally { loading.value = false; } };
-
定义输入框组件实现查找功能
<div style="margin-bottom: 20px;"> <el-input v-model="searchName" placeholder="请输入员工姓名" style="width: 200px;"></el-input> </div>
-
绑定输入框数据
const searchName = ref('');
-
向服务器发送请求获取模糊查询数据,更新表格数据
// 根据姓名模糊查询员工 const fetchEmployeesByName = async () => { if (!searchName.value.trim()) { ElMessage.warning('请输入要搜索的员工姓名'); return; } loading.value = true; try { const response = await apiClient.get('/emp/searchByName', { params: {name: searchName.value} }); employees.value = response.data; total.value = response.data.length; currentPage.value = 1; error.value = null; } catch (err) { error.value = '搜索员工失败,请稍后再试'; ElMessage.error(error.value); console.error('API请求失败:', err.response ? err.response.data : err.message); } finally { loading.value = false; } };
-
监听搜索框变化自动完成模糊查询
watch(searchName, (newValue) => { clearTimeout(searchTimeout.value); // 清除之前的定时器 // 设置一个短暂的延迟以避免频繁请求 searchTimeout.value = setTimeout(() => { if (newValue.trim()) { fetchEmployeesByName(); } else { // 如果输入框为空,则重新加载全部员工列表 fetchEmployees(); } }, 300); // 300ms 的延迟 });
-
编写新增按钮
<el-button size="small" type="primary" @click="dialogVisible = true">新增员工</el-button>
-
编写新增对话框
<!-- 新增员工对话框 --> <el-dialog title="新增员工" v-model="dialogVisible" width="30%"> <el-form :model="newEmployee" :rules="rules" ref="newEmployeeFormRef" label-width="100px"> <el-form-item label="员工编号" prop="empno"> <el-input v-model="newEmployee.empno"></el-input> </el-form-item> <el-form-item label="姓名" prop="ename"> <el-input v-model="newEmployee.ename"></el-input> </el-form-item> <el-form-item label="职位" prop="job"> <el-input v-model="newEmployee.job"></el-input> </el-form-item> <el-form-item label="上级" prop="mgr"> <el-input v-model="newEmployee.mgr"></el-input> </el-form-item> <el-form-item label="薪资" prop="sal"> <el-input v-model="newEmployee.sal"></el-input> </el-form-item> <el-form-item label="奖金" prop="comm"> <el-input v-model="newEmployee.comm"></el-input> </el-form-item> <el-form-item label="部门编号" prop="deptno"> <el-input v-model="newEmployee.deptno"></el-input> </el-form-item> </el-form> <template #footer> <span class="dialog-footer"> <el-button @click="dialogVisible = false">取 消</el-button> <el-button type="primary" @click="submitNewEmployee">确 定</el-button> </span> </template> </el-dialog>
-
const dialogVisible = ref(false);默认对话框不展现
-
向服务器发送请求,新增数据
// 提交新增员工 const submitNewEmployee = async () => { newEmployeeFormRef.value.validate(async (valid) => { if (valid) { try { await apiClient.post('/emp/add', newEmployee.value); ElMessage({message: '新增成功', type: 'success'}); dialogVisible.value = false; await fetchEmployees(); } catch (err) { ElMessage.error('新增失败,请检查输入或稍后再试'); console.error('API请求失败:', err.response ? err.response.data : err.message); } } else { console.log('验证失败'); return false; } });
-
编写删除逻辑,动态获取所在行的数据
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
-
向服务器发送请求,删除数据
// 处理删除 const handleDelete = async (row) => { try { const confirmResult = await ElMessageBox.confirm( '确定要删除该员工吗?', '警告', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' } ); if (confirmResult !== 'confirm') return; await apiClient.delete(`/emp/delete/${row.empno}`); ElMessage({message: '删除成功', type: 'success'}); await fetchEmployees(); } catch (err) { if (err === 'cancel') { ElMessage({ type: 'info', message: '已取消删除' }); } else { ElMessage.error('删除失败,请检查输入或稍后再试'); console.error('API请求失败:', err.response ? err.response.data : err.message); } } };
-
分页组件的使用
<el-pagination v-show="!searchName" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage" :page-sizes="[5, 10, 20, 50]" :page-size="pageSize" layout="total, sizes, prev, pager, next, jumper" :total="total" style="margin-top: 20px;" > </el-pagination>
@size-change="handleSizeChange": 当用户改变了每页显示的条目数时触发此事件,并调用 handleSizeChange 方法处理逻辑,通常会更新 pageSize 并重新加载数据。 @current-change="handleCurrentChange": 当用户点击了不同的页码或使用“上一页”、“下一页”按钮时触发此事件,并调用 handleCurrentChange 方法处理逻辑,通常会更新 currentPage 并重新加载对应页的数据。