news 2026/9/12 22:50:02

servlet+jsp+mysql实现药店管理系统完整实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
servlet+jsp+mysql实现药店管理系统完整实战指南

简介:一套基于JavaWeb技术栈的药店管理系统源码包,采用Servlet、JSP搭配MySQL数据库实现,覆盖客户信息管理、药品类别管理、药品信息管理、出入库记录与用户权限控制等核心业务模块。压缩包共112个文件,包含32个Java源文件及对应class文件、7个JSP页面、8个CSS样式、6个JavaScript脚本、6个JAR依赖包,并附带初始化数据库所需的SQL脚本,整体大小约2.19MB,目录结构清晰,适合导入Eclipse直接部署。已有573人学习下载,可满足JavaWeb课程设计、毕业设计或入门实战需求。通过该系统可完整掌握客户与药品信息的增删改查、药品出库入库、登录退出及用户管理等典型功能,适合需要快速上手Servlet+JSP项目开发的读者,也能为二次扩展和功能优化提供清晰的基础代码。

1. 为什么servlet+jsp+mysql仍是药店管理系统快速交付的首选

在Spring Boot几乎统治后端生态的今天,把药店管理系统做成javaWeb+servlet+jsp+mysql的组合,常被划进课堂作业那类评价。但真实需求里有一种场景恰恰只有这一套最适合:单店内网的进销存后台,数据量和并发规模都不大,部署环境可能只是一台Windows主机,页面要能直接打开、改两行就能生效。servlet把每个请求分发到对应的处理入口,jsp承担页面渲染,mysql保存药品与库存数据,没有中间层和自动配置要维护,war包扔进Tomcat就能跑。这套系统的业务面集中在登录、药品维护、库存变更和效期预警,MVC三层各管一段,后文会从mysql建库脚本开始,一路把DAO、servlet控制器、jsp渲染和部署排错完整走一遍。适合要给小药店快速交付管理工具的工程师,也适合想在面试里把javaWeb整个请求链路串起来讲的人。

2. javaWeb技术栈清晰分工:servlet处理请求、jsp渲染视图、mysql落地数据

开始编码之前先立住一条线:在同一套系统里,servlet、jsp、mysql各自的职责边界是什么。servlet不负责拼HTML,jsp不连接数据库,mysql不写业务判断。在药店管理系统里,你看到的往往是二十来个页面、七八张业务表,谁把职责往旁边挪一点,后面排错就要多绕一段路。下面按三块分别展开,顺带把常见误用一起点掉。

2.1 servlet单实例多线程模型:为什么controller里不能放成员变量

servlet由容器管理实例,默认一个servlet类只创建一个对象,所有并发请求复用这个实例,以多线程方式执行service方法。这个模型的直观后果是:servlet里的成员变量对所有请求共享。如果你在一个药品编辑servlet里用成员变量暂存前端传来的drug_id,两个管理员同时打开编辑页,后提交的那个人就会看到另一个人正在处理的数据。

正确约束是保持servlet无状态:doGet和doPost方法内部只用局部变量,请求数据通过request.getParameter读取,跨请求的用户状态唯一放session,查询结果用request.setAttribute传给jsp。控制流上养成一个习惯,让doPost直接调用doGet,或者把公共逻辑抽成一个私有方法,后续加查询参数时不会出现get调用链和post调用链不一致的错位。

servlet在这个系统里用到的核心API,按实际落点梳理成下面的对应关系:

API在药店管理系统里的落点容易踩的误区
request.getParameter读取药品名、批号、库存数字未先设置字符集,中文变问号
request.getSession登录后保存用户身份登录校验前就创建session,产生空会话
request.getRequestDispatcher().forward()把数据带进jsp渲染页面与sendRedirect混用,丢失request属性
response.sendRedirect()操作完成后的页面跳转表单重复提交时用了forward

getParameter取值之后,要做一次空值和长度的校验,药品编码为空、库存传了负数这类问题在servlet层挡掉,比到DAO层报SQL异常要直观得多。getSession(false)在不传true时,如果还没有会话会返回null,这是登录拦截最简单的实现方式。forward和sendRedirect的选择规则很固定:列表查询之后forward给jsp,表单提交成功之后redirect到列表页,防止用户按F5重复提交。

2.2 jsp的翻译、编译与class文件位置

