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

原生Servlet+JDBC点餐系统:从MVC分层到事务管理的实战指南

发布时间:2026/9/24 21:48:23 来源:云帆数科 栏目:资讯中心
原生Servlet+JDBC点餐系统:从MVC分层到事务管理的实战指南
简介这是一份基于MVC开发模式的原生ServletJDBC点餐系统完整项目包面向Java Web学习者、毕业设计及课程设计人群用于掌握Servlet核心处理流程、数据库交互与项目分层思想。包内共139个文件涵盖81张界面素材图片、20个JSP页面、6个Java源文件及对应class文件、7个依赖JAR包同时还包含SQL建表脚本、需求文档和项目配置文件便于直接导入IDE运行与二次开发。压缩包仅3.76MB结构紧凑适合快速上手。目前已有61人学习下载。通过该项目可系统梳理Servlet生命周期、doGet/doPost请求分发、JDBC增删改查、HttpSession会话管理、用户认证与权限控制等关键知识点并结合前端页面理解MVC各层协作方式是巩固Java Web基础、积累实战经验的实用资源。1. 点餐系统为什么还要用原生ServletJDBC一个MVC项目能学到什么在Java Web里基于MVC开发模式用原生ServletJDBC写一个点餐系统是最能看清框架底层的练手项目。现在Spring Boot遍地都是但真让你跑这样一套原生项目很多人会反问“这年头还写这个”——直到笔试遇到请求生命周期、事务边界或者接手老系统时才发现底层没打牢。点餐系统恰好把菜单查询、下单扣库存、订单事务这些最核心的Java Web能力完整过一遍业务实体清晰交互链路完整。这个方向适合正在学Java Web、缺一个完整项目经验的初学者也适合想搞清楚框架底层逻辑的开发者。把这套项目跑通再回头看任何MVC框架都能对应上它帮你省掉了哪些重复劳动。2. MVC三层在点餐系统里的落地包结构、请求流转与职责边界2.1 包结构先分对entity、dao、service、servlet各管什么很多项目的分层乱根子在于一开始没想清楚每一层的产出物是什么。我一般会先按这个结构建工程com.restaurant ├── entity // ModelDish、Order、OrderItem字段对应数据库表 ├── dao // 数据访问DishDao、OrderDao、OrderItemDao ├── service // 业务DishService、OrderService ├── servlet // 控制层DishServlet、OrderServlet、UserServlet ├── filter // CharacterEncodingFilter 编码过滤器 ├── util // DBUtil连接获取与关闭 └── webapp ├── pages // JSPmenu.jsp、order.jsp、orderList.jsp └── static // css、js这是MVC在Java Web项目里最常见的映射方式。entity里只放字段和getter/setter不掺任何数据库操作——有人习惯把SQL写在实体类里短期看省事等订单表和菜品表一关联实体类就变成谁都不敢动的臭泥潭。dao层的方法严格按数据操作命名findById、findAll、insert、updateStatus它不回答“这个订单能不能取消”这类问题能不能取消是service层的职责。service层才是业务规则的所在地下单时要校验菜品是否存在、库存够不够写订单表和订单明细要放在同一个事务里。servlet层只做三件事——从request取参数、调service、决定返回JSP还是JSON。判断分层是否合理的办法很简单如果数据库表结构改了你希望只动entity和dao如果需求变了你希望只动service如果页面交互变了你希望只动servlet和View。任何一层的变化穿透到其他层说明边界划错了。servlet里不直接写SQL这条建议当成硬性规范不然最终一定有人图省事把JDBC写进servlet让Controller变成数据访问层。这个分层的核心依据是“变化的方向不同”SQL变化来自表结构调整业务变化来自产品需求请求处理变化来自HTTP协议和前端约定。三个方向绑在一个类里改一处就要重新测整个链路。2.2 请求流转从点击“下单”到数据库一次完整路径以点餐系统的下单为例完整请求生命周期可以拆成七步。第一步浏览器表单POST到/order/create。第二步web.xml里的Servlet映射或WebServlet(/order/create)注解把URL交给OrderServlet。第三步doPost方法先执行request.setCharacterEncoding(UTF-8)再getParameter(dishId)拿到参数。第四步调用OrderService#createOrderservice里校验菜品是否存在、计算价格。第五步OrderDao创建订单主表记录OrderItemDao写入明细。第六步两个insert成功后connection.commit()失败rollback()。第七步OrderServlet把结果转发到订单结果页或输出JSON。用web.xml管理映射的方式很多老项目还在用值得看一次servlet servlet-nameorderServlet/servlet-name servlet-classcom.restaurant.servlet.OrderServlet/servlet-class /servlet servlet-mapping servlet-nameorderServlet/servlet-name url-pattern/order/create/url-pattern /servlet-mapping这段配置要说明两个点。servlet-class必须是包含包名的完整类名写成OrderServlet会直接ClassNotFound。servlet-name只是内部标识和类名可以不一致但全项目要唯一否则容器启动时就会报Servlet [xxx] is already mapped。注解方式更省事但web.xml的好处是不同环境的URL映射可以通过配置文件替换不用重新编译。第七步有个容易忽略的坑如果你在doGet里转发到JSP转发后JSP会继续走同一次请求此时request域里的对象能直接取到如果用了重定向浏览器发起第二次请求request域是空的数据必须用session或重新查一遍。点餐系统的“下单后跳转订单列表”适合用重定向因为刷新页面时不能把同一个订单再提交一次。2.3 为什么点餐系统适合用MVC菜单模块与订单模块互不污染点餐系统的业务实体多且规则集中菜品管理侧重查询订单管理侧重写入和状态流转用户管理侧重安全校验。三个模块如果写在一个Servlet里最直接的后果是doGet和doPost堆满if-else分支每加一个功能都要从头读一遍代码改菜的接口时手一抖就可能影响下单逻辑。用上MVC后菜单查询只走DishServlet→DishService→DishDao一条链订单走OrderServlet→OrderService→OrderDao另一条链。两边servlet互不引用即使后面要把菜品模块从数据库读取改成Redis缓存也只是替换DishService内部实现OrderServlet一行都不用动。这就是第2.1节说的“变化隔离”一个模块的改动不越过自己的边界。从请求流转还能看出另一层价值分层给了你一个天然的日志埋点位置。servlet入口统一记录“谁在什么时间请求了什么功能”service层记录“这笔订单算出来多少钱”dao层记录“实际执行了哪条SQL”。出问题的时候顺着这三层日志看定位速度比在一个大方法里翻打印语句快得多。这层能力是框架帮不了你的项目结构自己决定。2.4 什么时候用MVC反而笨重不要无脑套分层MVC不是银弹。点餐系统里那种单表维护页面比如管理员改一条轮播图配置只有三个字段走MVC要建entity、dao、service、Servlet、JSP五个类成本远大于收益。遇到这种纯CRUD直接在Servlet里用JDBC更新一条记录更务实。判断标准是业务规则的数量规则超过两条MVC帮你隔离复杂度规则为零分层就是给自己找事。我在实际项目里会把它当一个原则MVC是给“会变化的业务”用的。点餐系统里订单状态流转待接单、制作中、已完成、已取消是天然会持续加规则的部分必须走完整分层而餐桌编号这种字典数据怎么改都是update语句就不要强行套框架。3. 用原生Servlet实现点餐核心接口菜单查询、下单与订单展示3.1 菜单列表查询从JDBC连接配置到JSON输出先写DBUtil所有dao的前提public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/restaurant ?useSSLfalseserverTimezoneAsia/Shanghai; 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(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }这段代码是全项目的数据库入口几个参数在后端开发里最容易出问题。useSSLfalse在MySQL 8.x上是必加的不加它一些版本会在握手时尝试SSL加密本地自签名证书校验失败直接告警serverTimezoneAsia/Shanghai解决的是服务器时区与驱动默认时区不一致导致的日期偏差缺失时查询结果的时间字段可能少8个小时。Class.forName在JDBC 4.0之后可以省略因为驱动包里有SPI自动注册文件但显式写出来在Web容器类加载器隔离的场景下更稳。再写DishDao的菜单查询public ListDish findAll() throws SQLException { String sql SELECT id, name, price, description FROM dish WHERE status 1; ListDish list new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { Dish d new Dish(); d.setId(rs.getInt(id)); d.setName(rs.getString(name)); d.setPrice(rs.getBigDecimal(price)); d.setDescription(rs.getString(description)); list.add(d); } } return list; }这里用try-with-resources关闭三个资源是JDBC里最低成本的防连接泄漏写法比在finally里一个个close要稳因为finally块里一旦第一个close抛异常后面的close直接不执行。price字段用BigDecimal接收而不是double金额计算最怕浮点误差BigDecimal在价格和库存场景是写死的规定。SQL里WHERE status 1表示只查在售菜品下架菜品不在菜单里出现这个过滤条件放SQL里比放Java里更合适因为数据库可以利用索引也避免把无谓的数据传到业务层。DishServlet负责输出JSONWebServlet(/dish/list) public class DishServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType(application/json;charsetUTF-8); resp.setCharacterEncoding(UTF-8); ListDish dishes new DishService().findAllDishes(); String json new Gson().toJson(dishes); resp.getWriter().write(json); } }一个很容易被忽略的执行顺序setCharacterEncoding(UTF-8)必须在getWriter()之前调用否则编码设置不生效页面拿到的中文会变成乱码。setContentType里已经带了charset理论上可以不写第二行但我习惯都写因为某些容器对这两行的处理有差异。如果开发环境是VSCode记得把Maven的编译输出和Tomcat的部署目录对应起来VSCode本身不自带Servlet容器支持需要装Tomcat插件并配置好classpath不然每次改动都要手动拷贝class文件。3.2 下单接口服务端定价与入库的关键代码下单是点餐系统里业务规则最集中的地方。前端传来的参数只有dishId和quantity价格必须后端根据菜品的当前价格计算。如果让前端把price一起POST上来改一下请求体就能1块钱下单这是点餐系统最典型的越权漏洞。WebServlet(/order/create) public class OrderServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { req.setCharacterEncoding(UTF-8); int userId Integer.parseInt(req.getParameter(userId)); int dishId Integer.parseInt(req.getParameter(dishId)); int quantity Integer.parseInt(req.getParameter(quantity)); if (quantity 0) { resp.sendError(400, quantity must be positive); return; } try { Order order new OrderService().createOrder(userId, dishId, quantity); resp.setContentType(application/json;charsetUTF-8); resp.getWriter().write({\orderId\: order.getId() }); } catch (BusinessException e) { resp.sendError(400, e.getMessage()); } } }参数校验放在servlet入口至少挡掉三种非法输入quantity为负数、dishId不存在、直接POST伪造的price字段。业务异常和系统异常分开处理也很重要——BusinessException返回400给用户看SQLException记录日志返回500不要让用户看到一长串堆栈。Integer.parseInt这一行如果前端传了非法字符串会抛NumberFormatException这个属于参数格式错误实际项目里要么用包装类型判空要么用正则过滤别让异常越过servlet直接打到容器默认错误页。OrderService里的核心逻辑在上一章的代码块提过再补一个重要细节创建订单时要让订单状态处于“待支付”而不是“已完成”。很多新手把状态机的起点和终点搞混订单一创建就完成后面接支付模块时发现状态没法流转。3.3 订单查询与状态更新PreparedStatement占位符的硬性要求点餐系统里“按用户查历史订单”“按订单号查详情”都很常用查询订单号来自用户在页面的输入。这种外来值拼SQL字符串是绝对红线public ListOrder findByUserId(int userId) throws SQLException { String sql SELECT id, dish_id, quantity, total_price, status, create_time FROM orders WHERE user_id ? ORDER BY create_time DESC; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, userId); try (ResultSet rs ps.executeQuery()) { ListOrder orders new ArrayList(); while (rs.next()) { orders.add(mapRow(rs)); } return orders; } } }PreparedStatement的?占位符会自动转义特殊字符从源头堵住SQL注入这是它比Statement拼字符串最本质的优势。同时预编译语句在数据库端可以被重复执行复用同一SQL跑多遍时的解析成本可以摊薄。这段代码还有个细节内层ResultSet单独用一个try-with-resources外层PreparedStatement的关闭会自动把ResultSet也关掉这里写两层只有一个目的——可读性让读者一眼看出ResultSet的生命周期边界。4. JDBC连接与事务管理连接池、URL参数与下单扣库存的事务边界4.1 为什么不能每个请求新建连接连接开销与并发排队3.1节的DBUtil用DriverManager.getConnection教学项目没问题但点餐系统一旦有真实并发——比如食堂中午12点所有人同时下单——问题立刻暴露。每次getConnection都要经历TCP握手、MySQL认证、分配线程资源一次连接耗时几十毫秒并发上去后数据库最大连接数一满后续请求全部排队页面表现是“转圈”。常见做法是引入连接池。Druid和HikariCP都是成熟选择教学项目用Druid多一些因为自带监控页面/druid/index.html能看到活跃连接数、SQL执行耗时、事务提交回滚次数。连接池的核心参数你只需要理解五个initialSize是启动时建立的连接数maxActive是连接上限minIdle是最小空闲数maxWait是拿不到连接时的最大等待毫秒数validationQuery是心跳检测SQL。点餐系统初期规模配成3、10、2、3000、SELECT 1就够用了后面监控数据出来再调。4.2 MySQL 8.x连接参数把useSSL、sslmode、时区一次讲清这部分值得单独开辟一节因为JDBC URL的每个参数背后都对应一个真实报错。先看一份完整的Druid配置driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/restaurant?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8mb4allowPublicKeyRetrievaltrue usernameroot password123456 initialSize3 maxActive10 maxWait3000 minIdle2 validationQuerySELECT 1useSSLfalse对应MySQL 8.x默认开启SSL本地自签名证书会导致连接报错所以开发环境必须关掉这也就是很多新手遇到的“连接超时”“SSLHandshakeException”的根因。在高版本Connector/J里控制SSL的官方参数其实是sslMode可选DISABLED、PREFERRED、REQUIRED、VERIFY_CA、VERIFY_IDENTITY几种useSSLfalse与sslModeDISABLED描述的是同一件事如果你看到的报错信息带SSL字样先检查这两个参数是不是互相冲突。serverTimezone不设置时驱动用服务器默认时区常见的坑是差8小时数据入库时看着不对。characterEncodingutf8mb4是为了存emoji和生僻字MySQL 8.x的表也建议建表时统一DEFAULT CHARSETutf8mb4光改URL不改表结构同样会报Incorrect string value。allowPublicKeyRetrievaltrue专门解决MySQL 8.x的caching_sha2_password认证插件在非SSL连接下报Public Key Retrieval is not allowed的问题使用root账号时很常见。五个参数里哪怕漏一个项目都可能跑不起来这类问题大多数时候不是代码bug是配置参数没对齐。4.3 下单事务的commit和rollback边界位置决定成败JDBC默认autocommittrue每条SQL各自成一个事务。这就意味着在2.2节的第七步如果忘了手动管理事务订单主表和订单明细表之间没有原子性。正确写法是把“写订单”“写明细”“扣库存”包进同一个手动事务public void createOrderWithTransaction(int userId, int dishId, int quantity) throws SQLException { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); DishDao dishDao new DishDao(conn); OrderDao orderDao new OrderDao(conn); OrderItemDao itemDao new OrderItemDao(conn); Dish dish dishDao.findByIdForUpdate(dishId); if (dish null || dish.getStock() quantity) { throw new BusinessException(库存不足); } orderDao.insert(new Order(userId, dishId, quantity)); itemDao.insertOrderItem(item); dishDao.decreaseStock(dishId, quantity); conn.commit(); } catch (Exception e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException re) { re.printStackTrace(); } } throw new SQLException(下单失败事务已回滚, e); } finally { if (conn ! null) { conn.setAutoCommit(true); conn.close(); } } }三个细节值得记。第一findByIdForUpdate实际执行的是SELECT ... FOR UPDATE它在事务内锁定菜品行阻止另一个并发事务读到同一份库存然后重复扣减这是“不超卖”的第一道防线。第二conn.setAutoCommit(true)还原这段经常被漏掉如果连接来自连接池还原不彻底的话下一个请求拿到这条连接时仍然处于事务中忘记commit会导致数据悄悄丢失。第三rollback和close的顺序不能反先rollback再closeclose时连接池才会认为连接是干净的反过来先把连接还给池子再回滚等于把脏连接暴露给了其他请求。注意写事务代码时尽量让dao方法接收Connection参数而不是内部自己new连接否则你无法控制多个DAO在同一个连接上执行——这也是为什么第2.1节里建议在service层统一管理连接的生命周期。4.4 隔离级别与超卖要不要上悲观锁超卖场景在点餐系统里表现为库存只剩最后一份A和B同时提交订单两个事务都读到stock1都通过了库存判断然后都执行扣减最终库存变成-1。4.3节的SELECT ... FOR UPDATE是悲观锁方案它直接锁行简单可靠代价是并发下吞吐受限。乐观锁方案给菜品表加version字段UPDATE dish SET stock stock - ?, version version 1 WHERE id ? AND version ?返回受影响行数如果小于1说明version已被其他人改过整个下单事务回滚重试。乐观锁适合读多写少、冲突概率低的场景点餐系统属于典型的中低并发写场景用悲观锁也不会有什么性能压力。如果哪天业务变成了限时秒杀那就该把库存挪到Redis里做原子扣减MySQL只作为最终落库这就超出本文范围了。5. 点餐系统避坑指南从404到中文乱码5个高频问题排查5.1 Servlet404或ClassNotFoundException构建产物与URL映射先查现象Tomcat正常启动浏览器访问/dish/list返回404日志里没有任何异常。原因排查有固定顺序。第一步检查IDEA或Eclipse的编译输出目录下有没有DishServlet.class很多人改了代码没重新编译Tomcat跑的其实是旧class。第二步核对注解WebServlet(/dish/list)和访问路径完全一致大小写、开头斜杠、尾斜杠都不能错访问路径漏掉开头的/会变成相对路径解析这是Servlet 404里比例最高的原因。第三步看项目是不是以war包形式部署Tomcat解压到webapps下的目录和实际项目target目录经常不是同一个改了代码但没重新打包就会出现在“自己机器上能跑、部署到服务器就是404”的怪象。解决IDEA里执行Build-Rebuild Project清空Tomcat的work目录后重启。这个问题的本质不是代码bug而是构建产物和运行环境不一致。5.2 MySQL连接报错驱动版本、useSSL、serverTimezone三件套现象服务启动时日志抛ClassNotFoundException: com.mysql.jdbc.Driver或持续报Communications link failure。原因MySQL 8.x版本类名从com.mysql.jdbc.Driver换成了com.mysql.cj.jdbc.Driver旧类名在新版本驱动里已标记废弃反向的问题也存在用5.1.x驱动连MySQL 8.x服务器协议版本不兼容。除此之外8.x默认SSL握手本地开发环境没配置证书时SSLSocket连接会间歇性失败这类问题表现不固定最容易被当成网络抖动。解决驱动jar版本与MySQL服务器大版本对齐JDBC URL带上第4.2节里的useSSL、serverTimezone、allowPublicKeyRetrieval三个参数。这里提一句JDBC自动注册机制在绝大多数情况下可用但如果你的Web容器有类加载器隔离比如Tomcat的部分共享库显式Class.forName能少踩一个坑。5.3 中文乱码request、response、数据库三层一起查现象页面菜品名显示è??å??或者数据库里存进去的就是乱码。原因乱码从来不是单一原因一般要查三层。请求方向没设request.setCharacterEncoding(UTF-8)或者设了但它出现在getParameter之后等于没设响应方向没设setContentType(text/html;charsetUTF-8)容器用默认ISO-8859-1输出最隐蔽的是JDBC URL里没有characterEncodingutf8mb4并且数据库表本身charset不是utf8mb4。解决三层统一。Servlet里第一行就设request编码输出前设response的ContentTypeJDBC URL带编码参数建表语句写死DEFAULT CHARSETutf8mb4。还有一个容易漏的是IDE的properties文件编码如果db.properties里写的密码含中文而IDE默认存成GBK读出来就是另一串字符这类问题看文件编码一眼就懂。5.4 本地正常、部署到服务器就连不上数据库现象点餐系统在Windows本机跑没问题部署到Linux服务器后访问菜单接口报Communications link failure数据库日志提示连接被拒绝。原因从网络的五个点逐个排查。第一MySQL没有开启远程访问默认只允许localhost连接第二云服务器安全组或iptables只放行了80/4433306没有放行第三JDBC URL里写死了localhost或127.0.0.1但数据库其实在别的机器上第四防火墙过了但MySQL账号的host字段是localhost需要通过授权语句把host改成允许的网段第五服务器时区与MySQL时区不一致导致时间字段偏移8小时这个严格说不算连不上但数据相貌不对。解决数据库账号host授权到%或指定内网IP安全组放行3306JDBC URL里的地址抽到配置文件里用环境变量注入避免每次部署都改代码。生产环境最好不要把3306直接暴露到公网走内网地址或者跳板机更稳妥。5.5 连接泄漏没有报错但连接数悄悄占满现象系统连续运行几天后show processlist里全是Sleep状态连接max_connections被打满新请求全部超时重启Tomcat又能撑几天。原因代码里某个分支忘记关闭Connection。最典型的两个场景DAO内部new DBUtil().getConnection()但返回前没close或者用了finally手工关闭但ResultSet的close抛了异常导致后面两个资源没关掉。这种问题不会立刻报错是那种慢慢把系统拖死的慢性病。解决全面改用try-with-resources引入连接池设置removeAbandonedTimeout或HikariCP的connectionTimeout。观察手段也很简单MySQL执行show status like Threads_connected配合连接池监控面板看哪个SQL长期占着连接不释放。连接泄漏是最常见的“开发环境永远复现不了、生产环境几天挂一次”的问题属于典型的血泪型踩坑。6. 点餐系统的下一步改造参数化、流式查询与接口验证技巧点餐系统跑通之后值得做三件事。第一把DBUtil里的URL、账号密码全部搬进db.properties用Properties.load读取配合环境变量区分开发和生产两套配置。这样项目在本地、测试、生产之间迁移时只需要替换配置文件不需要重新编译源码。第二如果点餐系统后期加了销售报表比如统计一周内每道菜的销量查询结果可能上万行一次性executeQuery会把所有结果加载进内存。这时可以用MySQL JDBC的流式查询Statement设置setFetchSize(Integer.MIN_VALUE)让驱动按游标一行行取回内存占用从O(n)降到O(1)。但要注意流式查询期间连接不能执行第二条SQL结果集使用完必须立刻关闭。第三养成用curl验证接口的习惯。前端页面还没写的时候接口能不能用一条命令就能断言curl -X POST http://localhost:8080/restaurant/order/create \ -d userId1dishId3quantity1 curl http://localhost:8080/restaurant/dish/list | jq .[0]验证的重点不是“有没有返回”而是三件事状态码是200还是异常4xx/5xxJSON字段名和前端约定的是否一致比如把orderId写成id前端解析时直接undefined异常请求比如dishId不存在、quantity-1时服务端有没有正确返回400而不是抛500。这三个断言全部通过再打开浏览器看页面。我做这类ServletJDBC项目时有个习惯每写完一个接口先用curl把正常路径和异常路径各打一遍确认状态码和返回结构再碰JSP页面。这个习惯帮我避开过很多次“前端说后端有问题、后端说前端没对齐”的扯皮。希望帮到你。本文还有配套的精品资源点击获取

