SpringMVC 5+Hibernate 5+SpringSecurity 5+Maven工程骨架搭建与避坑
简介一套基于SpringMVC 5.1.5、Spring 5.1.5、SpringSecurity 5.1.4与Hibernate 5.3.7的Maven工程面向Java Web开发者和系统架构师可用于商业系统底层框架搭建。工程已开发好用户、角色、权限模块并附带资源数据、角色数据、管理员数据及MySQL建库脚本数据库层无需从零设计能明显缩短项目启动周期。前端选用SmartAdmin设计风格基于Bootstrap与AJAX对话框操作兼容JQuery且支持6种页面风格与配色交互改造灵活。资源包共786个文件大小约16.03MB以324个js脚本、174个png图片、61个jpg、35个gif、32个css及18个jsp等为主前端素材与脚本占比高其余Java源码、xml配置和sql脚本集中于权限认证、实体映射及初始化数据。目前已有349人学习下载适合具备Servlet/JSP或Spring基础、需要快速获得成熟权限体系参考的开发者。1. SpringMVC 5 Maven 工程骨架把 Spring 5、Hibernate 5、SpringSecurity 5 一次装到能跑每当要起一个新 Web 项目我第一反应都是先找一份已经跑通的 SpringMVC 5 Spring 5 SpringSecurity 5 Hibernate 5 Maven 工程而不是从空目录开始写 pom。因为这四个组件单独拎出来都有文档合在一起后版本兼容、容器装配、权限过滤器顺序任何一处错位都要花好几个小时排查。这份基础工程骨架把分层目录、数据源、事务、登录认证都预设好了拿到后可以直接往里面加业务模块。适合三类人要快速起商业系统后台的团队、维护老 SSH 项目想升级到 Spring 5 的开发者以及想少踩框架整合坑的后端新人。2. Maven 依赖装配技术栈选型与 pom.xml 的落地细节依赖装配是这套骨架里最不需要发挥创造力的地方但恰恰最容易翻车。版本错一位报的可能不是 Spring 的错误而是 Hibernate 内部类的 NoSuchMethodError藏在启动日志深处不熟悉的人会当玄学处理。我习惯先把依赖坐标锁死再谈业务。2.1 选型理由为什么是 Spring 5 而不是 Boot先说清一件事能用 Boot 的项目很多但传统 MVC 工程在存量系统里生命力依然很强。这个骨架选 Spring 5 SpringMVC 5不是为了复古而是显式 XML 配置下Bean 的装配路径非常清楚。数据源由谁建、SessionFactory 归谁管、事务管理器是谁一层一层看得见。团队里新人接手时翻配置比翻代码更快能建立整体认知商业系统后期交接成本会低不少。Spring 5 本身要求的基线是 JDK 8对比 Spring 4 时代的老工程最大的改进是响应式 WebFlux 和更干净的注解支持。这里用的是传统 Servlet Web 栈不涉及 WebFlux重点是 Spring 5 对 Hibernate 5 的 ORM 集成更顺畅Bean 类型自动装配也更准确。Hibernate 5 和 MyBatis 的选择我一般看业务形状。后台管理系统、OA、电商后台这类 CRUD 密集、实体关联层级分明的场景Hibernate 的自动映射和脏检查能省大量 SQL反过来报表系统、多表聚合查询多的场景更适合 MyBatis。这套骨架定位商业系统通用后台Hibernate 5 更合适。SpringSecurity 5 相比 4 的最大变化是密码编码方案强制化BCrypt 成了默认要求正好匹配商业项目对登录安全的基本要求。提示Spring Boot 也能整合同样技术栈但这个资源的价值恰恰在于保留 XML 显式装配适合不想被自动配置“黑匣子”吞掉过程的项目。2.2 pom.xml 依赖清单版本统一管理避免传递依赖打架我一般会把版本锁在下面这组兼容搭配上Spring 5.1.9、SpringSecurity 5.1.9、Hibernate 5.3.7 是同一时段发布、实测过兼容性的组合。写进 properties 统一管理后续回归升级只动一处。properties spring.version5.1.9.RELEASE/spring.version hibernate.version5.3.7.Final/hibernate.version springsecurity.version5.1.9.RELEASE/springsecurity.version project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties dependencies dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-orm/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-tx/artifactId version${spring.version}/version /dependency dependency groupIdorg.hibernate/groupId artifactIdhibernate-core/artifactId version${hibernate.version}/version /dependency dependency groupIdorg.springframework.security/groupId artifactIdspring-security-config/artifactId version${springsecurity.version}/version /dependency dependency groupIdorg.springframework.security/groupId artifactIdspring-security-web/artifactId version${springsecurity.version}/version /dependency dependency groupIdcom.mchange/groupId artifactIdc3p0/artifactId version0.9.5.4/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.47/version /dependency dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.9.9/version /dependency dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency /dependencies几个容易忽略的点spring-orm 是 Spring 整合 Hibernate 的桥梁LocalSessionFactoryBean、HibernateTransactionManager 都在这个包里。只引 hibernate-core 不引 spring-ormSessionFactory 很难被 Spring 容器管理事务也切不进去。spring-security-web 提供 DelegatingFilterProxy它是 SpringSecurity 过滤器链进入 Web 容器的大门。spring-security-config 才负责解析security:http这类命名空间配置。两个都要缺一个要么过滤器装不上要么 XML 解析报错。servlet-api 必须用 provided。Tomcat 自己带了一套 Servlet 实现如果打进了 war 包部署时类加载器会冲突表现是启动异常或者请求直接 500。c3p0 作为连接池比较老但配置透明、不依赖自动配置符合这套骨架的定位。如果数据库是 MySQL 8驱动坐标要换com.mysql:mysql-connector-j版本用 8.0.x方言也要同步调整。后面避坑章节会重点说这个。2.3 目录结构分层包名和配置文件职责边界工程目录按“框架配置与业务分离”的思路拆配置集中放在 resources业务代码按 Controller、Service、DAO、Entity 分层。src/main/java/com/xxx/framework ├── config 数据源、事务管理器、相关配置Bean ├── entity Hibernate实体类 ├── dao DAO接口 ├── dao/impl DAO实现 ├── service service业务层接口 ├── service/impl service实现 ├── controller SpringMVC控制器 └── security UserDetailsService、登录成功/失败处理器 src/main/resources ├── jdbc.properties ├── applicationContext.xml ├── spring-mvc.xml └── spring-security.xml src/main/webapp/WEB-INF ├── web.xml └── views ├── login.jsp ├── index.jsp └── 403.jspapplicationContext.xml 管根容器负责 dataSource、sessionFactory、transactionManager、dao、service、security 这些核心 Bean。spring-mvc.xml 是 DispatcherServlet 的子容器只扫 Controller 和视图解析器。这个拆分不是形式主义根容器先启动SpringMVC 子容器后启动子容器里的 Controller 能注入父容器的 Service Bean反过来父容器看不到 Controller。如果图省事把所有 Bean 都塞进 spring-mvc.xmlSpringSecurity 的过滤器链会找不到 UserDetailsService启动时最容易踩坑。2.4 构建与启动验证# 打成 war 包跳过测试 mvn clean package -DskipTests # 本地快速启动容器 mvn tomcat7:run第一次启动时看日志有两个关键节点。第一个是“Creating shared instance of singleton bean sessionFactory”说明根容器把 Hibernate 的 SessionFactory 建好了第二个是 SpringMVC 子容器初始化完成两个节点都出现后再访问http://localhost:8080/工程名/能被 SpringSecurity 重定向到登录页就说明三套框架的粘合已经通了。3. Hibernate 5 持久层落地数据源、SessionFactory 与 DAO 边界持久层是这套骨架里最容易“能跑但埋雷”的部分。很多项目把连接池参数、事务边界、分页方式写得随心所欲等流量一上来直接打穿数据库。这一章把配置和代码逐项过一遍。3.1 数据源与 SessionFactory 配置jdbc.properties 里最关键的三个值driverClass、连接串、字符集参数。连接串少一个 useUnicode中文乱码问题会一路传到业务层。jdbc.driverClasscom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/framework?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password你的密码applicationContext.xml 里装配数据源和 SessionFactorybean iddataSource classcom.mchange.v2.c3p0.ComboPooledDataSource property namedriverClass value${jdbc.driverClass}/ property namejdbcUrl value${jdbc.url}/ property nameuser value${jdbc.username}/ property namepassword value${jdbc.password}/ property nameinitialPoolSize value3/ property nameminPoolSize value3/ property namemaxPoolSize value20/ property namecheckoutTimeout value30000/ /bean bean idsessionFactory classorg.springframework.orm.hibernate5.LocalSessionFactoryBean property namedataSource refdataSource/ property namepackagesToScan valuecom.xxx.framework.entity/ property namehibernateProperties props prop keyhibernate.dialectorg.hibernate.dialect.MySQL57Dialect/prop prop keyhibernate.show_sqlfalse/prop prop keyhibernate.format_sqltrue/prop prop keyhibernate.hbm2ddl.autovalidate/prop /props /property /beanLocalSessionFactoryBean 是 Spring 整合 Hibernate 5 的标准入口packagesToScan 会自动注册实体类不需要一行行写mapping resource。方言是 MySQL 5.7 的 Dialect如果连的是 MySQL 8驱动版本升级后这里要换成 MySQL8Dialect否则部分分页 SQL 会生成得不准确。连接池参数里maxPoolSize 我会控制在 20 左右同时核对数据库的 max_connections。很多系统上线后才暴露问题不是 SQL 慢而是连接池把数据库连接数吃满。checkoutTimeout 设 30 秒连接池没资源时让请求快速失败而不是无限阻塞。hbm2ddl.auto 核心建议只有一个商用环境不要用 update。表结构变更必须走迁移脚本validate 模式启动时会校验实体和表结构是否一致不一致直接启动失败这种“硬失败”反而比线上字段缺失更安全。开发环境想省事可以临时用 update上线前必须切回 validate。3.2 实体类与字段映射实体类用 JPA 标准注解用 javax.persistence 包下的注解不依赖 Hibernate 私有注解未来切 JPA 实现时不用重写实体。Entity Table(name sys_user) public class SysUser implements Serializable { Id GeneratedValue(strategy GenerationType.IDENTITY) Column(name id) private Long id; Column(name username, length 64, nullable false) private String username; Column(name password, length 128, nullable false) private String password; Column(name real_name, length 32) private String realName; Column(name locked, nullable false) private Boolean locked; // getter / setter 省略 }主键用 GenerationType.IDENTITY 对应 MySQL 自增主键这个策略直白不会像 AUTO 一样绕一圈去查 hibernate_sequence。字段一律用包装类型数据库里的 NULL 映射成 null 而不是 0 或 false不然业务判断会把“未设置”和“等于 0”混淆。Boolean locked 这种字段也写明 nullablefalse配合 validate 模式能在启动阶段就发现表结构不一致。3.3 DAO 层封装与 Session 获取方式Hibernate 5 里 HibernateTemplate 已经不属于推荐路线直接用原生 SessionFactory 获取 Session 更符合当前 API。下面是一个通用 DAO 实现的写法public class BaseDaoImplT implements BaseDaoT { Autowired private SessionFactory sessionFactory; protected Session getSession() { return sessionFactory.getCurrentSession(); } public void save(T entity) { getSession().save(entity); } public T findById(ClassT clazz, Long id) { return getSession().get(clazz, id); } public void delete(T entity) { getSession().delete(entity); } }getCurrentSession 是关键。它要求 Session 绑定到当前事务事务开启期间拿到同一个 Session事务提交后自动关闭手工代码不需要回收连接。如果用 openSession 手动获取Session 必须自己 close漏掉就是连接泄漏数据库连接会一点点被吃光。业务方法上建议把事务放在 Service 层Service Transactional(readOnly true) public class UserServiceImpl implements UserService { Override Transactional public void changePassword(Long userId, String newPassword) { // 修改密码的业务逻辑 } }之前花了不少时间调一个诡异问题两个 DAO 各自都加了事务注解跨表操作时一个方法先提交、后一个异常数据一致性没法保证。从那以后事务注解统一只放在 Service 层方法上DAO 层一个事务注解都不留。查询方法默认 readOnlytrue 能减少不必要的脏检查增删改方法单独覆盖为 Transactional。3.4 分页与条件查询Hibernate 5 的 API 选择分页查询适合用 JPA 的 CriteriaQuery类型安全、动态条件好拼。老代码里的 org.hibernate.Criteria 已经过时不建议再用。public PageResultSysUser findUserPage(int pageNo, int pageSize, String keyword) { Session session sessionFactory.getCurrentSession(); CriteriaBuilder cb session.getCriteriaBuilder(); CriteriaQuerySysUser cq cb.createQuery(SysUser.class); RootSysUser root cq.from(SysUser.class); if (keyword ! null !keyword.trim().isEmpty()) { cq.where(cb.like(root.get(username), % keyword %)); } cq.orderBy(cb.desc(root.get(id))); QuerySysUser query session.createQuery(cq); query.setFirstResult((pageNo - 1) * pageSize); query.setMaxResults(pageSize); ListSysUser list query.list(); // 再单独执行 count 查询避免把全表数据拉进内存 return new PageResult(list, countUser(keyword), pageNo, pageSize); }pageNo 从 1 开始DAO 层换算成 firstResult。count 查询要单独跑一条select count(*) from sys_user where ...千万别用 list.size() 替代五万行数据就能把 JVM 堆内存撑爆。动态条件里的 like 注意%keyword%的位置keyword 本身可能包含 %如果用户输入特殊字符这里要考虑转义避免 HQL 注入和意外匹配。这一章直接按 CriteriaBuilder 的写法封装后续接复杂搜索条件时改动量最小。4. Spring 5 与 SpringSecurity 5 装配从 web.xml 到登录认证链路SpringMVC、SpringSecurity、Hibernate 三套框架的粘合点全在 web.xml 和三个 XML 配置里。顺序对了请求在容器里流转就是一条干净链路顺序错了现象千奇百怪。4.1 web.xml监听器、过滤器、Servlet 的装配顺序web.xml 里三个关键组件ContextLoaderListener、CharacterEncodingFilter、DispatcherServlet。SpringSecurity 的过滤器通过 DelegatingFilterProxy 挂进来。web-app context-param param-namecontextConfigLocation/param-name param-valueclasspath:applicationContext.xml/param-value /context-param listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping filter filter-namespringSecurityFilterChain/filter-name filter-classorg.springframework.web.filter.DelegatingFilterProxy/filter-class /filter filter-mapping filter-namespringSecurityFilterChain/filter-name url-pattern/*/url-pattern /filter-mapping servlet servlet-namedispatcher/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath:spring-mvc.xml/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namedispatcher/servlet-name url-pattern//url-pattern /servlet-mapping /web-appContextLoaderListener 先加载根容器DispatcherServlet 再加载 spring-mvc.xml。springSecurityFilterChain 这个名字是 SpringSecurity 的默认过滤链 Bean 名DelegatingFilterProxy 会按 filter-name 去根容器里找所以 SpringSecurity 的配置必须放进 applicationContext.xml 或能被根容器加载到的文件里。CharacterEncodingFilter 一定要放在 SpringSecurity 过滤器之前。原因很实际登录认证要读取用户名和密码参数如果编码过滤器还没执行POST 参数里的中文密码就已经乱码认证链路拿到乱码密码去比对必然失败。forceEncoding 设为 true强制请求和响应都使用 UTF-8避免只设置请求或只设置响应造成的半截乱码。DispatcherServlet 的 url-pattern 是/而不是/*。/*会连 JSP 请求一起拦截InternalResourceViewResolver 解析出的视图会被 DispatcherServlet 再拦一次页面返回源码或 404。4.2 spring-mvc.xmlController 与视图解析spring-mvc.xml 的核心是 annotation-driven、组件扫描和视图解析器。mvc:annotation-driven/ context:component-scan base-packagecom.xxx.framework.controller/ bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/views// property namesuffix value.jsp/ /bean mvc:default-servlet-handler/annotation-driven 注册了 HandlerMapping 和 HttpMessageConverterResponseBody 返回对象时才走 Jackson 转 JSON。组件扫描只扫 controller 包绝对不要顺带扫 Service 或 DAO否则同一个 Service Bean 会被创建两次事务代理可能出现不一致。视图解析器把 Controller 返回的字符串映射到/WEB-INF/views/xxx.jspJSP 放在 WEB-INF 下无法直接通过 URL 访问只能经 Controller 跳转页面也就绕开了 SpringSecurity 的权限拦截逻辑。default-servlet-handler 是容易漏的一个配置。DispatcherServlet 映射/后所有静态资源都会被它收走放行 CSS、JS、图片需要靠这个 handler 回退到容器默认 Servlet。没配它登录页打开后样式全是 404。Controller 注入 Service 是常见疑惑点。spring-mvc.xml 扫的是 controller 包Service 在 applicationContext.xml 的根容器里但 SpringMVC 容器创建 Controller 时可以向父容器要依赖所以 autowire 能成功。反过来不行根容器不认识 Controller。4.3 SpringSecurity 5认证与授权的最小配置spring-security.xml 是整个骨架里最需要细看的文件。beans:beans xmlnshttp://www.springframework.org/schema/security xmlns:beanshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocation http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http auto-configfalse use-expressionstrue intercept-url pattern/login accesspermitAll/ intercept-url pattern/css/** accesspermitAll/ intercept-url pattern/user/** accesshasRole(USER)/ intercept-url pattern/admin/** accesshasRole(ADMIN)/ form-login login-page/login login-processing-url/loginProcess username-parameterusername password-parameterpassword default-target-url/index authentication-failure-url/login?error1/ logout logout-url/logout logout-success-url/login/ csrf disabledtrue/ /http authentication-manager authentication-provider user-service-refuserDetailsService password-encoder refpasswordEncoder/ /authentication-provider /authentication-manager beans:bean idpasswordEncoder classorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder/ /beans:beansauto-configfalse 关闭自动配置所有过滤规则显式声明。use-expressionstrue 开启 access 表达式才能写 hasRole。给一个容易踩的点hasRole(‘USER’) 匹配的权限名是 ROLE_USER不是 USER。自定义 UserDetailsService 构建 GrantAuthority 时要么每个角色名都加上 ROLE_ 前缀要么把拦截规则换成 hasAuthority(‘USER’)两个写法在 SpringSecurity 5 里行为不同。form-login 里的 login-processing-url 指定表单 POST 提交到哪个 URL。用户名和密码参数默认就叫 username/password前端表单字段名必须对齐否则认证管理器拿到空参数登录永远失败。csrf 这里先 disabledtrue是为了让纯表单 Session 的旧结构先跑通。商业系统如果是前后端分离建议开启 CSRF 并在前端请求头带上 token纯页面 JSP 提交的话开启后需要在每个表单里嵌入 CSRF 隐藏字段工作量不小。SpringSecurity 5 的 passwordEncoder 是刚性的。BCryptPasswordEncoder 要求数据库里的密码是 BCrypt 密文明文密码或者 MD5 密文都会直接抛异常。老系统迁移时可以用 DelegatingPasswordEncoder用{noop}或{md5}前缀临时兼容但必须规划密码迁移不能长期保留弱哈希。4.4 登录跳转与 SecurityContext 读取UserDetailsService 是认证的数据源接口实现里从 sys_user 表加载用户并组装角色集合。Service(userDetailsService) public class UserDetailsServiceImpl implements UserDetailsService { Autowired private UserDao userDao; Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { SysUser user userDao.findByUsername(username); if (user null) { throw new UsernameNotFoundException(用户不存在); } ListGrantedAuthority authorities new ArrayList(); authorities.add(new SimpleGrantedAuthority(ROLE_USER)); if (user.getAdmin()) { authorities.add(new SimpleGrantedAuthority(ROLE_ADMIN)); } return new org.springframework.security.core.userdetails.User( user.getUsername(), user.getPassword(), authorities); } }登录成功后Controller 里通过 SecurityContextHolder 获取当前认证信息。业务代码里不要到处持有这个上下文尤其在 DAO 层安全上下文属于 Web 层概念。Service 方法需要的当前用户 ID由 Controller 解析后作为参数传进去业务层保持干净。没登录访问受保护 URL 时SpringSecurity 会重定向到 login-page。如果是前后端分离的接口调用期望的是 401 而不是 302否则前端 fetch 拿回来一段登录页 HTML又得排查半天。这种情况可以自定义 AuthenticationEntryPoint 返回 JSON。骨架里先按表单页面跳转处理接口开发阶段再单独适配。5. 避坑与排查这套组合最常见的六个翻车点组合越老坑越经典。以下六个问题都是反复遇到过的每条按现象、原因、解决三步展开。5.1 配置与启动期SessionFactory 起不来、密码校验报错现象 1容器启动直接抛 HibernateException提示 Access to DialectResolutionInfo cannot be null或 ClassNotFoundException: com.mysql.jdbc.Driver。原因jdbc.properties 里的 driverClass 写错或者 hibernate.dialect 和实际数据库版本对不上。MySQL 5.7 环境下配了 MySQL8Dialect或者 MySQL 8 环境还在用 5.1.x 的驱动都会在 SessionFactory 创建阶段暴露。解决驱动版本和数据库版本必须对齐。MySQL 5.7 用 com.mysql.jdbc.Driver MySQL57Dialect 驱动 5.1.47MySQL 8 用 com.mysql.cj.jdbc.Driver MySQL8Dialect 驱动 8.0.x。连接串上记得加 serverTimezoneAsia/Shanghai否则 MySQL 8 驱动会报时区错误。现象 2登录流程走到一半抛 IllegalArgumentException: There is no PasswordEncoder mapped for the id null。原因SpringSecurity 5 启动时就默认要求所有密码带有编码器标识。数据库里存的是明文或 MD5 密文没有{bcrypt}这类前缀BCryptPasswordEncoder 无法识别直接当成非法输入。解决密码库里的存量数据统一迁移为 BCrypt 密文。老系统过渡期可以用 DelegatingPasswordEncoder把明文密码前缀改为{noop}、MD5 改{md5}让认证先恢复。但这两个方案只适合临时兼容迁移计划必须跟上上线前把弱哈希全部洗掉。5.2 运行期乱码、懒加载、API 废弃现象 3中文入库存成问号按中文关键字查询查不到数据。原因三个环节只要一个断了都会出问题。连接串没加 useUnicodetruecharacterEncodingutf-8驱动就用默认编码CharacterEncodingFilter 没配 forceEncoding请求体编码不生效Tomcat 连接器 URIEncoding 没设 UTF-8GET 参数先乱一步。解决jdbcUrl 固定带useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghaiweb.xml 里 encodingFilter 保持在过滤器链第一位Tomcat server.xml 给 Connector 加URIEncodingUTF-8。这三项检查完登录页和业务查询的中文乱码基本清零。现象 4JSP 里访问 user.getDepartment().getName()抛 LazyInitializationException: could not initialize proxy - no Session。原因Service 方法事务提交后 Session 就关了视图层才去触发 Hibernate 的延迟加载代理发现没有可用 Session。解决最稳妥的办法是 Service 层把需要的关联数据一次加载完成HQL 里写 join fetch或者遍历前调用 Hibernate.initialize。有些人推荐 OpenSessionInViewFilter它能把 Session 延迟到视图渲染结束但 Session 占用数据库连接的时间被拉长高并发下连接池很容易被打穿。我一般不用它宁可在业务方法里把查询写透。现象 5老代码里session.createCriteria(User.class).add(Restrictions.eq(...))升级 Hibernate 5 后警告不断甚至运行期 AbstractMethodError。原因org.hibernate.Criteria 在 Hibernate 5.2 起进入废弃流程5.3 里仍保留但已经不推荐后续版本逐步裁剪方法。老工程依赖这套 API升级后编译可能不报错运行期才炸。解决等值查询直接改成 HQLfrom SysUser where username :username动态条件多的查询改用 CriteriaBuilder。迁移优先级先保证功能恢复再把全表扫描的旧查询逐步替换。新写的 DAO 方法一律不要再碰 org.hibernate.Criteria。现象 6war 包部署到独立 Tomcat 后启动报 NoClassDefFoundError: org/slf4j/LoggerFactory或日志满天飞。原因Spring 和 Hibernate 各自传递引入 slf4j 绑定工程里同时出现多个 slf4j-log4j12、logback-classic 实现类加载器冲突。IDE 里跑 tomcat7 插件没事独立 Tomcat 的类加载策略更严格问题就暴露了。解决pom 里统一声明 slf4j-api并在 spring-web、hibernate-core 的依赖上排除多余的日志绑定实现。团队里日志门面只保留一个业务 Logger 全用 slf4j 接口后端替换实现时不用改代码。6. 从工程骨架到商业项目扩展边界与上线前验证清单骨架跑通只是起点真正进入商业开发后需要一套固定的扩展套路。我一般遵循六步建表 → 写 Entity → DAO 继承 BaseDaoImpl → Service 加事务注解 → Controller 写接口 → spring-security.xml 里补 intercept-url 权限。这套路径下新人加一个模块大约半天能完成不会碰坏框架配置。进阶权限控制建议用方法级安全。spring-security.xml 里需要额外开启注解支持global-method-security pre-post-annotationsenabled/然后在 Service 方法上细化权限Service public class OrderServiceImpl implements OrderService { PreAuthorize(hasRole(ADMIN)) Override public void cancelOrder(Long orderId) { // 只有 ADMIN 角色可以执行 } }权限判断放在 Service 层适合同一接口下不同角色执行不同业务分支的场景。Controller 只负责参数接收和视图跳转不承担权限逻辑。上线前我会拿下面这张清单做强制核验每项都记录结果。验证项预期结果验证方式未登录访问受保护接口重定向到 /loginURL 带 error 或 redirect 参数curl -I 或浏览器开发者工具普通用户访问 /admin/**返回 403 页面而不是 500普通账号登录后访问中文增删改查数据正确入库页面显示无乱码插入一条含中文的测试数据事务回滚Service 内手动抛异常后数据无残留临时加一段 RuntimeException连接池上限并发压测下连接数不超 maxPoolSizejstack 或 druid 监控有一次某个客户系统上线前被我检查出连接池上限和数据库 max_connections 的比例不合适并发一上来连接数会先被打满。这种问题在功能测试阶段完全看不出来压测阶段才暴露。从那以后我每次起新工程都强制把编码过滤器顺序、密码方案、事务边界、连接池上限这四个点核验一遍再往骨架上加业务代码。这套 SpringMVC 5 Spring 5 SpringSecurity 5 Hibernate 5 的组合本身不复杂复杂的是每一步都被正确对待。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