jsp本质是servlet。第一次被请求时,容器把jsp源文件翻译成对应servlet的Java源文件,再用javac编译成class,之后按servlet路径执行。Tomcat在运行时依赖jsp文件的时间戳判断是否需要重新翻译,这解释了为什么“jsp改了不生效”是高频问题:部署目录里那份jsp不是你编辑的那份,或者容器没检测到时间戳变化。

翻译生成的java文件和class文件放在Tomcat的work目录下,路径类似work/Catalina/localhost/项目名/org/apache/jsp,每个jsp对应一个名为index_jsp.java这样的类。排查jsp不生效时,直接打开这个java文件看代码生成时间和内容,比盲目重启Tomcat有效得多。jsp页面里不建议再使用<%%>这种scriptlet嵌入业务代码,药品列表、库存预警这类页面只需要做循环输出和条件判断,EL表达式加JSTL标签完全够用。

JSP九大内置对象里,实际开发中用得最多的是request、session、application三个作用域。给jsp传数据时遵循就近原则:列表数据放request,登录用户放session,系统级配置放application。别把几十条药品数据同时塞进session,session存的是整个浏览器会话的共享数据,存大对象会白白占用内存。

2.3 mysql JDBC连接串:一组能少踩坑的参数

servlet不直接碰数据库,中间的通道是JDBC。不引入连接池的写法里,获取连接的代码长这样:

String url = "jdbc:mysql://127.0.0.1:3306/drug_store_db" + "?useSSL=false&characterEncoding=utf8" + "&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"; Connection conn = DriverManager.getConnection(url, "root", "yourpass");

逻辑说明:useSSL=false在内网部署时能明显缩短连接耗时;characterEncoding=utf8保证中文药品名和供应商名称在应用层与mysql之间正确转码;serverTimezone指定连接会话的时区,mysql 8.x驱动默认要求明确时区,否则会直接报时区错误;allowPublicKeyRetrieval=true解决caching_sha2_password认证方式下首次连接拿不到公钥的问题。

参数说明:serverTimezone的值必须与mysql实例所在操作系统一致,而不是与前端显示器上的时间一致。如果服务器跑在UTC时区,连接串写Asia/Shanghai会让日期字段整体偏差。这一版先用DriverManager直连,是为了在调试阶段能把异常栈看得最直接。系统上线前再按第5章的接法换成连接池,DAO层代码不需要改动。

3. mysql数据库设计:药店管理系统的核心表结构与初始化脚本

管理系统的成败,一半在数据库设计。药店系统的最核心实体是药品,但它还牵扯两件事:管理员从哪来、货源从哪来。所以至少需要用户表、药品表、供应商表,以及连接药品与供应商的中间表。下面把字段取舍和建表脚本一次讲完。

3.1 用户表、药品表、供应商表的关键字段

用户表不追求大而全,username、password、role三件套足够登录和鉴权用。药品表是这套系统的重心,字段要区分“基础信息”和“库存状态”。基础信息包括药品编码、通用名、规格、单位、生产厂家、分类、处方标志;库存状态包括库存数量、库存下限、采购价、售价、生产日期、有效期。把两类信息放在同一张表里没问题,但要清楚哪些字段是高频更新、哪些是低频更新,索引设计和页面表单分组都跟着这个认知走。

供应商表和药品是多对多关系:一种药品可以有多家供货商,一家供货商可以供多种药。用一张drug_supplier_rel中间表承载,联合主键是(drug_id, supplier_id)。如果以后要记录每次采购的数量和价格,再去中间表上扩展采购单字段,现阶段不引入复杂的采购单据模型,避免在一套小型系统里过度设计。

下面是药品表里最值得提前想清楚的字段:

字段类型业务含义设计说明
drug_codeVARCHAR(20)药品编码唯一索引,对应药品的条码或内部编码
prescription_flagTINYINT是否处方药0为OTC,1为处方药,页面按此显示提示
stock_quantityINT当前库存出库扣减、入库增加,业务层阻止负数
stock_lower_limitINT库存预警下限低于此值列表页标红
expire_dateDATE有效期至效期预警的查询条件
sale_priceDECIMAL(10,2)零售价用DECIMAL而不是double,避免浮点误差

另外要强调一个设计决策:效期字段直接放在药品表,而不是单独建批次表。药店场景下同一药品在一个时间段内通常只有一个主流批号,批次表会让入库操作多一步选批号,实际录入员往往直接沿用旧批号。如果系统面向的是带GSP要求的连锁药店,再升级成批次表不迟;单体药店的管理系统用单一效期字段,运维成本最低。

