播放2.4 JPA / Hibernate EntityManager不会刷新数据库

时间:2015-08-18 15:20:21

标签: playframework guice jpa-2.1 playframework-2.4

我开始了一个简单的基于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));
    }
}

My 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(...)包装合并(..),但没有区别。

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

也许我的设计中存在一个我无法看到的问题,但这是由我在春天的经历所驱动的。

每个想法都非常受欢迎,我真的很喜欢这个,并且在其他地方找不到任何想法,Play文档在这种情况下也没有用。

非常感谢!

1 个答案:

答案 0 :(得分:1)

调用JPA.em("default")会导致新的EntityManager无法打开。

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

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

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