相关推荐

YOLOv8古籍保护系统实战:从数据标注到界面部署
YOLOv8古籍保护系统实战:从数据标注到界面部署

简介:这份《基于YOLOv8的古籍保护系统》资源包专为毕业设计、课程设计或相关深度学习项目开发而整理,面向计算机、人工智能等专业的学生与初学者。内容涵盖完整的YOLOv8目标检测源码、古籍标注数据集、可视化交互界面及部署说明,可用于快速搭… · 2026/9/24 21:48:23

C#调用OpenXml操作Word嵌入文件:OLE对象与嵌入包详解
C#调用OpenXml操作Word嵌入文件:OLE对象与嵌入包详解

C#调用OpenXml操作Word文档这个系列写到第20篇了。前面十几篇我们把段落、表格、图片、页眉页脚、修订批注都过了一遍,今天专门说一个平时问得比较多、但网上讲得很少的内容:嵌入文件类。简单说,就是在Word里能双击打开的Excel表格、PDF文档、… · 2026/9/24 21:48:23

企业智能呼叫中心建设实施方案:从话务测算到部署避坑指南
企业智能呼叫中心建设实施方案:从话务测算到部署避坑指南

简介:《企业智能呼叫中心系统建设实施方案.docx》是一份可直接参考的智慧客服系统建设方案范文,适合作企业信息化规划、售前方案撰写、项目申报或毕业设计框架参考。文档以呼叫中心升级为主线,从项目背景、业务需求到整体解决方案逐层展开&am… · 2026/9/24 21:48:10