3.2 mysql建表脚本与字符集设置

下面的SQL脚本可以在mysql 8.x直接执行,建库、建表、建初始数据一次完成:

CREATE DATABASE IF NOT EXISTS drug_store_db DEFAULT CHARACTER SET utf8mb4; USE drug_store_db; CREATE TABLE sys_user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(30) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, real_name VARCHAR(30), role CHAR(2) NOT NULL DEFAULT 'A', created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB; CREATE TABLE drug_info ( id INT AUTO_INCREMENT PRIMARY KEY, drug_code VARCHAR(20) NOT NULL UNIQUE, drug_name VARCHAR(100) NOT NULL, spec VARCHAR(100), unit VARCHAR(10), manufacturer VARCHAR(100), category VARCHAR(30), prescription_flag TINYINT DEFAULT 0, stock_quantity INT NOT NULL DEFAULT 0, stock_lower_limit INT DEFAULT 10, purchase_price DECIMAL(10,2), sale_price DECIMAL(10,2), production_date DATE, expire_date DATE, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB; CREATE TABLE supplier ( id INT AUTO_INCREMENT PRIMARY KEY, supplier_name VARCHAR(100) NOT NULL, contact_person VARCHAR(30), phone VARCHAR(20), address VARCHAR(255) ) ENGINE=InnoDB; CREATE TABLE drug_supplier_rel ( drug_id INT NOT NULL, supplier_id INT NOT NULL, PRIMARY KEY (drug_id, supplier_id), FOREIGN KEY (drug_id) REFERENCES drug_info(id), FOREIGN KEY (supplier_id) REFERENCES supplier(id) ) ENGINE=InnoDB;

逻辑说明:建库语句里的DEFAULT CHARACTER SET utf8mb4一旦指定,后续建表不写字符集时全部继承。sys_user里password字段留64位,是为了给BCrypt或SHA-256预留空间,如果直接用MD5,32位也够。drug_info里update_time配合ON UPDATE CURRENT_TIMESTAMP,每次更新行时时间自动刷新,不用在Java代码里手动set。

参数说明:临时表、存储过程这类能力在这个系统里没有使用场景,不要为了展示技术硬加进去。外键约束是InnoDB的强项,但中间表的外键指向两张主表,删除药品或供应商时要先处理中间表的关联行,否则外键会拦下DELETE语句。实际开发里,如果发现删除频繁被外键阻塞,说明业务上并不适合物理外键,可以改成应用层维护关联关系。

3.3 初始化数据与效期预警查询

插入一个管理员账号和两条示例药品,让系统第一次登录就能看到列表效果:

INSERT INTO sys_user (username, password, real_name) VALUES ('admin', MD5('123456'), '管理员'); INSERT INTO drug_info (drug_code, drug_name, spec, unit, manufacturer, category, prescription_flag, stock_quantity, stock_lower_limit, purchase_price, sale_price, production_date, expire_date) VALUES ('DRG001', '阿莫西林胶囊', '0.25g*24粒', '盒', '某制药厂', '抗生素', 1, 120, 30, 8.50, 12.00, '2024-03-01', '2026-02-28'), ('DRG002', '布洛芬缓释胶囊', '0.3g*20粒', '盒', '某药业公司', '解热镇痛', 0, 85, 20, 10.00, 15.80, '2024-01-15', '2025-12-31');

逻辑说明:管理员密码用MD5只是为了演示登录链路。真实系统建议用BCrypt,JDK里没有自带实现,需要引入依赖,这也是为什么第2章直连方式更适合教学,而生产部署要换连接池一样,都属于上线前替换项。示例数据里把处方标志和库存预警都覆盖到,列表页才能同时展示两种状态。

插入后的第一条业务查询是效期预警。查未来90天内过期的药品,并和库存预警合并输出:

SELECT drug_code, drug_name, stock_quantity, expire_date FROM drug_info WHERE stock_quantity < stock_lower_limit OR expire_date < DATE_ADD(CURDATE(), INTERVAL 90 DAY);

逻辑说明:stock_quantity < stock_lower_limit是数值比较,expire_date < DATE_ADD(...)是日期比较,两个条件用OR连接,查询结果是两条线路的并集,正好对应页面上一张“需关注药品”列表。

参数说明:DATE_ADD(CURDATE(), INTERVAL 90 DAY)里的90天是业务参数,不同药店对效期预警的窗口要求不一样,有的是60天有的是180天,建议把天数提取成JSP页面上的一个下拉框,后端用PreparedStatement绑定参数传递。

注意:expire_date允许为空后,效期预警就会漏掉空值记录。严格模式就加上NOT NULL约束;保留空值的系统,查询时加上expire_date IS NOT NULL,避免NULL参与比较导致整行被过滤掉。

4. 从DAO到Servlet再到JSP:药品列表的完整编码链路

这一章用“药品列表页”作为最小闭环,串起从mysql查询到页面渲染的全部代码。药品列表是整套系统的首页级功能,它跑通之后,新增、编辑、删除、库存预警都按同一套模式复制扩展。写的时候把每一层职责看清楚,后面改需求就不会伤筋动骨。

4.1 DAO层:用PreparedStatement把mysql数据映射成对象

DAO层只看数据库,不看HTTP。查询药品列表并拼出供应商名单的DAO方法如下:

public List<DrugInfo> findDrugList() { String sql = "SELECT d.id, d.drug_code, d.drug_name, d.spec, " + "d.stock_quantity, d.stock_lower_limit, d.expire_date, " + "d.sale_price, GROUP_CONCAT(s.supplier_name) AS suppliers " + "FROM drug_info d " + "LEFT JOIN drug_supplier_rel r ON d.id = r.drug_id " + "LEFT JOIN supplier s ON r.supplier_id = s.id " + "GROUP BY d.id " + "ORDER BY d.drug_code"; List<DrugInfo> list = new ArrayList<>(); try (Connection conn = DbUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { DrugInfo item = new DrugInfo(); item.setId(rs.getInt("id")); item.setDrugCode(rs.getString("drug_code")); item.setDrugName(rs.getString("drug_name")); item.setSpec(rs.getString("spec")); item.setStockQuantity(rs.getInt("stock_quantity")); item.setStockLowerLimit(rs.getInt("stock_lower_limit")); item.setExpireDate(rs.getDate("expire_date")); item.setSalePrice(rs.getBigDecimal("sale_price")); item.setSuppliers(rs.getString("suppliers")); list.add(item); } } catch (SQLException e) { throw new RuntimeException("查询药品列表失败", e); } return list; }

逻辑说明:try-with-resources会在代码块结束时按逆序关闭ResultSet、PreparedStatement、Connection,省去finally里手写关闭的冗长样板。SQL里用的GROUP_CONCAT是mysql聚合函数,它把同一药品关联的多个供应商名称拼成一行,避免在Java里自己组装Map。LEFT JOIN保证药品还没关联供应商时,药品行依然出现在列表里,而不是被JOIN掉。

参数说明:查询列中不包含purchase_price采购价,列表页只需要展示零售价,把采购价留在详情或编辑页单独查,可以避免价格信息在页面上被随意看到。rs.getBigDecimal("sale_price")对应DECIMAL类型,比getDouble精确,后续做合计、对账时不会出现浮点数误差。

4.2 Servlet层:登录校验与请求转发

Servlet负责接收请求、调用DAO、决定下一步走向。药品列表的Servlet用Servlet 3.0的@WebServlet注解即可,不用在web.xml里写映射:

@WebServlet("/drug/list") public class DrugListServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); HttpSession session = request.getSession(false); if (session == null || session.getAttribute("loginUser") == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } DrugDao dao = new DrugDao(); List<DrugInfo> list = dao.findDrugList(); request.setAttribute("drugList", list); request.getRequestDispatcher("/WEB-INF/pages/drug/list.jsp") .forward(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

逻辑说明:request.setCharacterEncoding("UTF-8")必须发生在任何getParameter调用之前,因为参数是在第一次读取请求体时被解析的,晚了就来不及了。getSession(false)在没有会话时返回null,配合session里的loginUser属性判断登录态;未登录直接重定向到登录首页。请求转发用forward,它会把request对象连同drugList属性一起带到jsp,而不是让浏览器发起第二次请求。

参数说明:转发路径写成/WEB-INF/pages/drug/list.jsp。把jsp放进WEB-INF目录后,浏览器无法直接访问jsp源码,只能由servlet转发触达,这是servlet+jsp项目里最常见的权限保护手段。sendRedirect的参数用request.getContextPath()拼前缀,避免项目部署在非ROOT路径时跳错地址。

4.3 JSP层:用JSTL循环渲染药品表格

JSP页面只做展示。列表页用JSTL的c:forEach循环输出药品数据:

<%@ page contentType="text/html;charset=UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <html> <head><title>药品管理列表</title></head> <body> <table border="1" cellpadding="6" cellspacing="0"> <tr> <th>药品编码</th><th>药品名称</th><th>规格</th> <th>库存</th><th>效期至</th><th>售价</th><th>供应商</th> </tr> <c:forEach items="${drugList}" var="d"> <tr> <td>${d.drugCode}</td> <td>${d.drugName}</td> <td>${d.spec}</td> <td>${d.stockQuantity}</td> <td><fmt:formatDate value="${d.expireDate}" pattern="yyyy-MM-dd"/></td> <td>${d.salePrice}</td> <td>${d.suppliers}</td> </tr> </c:forEach> </table> </body> </html>

逻辑说明:${d.drugCode}这类EL表达式,调用的是DrugInfo对象对应的getter方法。c:forEach遍历servlet里放进request作用域的drugList,列表页的核心渲染逻辑只有循环和取值。fmt:formatDate负责把Date对象格式化成字符串,它内部对线程安全处理得比手动new SimpleDateFormat更可靠。

参数说明:页面里第4章延续了服务端渲染的方式,不要让jsp去调servlet接口拿JSON,那是前后端分离的玩法,在servlet+jsp栈里硬套反而多一层维护。库存预警的标红操作会在库存数量低于下限时用c:if判断class输出,做法是再加一列,把数量用小号字体和不同背景色渲染,逻辑同样在jsp内完成,不需要额外往返请求。

JSTL标签与当前项目的对应关系整理如下:

标签作用药店系统里的位置
c:forEach集合循环药品列表、库存预警列表
c:if条件判断库存低于阈值时标红
c:choose多分支选择按药品分类显示不同标签样式
fmt:formatDate日期格式化有效期、生产日期字段

4.4 web.xml与部署环境的几处关键配置

使用注解后,web.xml不需要servlet mapped,但还有几项适合放在web.xml里的全局配置:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <session-config> <session-timeout>30</session-timeout> </session-config> <welcome-file-list> <welcome-file>login.jsp</welcome-file> </welcome-file-list> </web-app>

参数说明:session-timeout设成30分钟,药店收银台长时间不操作后自动断开登录,避免公用电脑上的会话被他人沿用。welcome-file直接指向login.jsp,整个系统的入口从这里开始。之后如果要给多个后台页面统一做登录过滤,把Filter的filter-mapping也写进本文件,URL pattern用/drug/*、/supplier/*分块拦截即可。

部署时还有一个和IDEA相关的坑:在开发环境直接右键运行servlet类,会看到“错误: 找不到或无法加载主类”的提示。原因是servlet本身没有main方法,它必须由Tomcat容器加载并调用。遇到这种情况,检查编译输出目录有没有生成对应class文件,然后通过Tomcat的Run Configuration启动Web项目,不要试图把servlet当普通Java类执行。servlet的入口是容器调起的service方法,IDE弹出的main入口提示属于误操作。

5. 上线前后必查的三个问题:jsp改不生效、mysql乱码与连接池接入

前面几章把系统搭起来后,运行期最常见的问题集中在三个方向。它们不太影响功能完成度,但直接影响交付印象。

5.1 jsp改了不生效的排查顺序

jsp改不生效时,先别急着重启Tomcat,按下面的顺序查:

  1. 确认你改的是Tomcat实际加载的那份jsp。IDE里类似target或out的构建目录经常和源目录并存,构建产物没刷新时,容器加载的是旧文件。
  2. 打开Tomcat work目录下对应项目的org/apache/jsp,找到页面名对应的java文件,看生成时间是否晚于你最后一次编辑时间。
  3. 检查jsp编译是否报错。新写的EL表达式或taglib指令存在语法错误时,容器会继续沿用上一次编译成功的class,从catalina.out里能看到编译异常。

Tomcat通过时间戳判断jsp是否需要重编译,如果服务器时间被回拨,也会触发不生效。开发阶段把部署方式保持为exploded模式,改完jsp后直接刷新页面,不要每次打包war再发布。

5.2 mysql中文乱码的检查与修复

药品名、供应商名出现中文问号,根源几乎都是字符集不一致。排查范围锁定三个节点:连接串里的characterEncoding、mysql表默认字符集、jsp的contentType指定charset。三者统一用utf8mb4时,乱码基本不会出现。如果数据已经乱码,先不要直接UPDATE,用下面的命令看清楚当前连接的字符集:

SET NAMES utf8mb4; SHOW VARIABLES LIKE 'character_set%';

逻辑说明:SET NAMES仅影响当前会话,让客户端与服务器之间的通信立即切到utf8mb4;SHOW VARIABLES输出六项character_set变量,其中character_set_client、character_set_connection、character_set_results三项值得关注。三项都是utf8mb4,说明链路没问题。

参数说明:如果三项不一致,优先在连接串层面修复,不要在数据库里反复转码。修复存量乱码数据时,尽量避免“更新子查询”这种写法:UPDATE drug_info SET drug_name = (SELECT ... FROM drug_info WHERE id = ?)会直接报“You can't specify target table for update”,因为mysql不允许在子查询中直接引用目标表,需要再包一层临时表派生查询才能绕过。

5.3 把直连改成连接池的接入方式

DriverManager直连在开发阶段完全够用,但并发一上来,每次请求的建连耗时就会拖慢页面。常见做法是引入HikariCP连接池,把获取连接的工具类替换掉:

HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/drug_store_db" + "?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai"); config.setUsername("root"); config.setPassword("yourpass"); config.setMaximumPoolSize(10); config.setMinimumIdle(2); config.setConnectionTimeout(3000); HikariDataSource ds = new HikariDataSource(config);

逻辑说明:HikariDataSource.getConnection()返回的是标准的java.sql.Connection对象,所以DAO层已有的try-with-resources代码一行都不用改,只把DbUtil里获取连接的方式指向连接池。上面这段初始化代码放在DbUtil的静态块里执行一次,之后整个系统复用这个数据源。

参数说明:maximumPoolSize设为10对于药店系统足够,过大的连接数反而会拖垮mysql;minimumIdle设为2是为了让应用启动时就备好两条连接,避开第一波请求同时建连;connectionTimeout是3000毫秒,拿不到连接就抛异常,避免请求无限期挂起。改完之后,用带查询参数的药品列表接口连续刷几十次,观察数据库端连接数和响应耗时的变化,如果连接数稳定在池的上限以内、耗时不持续攀升,说明连接池已经接管了连接生命周期。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 22:49:49

三相变压器励磁涌流为何至少两相同时发生

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 22:48:01

基于YOLOv5的吸烟行为检测实战:优化小目标与部署防误报

简介&#xff1a;基于Yolov5的吸烟行为检测资源&#xff0c;面向计算机视觉开发者与安防监控领域从业者&#xff0c;提供从模型训练到实时检测的完整工程方案。资源包共100个文件&#xff0c;涵盖41个Python脚本、31个YAML配置文件、2个预训练模型权重&#xff0c;以及Dockerfi…

作者头像 李华
网站建设 2026/9/12 22:46:54

开源轻量Markdown编辑器mdput实测:能否成为Typora的免费平替?

说实话&#xff0c;我是从 Typora 0.9 时代一路用过来的老用户。当时它靠实时渲染和极简界面吸引了一大波 Markdown 写作爱好者&#xff0c;我也顺手把笔记、博客草稿、项目文档全部丢进去了。后来 Typora 转成收费模式&#xff0c;官方提供免费试用&#xff0c;但过期后那个购…

作者头像 李华
网站建设 2026/9/12 22:45:18

基于STM32F103C8T6的OLED万年历:RTC闰年算法与DHT11传感器实战

简介&#xff1a;基于STM32F103C8T6单片机打造的智能万年历设计&#xff0c;涵盖OLED屏显示年月日时分秒、闰年自动调整&#xff0c;以及DS18B20与DHT11实时温湿度采集&#xff0c;面向单片机课程设计、毕业设计及物联网入门学习者&#xff0c;可直接作为数字时钟类项目的参考模…

作者头像 李华
网站建设 2026/9/12 22:44:49

家庭实验室HTTPS困境破局:自建根CA实现零配置内网证书管理

1. 为什么“自托管家庭实验室”正在集体陷入认证泥潭&#xff1f;“这款极简认证神器&#xff0c;拯救你的自托管家庭实验室&#xff01;”——标题里没提名字、没列参数、甚至没写一句功能描述&#xff0c;却让无数在NAS、树莓派、Home Lab里折腾了三年以上的人心头一震。不是…

作者头像 李华
网站建设 2026/9/12 22:44:47

ASP.NET实现汽车制造业文件批量上传解决方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华