C#连接PostgreSQL:Npgsql驱动原理与生产实践

C#连接PostgreSQL:Npgsql驱动原理与生产实践

1. 项目概述:为什么选Npgsql而不是ADO.NET原生驱动?“C#连接数据库PostSQL-NpgSQL”这个标题看似简单,但背后藏着一个在.NET生态里被反复验证过、却常被新手忽略的关键决策点:PostgreSQL不是SQL Server,硬套微软全家桶…

2026/10/15 13:30:01 阅读更多 →
数学物理方程详解:PDE分类与三大经典方程推导

数学物理方程详解:PDE分类与三大经典方程推导

简介:《中科大数理方程》PPT课件面向高校数学、物理及相关工科专业学生,适合课程学习、考研复习和教师备课。课件系统讲解偏微分方程(PDE)核心知识:先阐明基本概念与阶数定义,再区分线性与非线性、常系数与…

2026/10/15 13:30:01 阅读更多 →
XSS攻击深度解析与防御实战指南

XSS攻击深度解析与防御实战指南

1. XSS攻击的本质与危害解析跨站脚本攻击(XSS)作为OWASP Top 10常驻成员,本质上是一种将恶意脚本注入到可信网站中的攻击手段。攻击者利用网站对用户输入过滤不严的漏洞,在受害者浏览器中执行非预期的JavaScript代码。不同于其他W…