深度学习新闻分类推荐系统:从TextCNN到个性化推荐
深度学习新闻分类推荐系统:从TextCNN到个性化推荐

简介:这份基于深度学习的新闻分类推荐系统Python实现源码,是专为课程设计与期末大作业准备的高分项目,下载后无需修改即可运行,适用于需要快速交付完整课题的高校学生。系统涵盖新闻数据预处理、文本分类模型训练、推荐逻辑展示等… · 2026/9/24 23:59:53

汽车电子底层软件开发:AUTOSAR与CAN总线实战解析
汽车电子底层软件开发:AUTOSAR与CAN总线实战解析

1. 这门“汽车电子底层软件开发就业课”到底在教什么?——不是写个LED闪烁就能上岗的很多人看到“汽车电子底层软件开发就业课”这个标题,第一反应是:不就是嵌入式C语言单片机CAN通信?刷几道LeetCode、调通一个STM32 CAN收发例程&… · 2026/9/24 23:59:53

Vim基础操作全攻略:保存退出、模式切换与高频命令实战
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保… · 2026/9/24 23:59:53

Python+CNN车牌识别实战:从数据预处理到模型训练与部署
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据… · 2026/9/24 23:59:53

AI元人文:从工具使用到思维重构的深度探索
AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决… · 2026/9/24 23:59:53

《AI Agent 场景应用 - MobileOpenClaw》第5-9节:会话上下文细化处理实战指南
《AI Agent 场景应用 - MobileOpenClaw》第5-9节:会话上下文细化处理实战指南

文档教程后端 【免费下载链接】CodeGuide :books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助,请给予支持(关注、… · 2026/9/24 23:59:47

了解更多?预约专属演示

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

企业微信二维码