首页/新闻资讯/正文详情

Java Web入门必练:JDBC+Servlet+JSP实现部门增删改查系统

发布时间:2026/9/26 20:15:13 来源:云帆数科 栏目:资讯中心
Java Web入门必练:JDBC+Servlet+JSP实现部门增删改查系统
学 Java 到第 14 天终于不再是照着教程敲语法 demo 了。这篇博文记录的是一套完整的“部门系统”案例开发过程核心功能就是部门的增删查改。选它当第一个完整案例是因为它足够小却能覆盖 Java Web 开发里最要命的一整条链路页面提交请求 → Servlet 接收参数 → Service 做业务判断 → DAO 访问数据库 → 再把结果返回页面。如果你也处在 Java 基础刚过完、想找一个能练手的闭环项目或者准备课程设计却不知道从哪下手这份 Day14 笔记应该能直接抄作业。我自己的进度是第 1~13 天过完了变量、流程控制、面向对象、集合、异常、IO还花了两天把 JDBC 的基础 API 摸了一遍用的是 MySQL 8 原生 JDBC从第 14 天开始进入 Web 层。技术栈是 JDBC Servlet JSP没有引入 Spring 那一套。正因为还没上框架原生方式反而把问题暴露得更彻底后面再换框架时你才会知道框架到底帮我们省了什么。1. 为什么拿“部门系统”当第一个完整案例1.1 案例选型它小到什么程度又大到什么程度很多人学完 Java 基础之后会面临同一个问题资料看了一堆真正动手时脑子里一片空白。我当时也这样。Day14 之前我试着写过图书管理、学生信息管理发现要么是表字段太多写着写着就去调样式了要么是业务太抽象根本理解不了为什么要分 Service 和 DAO 两层。部门系统刚好卡在一个很舒服的位置。部门这个对象只有几个核心属性部门编号、部门名称、部门描述、创建时间。增删查改的每一个功能都直白得不需要解释但背后的工程问题一个都不少表单怎么提交、参数怎么校验、数据库连接什么时候关闭、列表查询结果为空怎么办、删除一个被引用的部门会出什么问题。它小到一天能跑完大到足够把 Java Web 的分层思想完整串一遍。1.2 前后 13 天的知识在这里完成第一次合流我整理了一下这套案例到底用到了哪些前 13 天的内容列出来之后连我自己都吓一跳面向对象Department 实体类封装属性私有、提供 getter/setter集合框架查询结果用ListDepartment承载循环渲染到表格异常处理SQLException 的上抛与 finally 中关闭资源JDBCConnection、PreparedStatement、ResultSet 的常规使用流程控制根据 action 参数分发到不同业务方法。更关键的是它第一次强迫你关注“一个请求从浏览器到数据库再回来”这件事。以前写 JDBC 是在 main 方法里打印结果一旦放进 Web 容器你会发现控制台输出的时代结束了数据的去处变成了 JSP 页面上的表格。这个转变是第 14 天最重要的收获。1.3 技术栈为什么不直接上 Spring Boot我在网上搜了很多案例教程清一色用 Spring Boot 一键生成 CRUD。对初学者来说这其实是有毒的。一键生成之后你只知道点“运行”按钮网页就出来了但数据是怎么流过去的、请求是怎么被路由的、SQL 是怎么拼接的全部被框架吞掉了。所以我坚持用 JDBC Servlet JSP 裸写一遍。目的很简单让每一条数据流肉眼可见。Servlet 里你亲眼看着request.getParameter()取出字符串DAO 里你亲手把字符串填进PreparedStatementJSP 上用 EL 表达式把数据渲染出来。等以后接触 MyBatis 和 Spring Data JPA你会觉得那些框架的底层设计全是顺理成章的而不是一片黑盒。2. 搭工程和设计表动手前先想清楚这三件事2.1 环境清单和项目目录长什么样动手前先确认环境别到写代码时才发现问题JDK 11Tomcat 9MySQL 8.0数据库名company_dbIDE 用的 IDEA直接配置本地 Tomcat 运行Maven 管理依赖项目是 war 包结构。如果你是手动导入 jar 包的流程思路完全一样区别只是把依赖文件放进WEB-INF/lib而已。Maven 的pom.xml里我引入了三个依赖Servlet APIprovided 作用域、MySQL 驱动、JSTL 标签库。后三个用不到 EL 也许可以不引但我为了给列表页做遍历用到了 JSTL 的c:forEach所以提前补上了。项目包结构我按照 Web 项目的经典分层来建src/main/java ├── com.demo.entity -- Department 实体类 ├── com.demo.dao -- DepartmentDao 数据访问类 ├── com.demo.service -- DepartmentService 业务逻辑类 ├── com.demo.servlet -- DepartmentServlet 控制器 └── com.demo.util -- DBUtil 数据库连接工具 src/main/webapp ├── department │ ├── list.jsp -- 列表页 │ ├── add.jsp -- 新增页 │ └── edit.jsp -- 修改页 └── WEB-INF/web.xml包结构划分的意义不在好看而在隔离变化。DAO 只管数据库读写Service 只管业务规则Servlet 只管接收参数和转向页面。后面你哪怕把 JDBC 换成 MyBatis前端页面不用动一行这就是分层的价值。2.2 department 表设计的三个关键字唯一、默认、可空表结构我第一版建得很偷懒只有 id 和 name后来加搜索功能时发现没有描述字段搜索不知道该匹配什么只好回头重建表。第二版我稳了一点建表语句如下CREATE TABLE department ( id INT NOT NULL AUTO_INCREMENT COMMENT 部门编号, name VARCHAR(50) NOT NULL COMMENT 部门名称, description VARCHAR(255) DEFAULT NULL COMMENT 部门职责描述, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), UNIQUE KEY uk_name (name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;三个细节值得说。第一个是 name 加了唯一约束这样重名部门在数据库层面就被拦住了比在 Java 代码里先查再插更可靠。并发场景下“先查后插”会有竞态问题唯一索引才是最终防线。第二个是 create_time 用了DEFAULT CURRENT_TIMESTAMP插入的时候完全不用管它数据库自动写当前时间。第三个是 description 允许为空因为不是所有部门都需要填职责描述强制 NOT NULL 反而给用户制造麻烦。2.3 实体类与数据库连接工具的最小骨架实体类没什么技术含量但字段类型要和数据库对齐。MySQL 的DATETIME对应 Java 的java.util.DateTINYINT类型很容易被顺手写成Integer在这里还没遇到后面做员工状态字段时要小心。public class Department { private Integer id; private String name; private String description; private Date createTime; public Department() {} public Department(String name, String description) { this.name name; this.description description; } // getter 和 setter 省略IDE 自动生成 }DBUtil 工具类是整个项目的地基。我最初写的时候把驱动注册放到每次获取连接里结果每调用一次就 Class.forName 一次虽然影响不大但看着别扭。后来改成了静态代码块类加载时只注册一次public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/company_db ?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingUTF-8; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(MySQL驱动加载失败请检查依赖); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }注意这里的 URL 参数serverTimezoneAsia/Shanghai解决 MySQL 8 的时区警告characterEncodingUTF-8解决下文中文字符乱码。这两个参数不写后面一定会回来补课。3. 查询和删除两个越简单越容易漏的功能3.1 列表查询和关键字搜索SQL 别这么拼查询是所有页面的入口。列表页一打开就要把全部部门查出来然后渲染成表格。DAO 里最基础的findAll方法public ListDepartment findAll() throws SQLException { String sql SELECT id, name, description, create_time FROM department; ListDepartment list new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { Department dept new Department(); dept.setId(rs.getInt(id)); dept.setName(rs.getString(name)); dept.setDescription(rs.getString(description)); dept.setCreateTime(rs.getDate(create_time)); list.add(dept); } } return list; }这里我要特意强调 try-with-resources 的写法。第 13 天之前我习惯用 finally 手动关闭 ResultSet、Statement、Connection代码又臭又长。JDK 7 之后 try-with-resources 会自动关闭实现了 AutoCloseable 的资源而且关闭顺序是反着的ResultSet 先关、Connection 最后关。初学阶段就该养成这个习惯不然写数据库代码一半时间都在处理资源泄漏。关键字搜索是列表查询的自然延伸。我在列表页上面加了一个搜索框提交一个keyword参数过来。SQL 的写法是最容易翻车的地方// 错误写法 String sql SELECT * FROM department WHERE name LIKE % keyword %; // 正确写法 String sql SELECT id, name, description, create_time FROM department WHERE name LIKE ?; ps.setString(1, % keyword %);错的那种写法有两个问题一是字符串拼接容易引发 SQL 注入二是如果 keyword 里有单引号整个 SQL 直接语法错误。用PreparedStatement占位符然后传%关键字%既防注入又干净。我一开始甚至想过写LIKE %?%执行后毫无悬念地报错占位符不能嵌在字符串字面量里。想实现前后模糊匹配要么用concat(%, ?, %)要么在 Java 测把百分号拼进参数里我选了后者可读性更好。Servlet 层通过 action 参数分发列表和搜索共用一套方法WebServlet(/department) public class DepartmentServlet extends HttpServlet { private DepartmentService service new DepartmentService(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action req.getParameter(action); if (list.equals(action) || action null) { listWithSearch(req, resp); } else if (delete.equals(action)) { delete(req, resp); } else if (edit.equals(action)) { showEditPage(req, resp); } } private void listWithSearch(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String keyword req.getParameter(keyword); ListDepartment list service.findDepartments(keyword); req.setAttribute(deptList, list); req.getRequestDispatcher(/department/list.jsp).forward(req, resp); } }action null时也默认走列表这样直接访问/department就能看到列表页减少一次手输参数的麻烦。3.2 删除单个部门外键约束第一次教做人删除功能的代码很短private void delete(HttpServletRequest req, HttpServletResponse resp) throws IOException { int id Integer.parseInt(req.getParameter(id)); boolean success service.deleteDepartment(id); if (success) { resp.sendRedirect(req.getContextPath() /department?actionlist); } else { resp.sendRedirect(req.getContextPath() /department?actionlisterror1); } }真正有戏的是 Service 层。删除一个部门之前必须考虑“这个部门下面还有员工”。我为了验证这个问题专门建了一张employee表里面dept_id外键指向department.id。然后试着删除一个部门数据库直接报错Cannot delete or update a parent row: a foreign key constraint fails这个报错是好事它说明数据完整性被保护住了。但直接把异常抛给用户不友好。我的处理方式是在 Service 里显式检查public boolean deleteDepartment(int id) { try (Connection conn DBUtil.getConnection()) { // 先查部门下是否有员工 String checkSql SELECT COUNT(*) FROM employee WHERE dept_id ?; try (PreparedStatement ps conn.prepareStatement(checkSql)) { ps.setInt(1, id); try (ResultSet rs ps.executeQuery()) { rs.next(); if (rs.getInt(1) 0) { return false; // 有关联员工删除失败 } } } // 再执行删除 String deleteSql DELETE FROM department WHERE id ?; try (PreparedStatement ps conn.prepareStatement(deleteSql)) { ps.setInt(1, id); return ps.executeUpdate() 0; } } catch (SQLException e) { throw new RuntimeException(删除部门失败, e); } }列表页上的删除按钮我加了一个onsubmit确认问一句“确定删除该部门吗”。这个交互虽然简单但能挡住很多手滑误删。删除成功后必须用sendRedirect重定向回列表页不能用forward原因后面第 5 章专门讲。3.3 两个容易踩的坑空集合与重复提交第一个坑是查询结果为空。有人喜欢在 DAO 里返回null然后在 JSP 里判断if (null ! deptList)。这个习惯非常危险因为你无法保证 Service 层的每一处都对 null 做了判空一个疏忽就是 NullPointerException。我在自己的项目里定了条规矩查询集合永远返回空集合而不是 null。ListDepartment list new ArrayList(); // 初始就是空集合 return list;这样 JSP 页面用c:forEach遍历空集合时会自动什么都不显示不用单独写判空逻辑。第二个坑是删除和新增之后的重复提交。我刚写完删除功能时用的是forward转发到列表页。结果发现删除一次之后按一下 F5浏览器提示“是否重新提交表单”确认之后又删了一次。原因是转发只在服务器内部跳转浏览器地址栏还是原来的删除请求地址。改成重定向之后浏览器先收到一个 302然后重新发起一次 GET 请求到列表页地址栏变成/department?actionlist刷新就只会重新查询不会再次删除。4. 新增和修改把表单校验写进 Service 层4.1 新增部门的完整数据流与自增主键回显新增功能的页面是add.jsp表单里有两个字段部门名称和部门描述。提交之后走doPost我照样通过 action 分发Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action req.getParameter(action); if (add.equals(action)) { add(req, resp); } else if (update.equals(action)) { update(req, resp); } }add 方法接收参数、调用 Service、重定向private void add(HttpServletRequest req, HttpServletResponse resp) throws IOException { String name req.getParameter(name); String description req.getParameter(description); boolean success service.addDepartment(name, description); if (success) { resp.sendRedirect(req.getContextPath() /department?actionlist); } else { resp.sendRedirect(req.getContextPath() /department?actionadderror1); } }Service 层负责最核心的插入逻辑。这里有一个知识点很多人会忽略插入之后要拿到数据库自动生成的自增主键。业务场景很常见比如新增部门成功后跳转到这个部门的编辑页就需要先有这个 id 才能回显。public boolean addDepartment(String name, String description) { if (name null || name.trim().isEmpty() || name.length() 50) { return false; } String sql INSERT INTO department(name, description) VALUES(?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { ps.setString(1, name.trim()); ps.setString(2, description); ps.executeUpdate(); try (ResultSet rs ps.getGeneratedKeys()) { if (rs.next()) { int generatedId rs.getInt(1); System.out.println(新生成的部门ID generatedId); } } return true; } catch (SQLException e) { if (e instanceof SQLIntegrityConstraintViolationException) { return false; // 部门重名唯一约束触发 } throw new RuntimeException(新增部门失败, e); } }注意prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)这个重载。如果不传第二个参数执行完executeUpdate后getGeneratedKeys()很可能拿不到自增 id。这是 MySQL 驱动的一个实际行为我当时查了很久才发现是这个细节。4.2 修改部门的三步走查详情、回显、提交更新修改功能比新增多了一个步骤回显。用户点“编辑”时需要先把这条部门数据查出来填到表单里用户改完再提交更新。private void showEditPage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int id Integer.parseInt(req.getParameter(id)); Department dept service.findDepartmentById(id); req.setAttribute(dept, dept); req.getRequestDispatcher(/department/edit.jsp).forward(req, resp); }edit.jsp 的表单和 add.jsp 几乎一样区别只有两点第一form 里多了个隐藏域id第二输入框的 value 从dept对象里取。EL 表达式写起来很爽input typehidden nameid value${dept.id} input typetext namename value${dept.name} required textarea namedescription${dept.description}/textarea这里有个小坑${dept.description}如果数据库里是 null页面上会显示字符串null而不是空白。解决办法是回显时在 Service 里做一次 null 到空串的转换或者直接用 JSTL 的c:out加默认值。我嫌麻烦直接在实体类的 getter 里兜底public String getDescription() { return description null ? : description; }修改的 update 方法就是 SQL 的 UPDATE 语句把 name 和 description 按 id 更新。执行完同样重定向回列表页。核心点在于修改不涉及主键别把 id 写在 SET 子句里Where 条件只写 id。有的初学者把 SQL 写成UPDATE department SET name?, id? WHERE id?数据错乱得很隐蔽。4.3 前后端双重校验别把希望全押在 required 上很多人以为 HTML 表单里写了required属性后端就能高枕无忧了。这完全是错觉。required只是浏览器层面的提示用户可以绕过前端直接构造请求。所以后端必须有一层真正的校验。我把校验逻辑统一放在 Service 层新增和修改复用同一个方法private String validate(String name, String description) { if (name null || name.trim().isEmpty()) { return 部门名称不能为空; } if (name.trim().length() 50) { return 部门名称长度不能超过50; } if (description ! null description.length() 255) { return 部门描述长度不能超过255; } return null; }校验不通过时直接把错误消息返回给 ServletServlet 把消息放到 request 域里转发回原表单页面并在页面上显示。这套流程比返回一个笼统的 false 要友好得多用户能立刻知道错在哪。可能有人会问为什么不在 Servlet 里写校验因为校验属于业务规则放在 Servlet 会让你以后复用很痛苦。假设将来出现一个批量导入部门的接口如果校验逻辑只在表单提交的 Servlet 里批量导入就得重写一遍。放在 Service 层所有入口共用一套规则这才是分层设计的意义。5. 乱码和运行时错误两段高价值排错记录5.1 中文变问号从 JSP 到 MySQL 的四层排查这是整套案例里我花时间最长的一个 bug。新增部门时填“技术部”点击提交到数据库里看变成“????”。问题出在四个环节任何一个不对中文都会翻车。第一层是 JSP 页面本身的编码。新建的 JSP 文件顶部必须有% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8 %pageEncoding告诉 JSP 引擎用 UTF-8 读这个文件contentType告诉浏览器响应用 UTF-8 显示。第二层是请求编码。POST 请求的表单数据在请求体里Servlet 读取时必须先设定编码而且必须在第一次调用getParameter之前。我一开始写在doPost末尾完全无效。req.setCharacterEncoding(UTF-8);如果漏了这行浏览器用 UTF-8 编码提交的中文到了 Servlet 里会被当成 ISO-8859-1 解码妥妥乱码。第三层是 JDBC 连接 MySQL 时的编码。这一步就是前面 2.3 节 DBUtil 里 URL 上的characterEncodingUTF-8参数。它告诉 MySQL 驱动客户端发过来的字符串是 UTF-8 编码的请按 UTF-8 处理。第四层是表和数据库的字符集。我建表时用的CHARSETutf8mb4可以放心存中文。如果建库时默认字符集是 latin1那前面三层全对了也白搭。排查顺序建议从数据库往浏览器倒着查先看表结构字符集再看 JDBC URL再看请求编码最后看 JSP 头。实际踩坑时往往是同时改好几处才恢复正常的所以最好一次性把四层全部检查到位再测试。5.2 NullPointerException多半是参数名叫错了Day14 里我遇到的最多的运行时错误就是 NullPointerException。排查下来有一个规律绝大多数 NPE 的前一行都是request.getParameter的返回值直接调了方法。举个例子表单里input namedeptNameServlet 里写成String name req.getParameter(deptName); if (name.trim().isEmpty()) { ... } // NPEgetParameter拿不到参数时返回 null然后对 null 调用.trim()必炸。而且这个 bug 很隐蔽因为你肉眼扫代码看不出问题只有跑起来在控制台看到异常栈才能顺着at com.demo.servlet.DepartmentServlet.add()定位到具体哪一行。我的解决习惯是写一个安全取值方法所有 Servlet 入口统一走它private String getParam(HttpServletRequest req, String name) { String value req.getParameter(name); return value null ? : value.trim(); }这样至少不会因为一个 null 就把整个请求打崩。还有一类 NPE 来自 rs 取不到数据。比如findDepartmentById方法里如果查不到结果rs.next()返回 false你再去rs.getInt(id)就会抛SQLException不是 NPE。但如果你把查询结果直接赋给实体类代码又不判空在 Servlet 里传dept.getId()就会 NPE。所以查询单个对象时查不到就返回 null 没问题但使用方必须判空查多条的就按 3.3 节的习惯返回空集合。5.3 刷新就重复提交转发和重定向到底差在哪这个坑其实在 3.2 节已经提过但我还想单独拎出来说因为它太典型了。用forward转发做新增成功跳转会有什么后果req.getRequestDispatcher(/department/list.jsp).forward(req, resp);转发是服务器内部动作浏览器地址栏还停留在/department?actionadd。用户看到新增成功想刷新一下列表浏览器重新提交上一次的 POST 请求结果又插了一条一模一样的部门记录而且因为 name 有唯一约束这次刷新会触发重复键异常。重定向写的是resp.sendRedirect(req.getContextPath() /department?actionlist);浏览器收到 302 响应后会主动发起一个新的 GET 请求地址栏变成列表中。/GET 请求天然是安全幂等的刷新一万次只是重新查一次列表不会产生副作用。这个原则记住一句话凡是会修改数据的请求处理完一定用重定向不要用转发。此外还有一个很小的性能细节重定向比转发多一次网络往返但这个代价换来的正确性完全值得。6. 从部门系统到员工系统下一步扩展建议这一整套部门系统跑通之后第 15 天的计划就非常清晰了直接套用同样的分层结构去做员工系统。员工表的字段更多卡片更复杂还需要在设计部门删除时处理“部门下有员工”的联动。我的具体建议是往这几个方向扩展第一列表页加上分页。LIMIT ? OFFSET ?两行 SQL 就能实现还要配合一个SELECT COUNT(*)算总页数。这个练习能帮你理解为什么前端分页和后端分页对大数据量来说完全是两个概念。第二删除改成批量删除。用 checkbox 勾选多个部门提交时把 id 拼成逗号分隔的字符串后端拆开后用动态拼接生成 IN 占位符。这个练的是字符串处理和 SQL 动态拼接的功力。第三把查询条件从“部门名称模糊搜索”扩展到“按创建时间范围查询”。你会发现写一条带日期参数的 SQL 也没多难但日期格式的前后端传递会小小折磨你一下早点踩坑早点长记性。我做完这套部门 CRUD 之后最大的变化是不再害怕看完整代码了。以前点开一个项目总觉得眼花缭乱现在哪怕是大项目的 Controller-Dao 分层我也能顺着请求路径逐个摸清。这就是第 14 天这一整套案例开发给我的最大回报。最后再分享一个习惯每次写完一个功能我会在浏览器里把“正常操作、空输入、超长输入、连续点击提交”这四类情况各测一遍。这套部门系统后来能顺利扩展成员工系统而没有返工靠的就是前期这些看起来有点笨的测试。如果你也在学 Java 的道路上走到了类似阶段建议别急着开新章节先把手头的这套 CRUD 改到滚瓜烂熟你会发现后面很多 Web 框架的引入都变得顺理成章了。

相关推荐

Docker 部署 Nginx 1.24 实操指南:从镜像选型到生产配置
Docker 部署 Nginx 1.24 实操指南:从镜像选型到生产配置

这问题我太熟了。上周帮一个朋友把跑了三年的老站从裸机 Nginx 迁到容器里,他盯着终端问我的第一句话就是:docker 部署 nginx 1.24,到底稳不稳?这个疑问太典型。很多人一听到“容器”就觉得性能要打折、配置要成大麻烦&#xff0c… · 2026/9/26 20:14:59

思科网络设备巡检命令大全:交换机/路由器/无线控制器排查实战
思科网络设备巡检命令大全:交换机/路由器/无线控制器排查实战

做网工这些年,我越来越觉得,巡检才是检验基本功的试金石。别看思科网络设备巡检命令翻来覆去就是那几条 show 命令,真到设备告警、业务中断的时候,能不能从输出里一眼看出隐患,靠的就是平时对每一个字段背后含义的理解… · 2026/9/26 20:14:53

STGCN实战:PyTorch实现时空图卷积网络进行交通流预测
STGCN实战:PyTorch实现时空图卷积网络进行交通流预测

简介:这份代码基于PyTorch框架实现了STGCN时空图卷积网络,源自IJCAI 2018论文官方实现。它面向人体行为分析、序列数据建模等研究场景,适合希望深入理解时空图卷积原理的开发者、学生或研究人员。压缩包共12个文件,类型涵盖Python… · 2026/9/26 20:14:38

Atlas 300V 24G跑YOLO实战指南:环境配置、模型转换与性能调优
Atlas 300V 24G跑YOLO实战指南:环境配置、模型转换与性能调优

“atlas 300v 24g 是运算加速卡吗?”这句搜索词每隔几天就会出现在我的后台,凡是搜这句话的人,下一步九成都会接上“部署yolo”。原因也很直白:大显存、价格比同显存的训练卡便宜不少、看起来插上就能跑AI模型,确实像个… · 2026/9/26 20:50:13

黑苹果OpenCore 0.6.3 EFI制作全攻略:从零定制config.plist
黑苹果OpenCore 0.6.3 EFI制作全攻略:从零定制config.plist

玩黑苹果的人都知道,真正决定一台机器能不能顺利进系统的,不是那个安装镜像,而是 EFI 分区里的那一整套文件。OpenCore 0.6.3 是 2020 年底开始被大规模采用的引导器版本,用这套引导器配合按机器硬件定制出来的 EFI 目录&#xff… · 2026/9/26 20:50:13

WinHex中文设置与数据恢复实战:语言包安装和报错排查
WinHex中文设置与数据恢复实战:语言包安装和报错排查

很多第一次接触 WinHex 的朋友,多半是被同事或教程推荐来做数据恢复、磁盘镜像或底层二进制分析。打开软件的一瞬间,满屏英文菜单确实让人头大——File、Edit、Search、Tools,这些词本身不难,但组合起来找某个功能就费劲了。尤其数… · 2026/9/26 20:50:07

ax是什么?深入解析Agent eXecution智能体基座原理与K8s集成实践
ax是什么?深入解析Agent eXecution智能体基座原理与K8s集成实践

1. 项目概述:从“ax”这个神秘缩写切入,搞懂它到底是什么、能干什么、为什么值得花时间深挖最近在多个技术社区和开源项目讨论区里,“ax”这个词高频出现,但很少有人讲清楚它到底指什么。它既不是某个知名商业产品的代号&#xff… · 2026/9/26 20:50:00

IDEA推代码到Gitee码云全流程:SSH密钥配置与分支冲突解决指南
IDEA推代码到Gitee码云全流程:SSH密钥配置与分支冲突解决指南

先说个我最近常遇到的场景:本地项目在 IDEA 里编译运行一点问题没有,结果到了要提交交付的时候,团队的人说“把代码传到码云仓库”,不少同学就卡在第一步。只记了一句git push,后面跟着就是认证失败、分支对不上、远程… · 2026/9/26 20:50:00

WAF规则层注入绕过全解析:原理、手法与加固
WAF规则层注入绕过全解析:原理、手法与加固

WAF这东西,圈里人对它的态度一直很分裂。有人把它当万能保险箱,规则一开就高枕无忧;有人觉得它就是个摆设,随便一个变形的注入就能打穿。我做了几年安全和运维相关的工作,大大小小的WAF也见了不少,说实话&a… · 2026/9/26 20:50:00

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

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

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码