Play 2.4 JPA/Hibernate EntityManager 不刷新到数据库
Play 2.4 JPA/Hibernate EntityManager does no flush to database
我开始了一个简单的基于 JPA 的 Play 2.4 项目,但我在理解 Play 的 EntityManager 在分层架构中的正确用法方面遇到了一些问题,特别是在将实体写入数据库的情况下。我是 Play 的初学者,通常我使用 Spring 进行应用程序开发,几乎没有直接使用 entitymanager。
假设我有一个用于注册新用户的控制器。为了分离关注点,控制器只接收请求,将请求委托给底层服务,然后 returns 结果,即我示例中新创建的注册实体:
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 注释方法,但没有刷新。 reader 方法与 getEntityManager() 配合使用效果很好。我已经尝试用 JPA.withTransaction(...) 包装 merge(..),但这没有区别。
但是当我将其更改为 JPA.em().merge(..) 时,它会正常工作,实体将写入 db。
但是,我在我的服务中对 Play 有一个丑陋的依赖,这是我真正想避免的,这就是为什么我在模块中提供 entitymanager 来进行依赖注入。
也许我的设计中存在我看不到的问题,但它是由我在 spring 中的经验驱动的。
非常感谢每个想法,我真的坚持这个,并且在其他地方找不到任何想法,Play 文档在这种情况下也没有帮助。
非常感谢!
调用 JPA.em("default")
创建新的 EntityManager 未被打开。
只有调用 JPA.em()
才会获取使用 @Transactional
创建的当前 EntityManager,它已打开事务并将在调用结束时刷新。
您可以在 @Transactional
中设置 EntityManager
的名称。
如果需要在模块中指定名称,唯一的解决办法是将方法体 RegistrationServiceSupport.startRegistration
包裹在 JPA 事务中。
我开始了一个简单的基于 JPA 的 Play 2.4 项目,但我在理解 Play 的 EntityManager 在分层架构中的正确用法方面遇到了一些问题,特别是在将实体写入数据库的情况下。我是 Play 的初学者,通常我使用 Spring 进行应用程序开发,几乎没有直接使用 entitymanager。
假设我有一个用于注册新用户的控制器。为了分离关注点,控制器只接收请求,将请求委托给底层服务,然后 returns 结果,即我示例中新创建的注册实体:
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 注释方法,但没有刷新。 reader 方法与 getEntityManager() 配合使用效果很好。我已经尝试用 JPA.withTransaction(...) 包装 merge(..),但这没有区别。
但是当我将其更改为 JPA.em().merge(..) 时,它会正常工作,实体将写入 db。 但是,我在我的服务中对 Play 有一个丑陋的依赖,这是我真正想避免的,这就是为什么我在模块中提供 entitymanager 来进行依赖注入。
也许我的设计中存在我看不到的问题,但它是由我在 spring 中的经验驱动的。
非常感谢每个想法,我真的坚持这个,并且在其他地方找不到任何想法,Play 文档在这种情况下也没有帮助。
非常感谢!
调用 JPA.em("default")
创建新的 EntityManager 未被打开。
只有调用 JPA.em()
才会获取使用 @Transactional
创建的当前 EntityManager,它已打开事务并将在调用结束时刷新。
您可以在 @Transactional
中设置 EntityManager
的名称。
如果需要在模块中指定名称,唯一的解决办法是将方法体 RegistrationServiceSupport.startRegistration
包裹在 JPA 事务中。