我尝试在Play2(目前为2.6.15版)中迁移Play1应用程序。
使用Play1,在请求到达时自动管理交易。因此,我不必担心数据库更新的一致性。数据库更新已在整个服务层完成。
我必须将这种逻辑保留在Play2中,但我不确定该怎么做。我看到了Scala的一些线索,但答案无济于事。所以我会抓住机会的:)
要了解它是如何工作的,我编写了一个包含两个二进制模型的代码段:人员和地址。我想保存一个人及其地址列表和回滚,以防其中一个请求发生错误。
开始迁移时,我受到play-java-jpa-example的启发:我创建了一些存储库,其中包含基于JPAApi并在DatabaseExecutionContext上执行的对数据库的所有访问权限
public class PersonRepository implements IPersonRepository {
private final JPAApi jpaApi;
private final DatabaseExecutionContext executionContext;
@Inject
public PersonRepository(JPAApi jpaApi, DatabaseExecutionContext executionContext) {
this.jpaApi = jpaApi;
this.executionContext = executionContext;
}
...
@Override
public CompletionStage<Person> add(Person person) {
return supplyAsync(() -> jpaApi.withTransaction(em -> {
em.persist(person);
return person;
}), executionContext);
}
}
public class AddressRepository implements IAddressRepository {
...
@Override
public CompletionStage<Address> add(Address address) {
return supplyAsync(() -> jpaApi.withTransaction(em -> {
em.persist(address);
return address;
}), executionContext);
}
}
我有一项保存地址的服务
public class AddressService implements IAddressService {
....
@Override
public CompletionStage<Address> add(Address address) {
//do some stuff here
return addressRepository.add(address);
}
}
以及用于保存人的服务,
public class PersonService implements IPersonService {
...
@Override
public CompletionStage<Person> add(Person person, List<Address> address) {
return add(person).thenApplyAsync(p -> {
for (Address a : address) {
a.person=p;
addressServiceAsync.add(a);
}
return p;
}, ec.current());
}
}
使用add(Person person,List address)的这种实现,如果保存第二个地址失败,则该人和第一个地址将被保存在数据库中,这对我来说不够好。
我试图删除存储库中的事务管理并将其放在我的PersonService.add函数中。通过将实体管理器传递给我的服务和存储库的功能,它可以工作(我仅使用同步调用进行了测试)。像这样的东西:
public class PersonService implements IPersonService {
@Override
public CompletionStage<Person> add(Person person, List<Address> address) {
return supplyAsync(() -> jpaApi.withTransaction(em -> {
Person person1 = personRepository.insert(em, person);
for (Address a : address) {
a.person = person1;
addressService.add(em, a);
}
return person1;
})).exceptionally(throwable -> {
Logger.error("pan", throwable);
return null;
});
}
}
我不喜欢这种方法(将所有功能都赋予它们)并且想知道异步调用。
在Play中计划如何处理JPAApi和DatabaseExecutionContext这种回滚问题?
我没有看到任何明确的线索引起这一点,也许我错过了一些东西。解决此问题的最佳实践是什么?
感谢您的帮助。