代码之家  ›  专栏  ›  技术社区  ›  blindmind

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

  •  1
  • blindmind  · 技术社区  · 9 年前

    我开始了一个简单的基于JPA的Play 2.4项目,但我在理解分层架构中正确使用Play的EntityManager时遇到了一些问题,特别是在将实体写入数据库的情况下。我是一个初学者,通常我使用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;
    }
    

    我的服务层,执行db操作:

    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().mmerge(..)不写入我的数据库,但调用了我的@PrePersist注释方法,但没有刷新。读取器方法与getEntityManager()配合使用很好。我已经尝试用JPA.withTransaction(…)包装merge(..),但没有任何区别。

    但是,当我将其更改为JPA.em().mmerge(..)时,它会正常工作,实体被写入数据库。 但是,在我的服务中,我有一个丑陋的依赖关系,这是我真正想要避免的,这就是为什么我在模块中为依赖注入提供实体管理器。

    也许在我的设计中有一个我看不到的问题,但这是由我在春天的经历推动的。

    每一个想法都很受欢迎,我真的很执着于此,在其他地方找不到任何想法,Play文档在这种情况下也没有帮助。

    谢谢!

    1 回复  |  直到 9 年前
        1
  •  1
  •   Pawel Hawro    9 年前

    使命感 JPA.em("default") 创建新的EntityManager,但未打开。

    仅呼叫 JPA.em() 将获取使用创建的当前EntityManager @Transactional 其已打开交易并将在呼叫结束时刷新。

    您可以设置的名称 EntityManager 在里面 @事务性的 .

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