Play 2.4 JPA/Hibernate EntityManager 不刷新到数据库

Posted

技术标签:

【中文标题】Play 2.4 JPA/Hibernate EntityManager 不刷新到数据库【英文标题】:Play 2.4 JPA/Hibernate EntityManager does no flush to database 【发布时间】:2015-08-18 15:20:20 【问题描述】:

我开始了一个简单的基于 JPA 的 Play 2.4 项目,但在理解 Play 的 EntityManager 在分层架构中的正确使用方面存在一些问题,特别是在将实体写入数据库的情况下。我是Play的初学者,通常我使用Spring进行应用程序开发,几乎没有直接使用实体管理器。

假设我有一个用于注册新用户的控制器。为了分离关注点,控制器只接受请求,将请求委托给底层服务并返回结果,在我的示例中是新创建的注册实体:

public class ApiController extends Controller 

    private final RegistrationService registrationService;

    @Inject
    public ApiController(final RegistrationService registrationService) 
        this.registrationService = registrationService;
    

    @BodyParser.Of(BodyParser.Json.class)
    @Transactional
    public Result startRegistration() 
        final JsonNode requestBody = request().body().asJson();
        final RegistrationRequest registrationRequest = Json.fromJson(requestBody, RegistrationRequest.class);
        final Registration result = registrationService.startRegistration(registrationRequest);
        return ok(Json.toJson(result));
    

我的 Guice 模块,它绑定我的依赖项并提供实体管理器,以便我可以将其注入到我的服务中:

public class RegistrationModule extends AbstractModule 

    protected void configure() 
        bind(TokenGenerator.class).to(TokenGeneratorSupport.class);
        bind(RegistrationService.class).to(RegistrationServiceSupport.class);
    

    @Provides
    public EntityManager provideEntityManager() 
        EntityManager em = JPA.em("default");
        LOG.info("Created EntityManager: ", em);
        return em;
    

    @Provides
    public Validator provideValidator() 
        final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
        final Validator validator = validatorFactory.getValidator();
        return validator;

我的服务层,执行数据库操作:

public abstract class DefaultJpaServiceSupport<T extends AbstractJpaEntity> implements BaseService<T> 

    private final EntityManager entityManager;
    private final Validator validator;
    private final Class<T> type;

    protected DefaultJpaServiceSupport(final EntityManager entityManager, final Validator validator,
                                   final Class<T> type) 
        this.entityManager = entityManager;
        this.validator = validator;
        this.type = type;
    

    @Override
    public Optional<T> getByUuid(final UUID uuid) 
        ...
        final TypedQuery<T> query = getEntityManager().createQuery(queryString, getType());
        ...
        return entity;
    

    protected final EntityManager getEntityManager() 
        return entityManager;
    


public class RegistrationServiceSupport extends DefaultJpaServiceSupport<Registration>
    implements RegistrationService 

    private static final Logger.ALogger LOG = Logger.of(RegistrationServiceSupport.class);

    private final TokenGenerator tokenGenerator;

    @Inject
    protected RegistrationServiceSupport(final EntityManager entityManager, final Validator validator,
                                     final TokenGenerator tokenGenerator) 
        super(entityManager, validator, Registration.class);
        this.tokenGenerator = tokenGenerator;
    

    @Override
    public Registration startRegistration(final RegistrationRequest request) 
    ...
        final Registration registration = getEntityManager().merge(new Registration.Builder(randomUUID(), request.getEmail(),
            request.getPassword(), tokenGenerator.generate()).build());

        return registration;
    
 

这就是问题所在,getEntityManager().merge(..) 不会写入我的数据库,但会调用我的 @PrePersist 注释方法,但没有刷新。读取器方法可以与 getEntityManager() 一起正常工作。我已经尝试用 JPA.withTransaction(...) 包装 merge(..),但没有任何区别。

但是当我将它更改为 JPA.em().merge(..) 时,它可以正常工作,实体被写入 db。 但是,我在我的服务中对 Play 有一个丑陋的依赖,这是我真正想避免的,这就是为什么我在 Module 中提供 entitymanager 以进行依赖注入。

也许我的设计存在我看不到的问题,但它是由我在春季的经历所驱动的。

每一个想法都非常感谢,我真的坚持这一点,在其他地方找不到任何想法,Play 文档在这种情况下也没有帮助。

非常感谢!

【问题讨论】:

【参考方案1】:

调用 JPA.em("default") 会创建一个新的 EntityManager 而不是打开一个。

只有调用 JPA.em() 才能获得使用 @Transactional 创建的当前 EntityManager,它已经打开了事务并在调用结束时刷新。

您可以在@Transactional 中设置EntityManager 的名称。

如果需要在 Module 中指定名称,唯一的解决方案是在 JPA 事务中包装方法体 RegistrationServiceSupport.startRegistration

【讨论】:

感谢您的回复。 我认识到需要调用 JPA.em(),而不是使用 getEntityManager(),它会从我的基础服务返回注入的 entitymanager。

以上是关于Play 2.4 JPA/Hibernate EntityManager 不刷新到数据库的主要内容,如果未能解决你的问题,请参考以下文章

Play + JPA + Hibernate + PostgreSQL:无法创建表

Spring Data JPA HIbernate 批量插入速度较慢

JPA,Hibernate框架使用的踩坑记录和使用的一些细节问题

如何在 play 2.4 中使用 CommonsMailerPlugin

Play Framework 2.4 发送邮件

JPA(Hibernate)