2026/10/15 13:29:01 阅读更多 →

最新新闻

Windows 10 1803安全基线:本地化终端合规落地实践

Windows 10 1803安全基线:本地化终端合规落地实践

简介:本资源是面向企业IT管理员、安全合规工程师及Windows系统运维人员的Windows 10 Version 1803(RS4)安全基线实施套件,用于快速落地等保2.0、ISO 27001等合规要求中的终端安全配置。压缩包含72个文件,总计1.06MB&am…

2026/10/15 14:56:39 阅读更多 →
C语言迷宫求解课程设计:DFS/BFS/A*算法实现与终端可视化

C语言迷宫求解课程设计:DFS/BFS/A*算法实现与终端可视化

简介:本资源是一份面向计算机专业本科生的C语言与数据结构综合课程设计实践项目,聚焦迷宫路径求解与交互式游戏开发,适用于算法实现、链表/栈/队列应用及图形化控制等核心知识点训练。压缩包共5个文件(3个txt迷宫配置文件、1个cpp…

2026/10/15 14:56:39 阅读更多 →
Ceph OSD启动失败的四层校验机制深度解析

Ceph OSD启动失败的四层校验机制深度解析

简介:本资源为Ceph分布式存储系统的中文技术文档合集,面向Linux系统运维工程师、云平台架构师及存储方向开发者,解决Ceph集群部署、配置调优与日常管理中的核心知识缺口。文档覆盖对象存储(RGW)、块设备(RB…

2026/10/15 14:56:39 阅读更多 →
视频加密与转码协同方案:AES-CBC+CENC分片加密实践

视频加密与转码协同方案:AES-CBC+CENC分片加密实践

简介:这是一套基于C#开发的视频文件加密与转码一体化解决方案,面向多媒体安全开发人员及音视频应用开发者,解决商业视频内容防盗播、本地化安全分发等核心需求。程序采用AES算法对视频文件进行全量加密,结合内置微型Web服务器实现…

2026/10/15 14:56:39 阅读更多 →
三页静态网页设计源码实战:HTML+CSS实现文字、图片与超链接跳转

三页静态网页设计源码实战:HTML+CSS实现文字、图片与超链接跳转

简介:这份网页设计资源面向刚接触前端、需要完成课程作业或入门练习的学习者,围绕“三个页面”这一典型场景,讲解如何用HTML搭建文字、图片与超链接内容,并借助CSS完成基本美化,解决页面结构组织与页面间跳转的入门问题…

2026/10/15 14:56:39 阅读更多 →
CD44重组蛋白是肿瘤干细胞标志物研究与靶向药物开发的重要工具

CD44重组蛋白是肿瘤干细胞标志物研究与靶向药物开发的重要工具

简述: CD44又称HCAM、PGP‑1、ECMR‑III,是位于人类11号染色体的跨膜糖蛋白,也是透明质酸、骨桥蛋白等多种配体的受体。CD44参与细胞黏附、迁移、淋巴细胞归巢、上皮‑间质转化、肿瘤干细胞维持及转移调控,其标准型与变异体在不同…

2026/10/15 14:55:37 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/15 9:49:14 阅读更多 →