使用Hibernate JPA时,如何划分MVC(除视图外)的代码结构与事务?
分层逻辑与JPA组件职责划分建议
核心原则
Service层负责业务逻辑编排+事务边界控制,Repository层只负责纯数据CRUD操作,这是分层架构的核心准则,以此为基础来分配JPA组件的职责:
各JPA组件的放置方案
1. EntityManagerFactory(emf)
- emf是重量级对象,创建和销毁成本极高,绝对不能在Service/Repository的方法内重复创建。
- 正确做法:在应用启动时初始化一次,作为全局单例复用(比如放在静态代码块、Spring的配置类中),避免每次业务调用都重新创建。
2. EntityManager(em)
- em是轻量级对象,生命周期与事务绑定,必须由Service层管理。
- 原因:Repository的操作必须在Service的事务范围内执行,否则无法保证数据一致性。Service层创建em后,通过构造函数或方法参数传递给RepositoryImpl使用,Repository只负责用em执行CRUD,不管理其生命周期。
3. EntityTransaction(tx)
- 事务控制完全属于Service层职责,因为一个业务方法可能涉及多个Repository操作(比如先查再改),需要统一的事务边界(要么全成功,要么全回滚)。
- tx的开启、提交、回滚逻辑必须放在Service层,Repository层不碰任何事务相关代码。
代码调整示例
Service层代码(事务+em管理)
// 全局单例emf,应用启动时初始化一次 private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); public UserInfo handleLogin(String id, String pw) { EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); UserInfo result = null; try { tx.begin(); // 将当前em传入Repository LoginRepository repo = new LoginRepositoryImpl(em); List<UserInfo> users = repo.findUser(id, pw); // 这里处理业务逻辑:比如判断用户是否存在、生成登录凭证等 result = users.isEmpty() ? null : users.get(0); tx.commit(); } catch (Exception e) { if (tx.isActive()) { tx.rollback(); } throw new RuntimeException("登录失败", e); } finally { em.close(); } return result; }
RepositoryImpl代码(纯数据操作)
public class LoginRepositoryImpl implements LoginRepository{ private final EntityManager em; // 通过构造函数注入Service层传递的em public LoginRepositoryImpl(EntityManager em) { this.em = em; } @Override public List<UserInfo> findUser(String id, String pw) { // 使用em执行JPQL查询,只做纯数据操作 String jpql = "SELECT u FROM UserInfo u WHERE u.id = :id AND u.pw = :pw"; return em.createQuery(jpql, UserInfo.class) .setParameter("id", id) .setParameter("pw", pw) .getResultList(); } }
最佳实践(结合Spring)
如果使用Spring框架,完全可以摆脱手动管理的繁琐:
- Service层用
@Transactional注解自动控制事务,无需手动操作tx; - Repository层用
@Repository注解,通过@PersistenceContext自动注入与当前事务绑定的em; - emf由Spring容器自动管理,无需手动创建。
示例代码:
// Service层 @Service public class LoginService { private final LoginRepository loginRepository; // 构造注入Repository public LoginService(LoginRepository loginRepository) { this.loginRepository = loginRepository; } @Transactional public UserInfo login(String id, String pw) { List<UserInfo> users = loginRepository.findUser(id, pw); // 业务逻辑处理 return users.isEmpty() ? null : users.get(0); } } // RepositoryImpl层 @Repository public class LoginRepositoryImpl implements LoginRepository{ @PersistenceContext private EntityManager em; @Override public List<UserInfo> findUser(String id, String pw) { String jpql = "SELECT u FROM UserInfo u WHERE u.id = :id AND u.pw = :pw"; return em.createQuery(jpql, UserInfo.class) .setParameter("id", id) .setParameter("pw", pw) .getResultList(); } }
内容的提问来源于stack exchange,提问作者JayCold
相关产品推荐
相关产品推荐

