Spring服务层必须始终事务化吗?无事务时懒加载异常如何处理?
Spring事务相关问题解答
问题1:Spring中的服务层是否必须始终开启事务?
答案是完全不需要。事务是用来保证数据操作ACID特性(原子性、一致性、隔离性、持久性)的工具,要不要加事务完全看你的业务场景:
- 如果服务层方法只是做单一只读操作,且不需要访问实体的懒加载关联属性,那完全可以不用
@Transactional。比如单纯调用findById()获取完整实体并直接返回,这种场景事务并非必需。 - 但如果方法涉及写操作(增、删、改)、跨多个Repository的复合操作(比如同时更新两张表,需要保证操作原子性),或者要访问实体的懒加载关联属性,那事务就是必要的——它能维持数据库会话在方法执行期间处于打开状态,同时保证操作的原子性。
简单说:事务是解决特定问题的手段,不是服务层的强制标配。
问题2:移除@Transactional后抛出懒加载异常,为啥必须开事务?
核心原因得从Hibernate的懒加载机制说起,结合你的代码来拆解:
你用的serviceRepository.getOne(id),返回的不是真实的Model对象,而是Hibernate生成的代理对象。这个代理只有在你实际访问它的属性(比如model.getXXX())时,才会去数据库执行查询获取真实数据。
当你给类或方法加了@Transactional时,Spring会在方法执行前打开数据库会话(Session),直到方法执行完毕才关闭。这意味着在方法内部访问代理属性时,会话还处于可用状态,Hibernate能正常去数据库加载数据。
但移除@Transactional后,getServiceById方法执行完毕后,Spring不会维护这个会话,会话会立即被关闭。这时候再去访问代理对象的属性,Hibernate找不到可用的数据库会话,就会抛出org.hibernate.LazyInitializationException: could not initialize proxy异常。
如果不想用事务,你有几个替代方案:
- 改用
findById(id)代替getOne(id):findById会立即从数据库加载完整实体,而非返回代理,不会触发懒加载。 - 修改实体关联属性为饿汉加载(EAGER):比如在关联字段上添加
@ManyToOne(fetch = FetchType.EAGER),但这种方式不推荐,会强制加载所有关联数据,可能引发性能问题。 - 在Repository中自定义Fetch Join方法:用JPQL的
fetch join语句一次性加载关联数据,从根源避免懒加载触发。
内容的提问来源于stack exchange,提问作者abidinberkay
相关产品推荐
相关产品推荐

