Java JPA版本使用Orika进行类映射的奇怪行为。子版本将递增,但不应';T
在我的数据库模型中,每个实体都有版本字段。 我有三个简单的实体(客户、债务、付款)和@OneToMany realtions。客户有很多债务。债务有很多偿还。 我还使用Orika进行类映射(dto->entity和entity->dto) 我有两个测试用例:Java JPA版本使用Orika进行类映射的奇怪行为。子版本将递增,但不应';T,java,hibernate,jpa,spring-data,orika,Java,Hibernate,Jpa,Spring Data,Orika,在我的数据库模型中,每个实体都有版本字段。 我有三个简单的实体(客户、债务、付款)和@OneToMany realtions。客户有很多债务。债务有很多偿还。 我还使用Orika进行类映射(dto->entity和entity->dto) 我有两个测试用例: 我已经完全满足并坚持客户需求。然后我换一个 几乎每次更改后,客户只需保存一个属性。在测试用例的末尾 客户实体的版本是递增的。子实体的版本 为0(因为它们未更改) 当我使用相同的测试用例,但使用ClientDto和Orika映射时, 在测试用
// necessary annotations
public class ClientEntity extends BaseEntity {
@Id
@SequenceGenerator(sequenceName = "CLIENT_SEQUENCE", name = "CLIENT_SEQUENCE", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CLIENT_SEQUENCE")
private Long id;
private String firstName;
private String lastName;
private String language;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "client_id")
private Set<DebtEntity> debts;
}
Orika转换器:
@Component
@Getter
public class Converter {
private MapperFacade mapper;
@PostConstruct
public void init() {
var mapperFactory = new DefaultMapperFactory.Builder().build();
registerMappings(mapperFactory);
mapper = mapperFactory.getMapperFacade();
}
private void registerMappings(DefaultMapperFactory mapperFactory) {
mapperFactory.classMap(BaseDto.class, BaseEntity.class).byDefault().register();
mapperFactory.classMap(Client.class, ClientEntity.class).byDefault().register();
mapperFactory.classMap(Debt.class, DebtEntity.class).byDefault().register();
mapperFactory.classMap(Payment.class, PaymentEntity.class).byDefault().register();
mapperFactory.classMap(BaseEntity.class, BaseDto.class).byDefault().register();
mapperFactory.classMap(ClientEntity.class, Client.class).byDefault().register();
mapperFactory.classMap(DebtEntity.class, Debt.class).byDefault().register();
mapperFactory.classMap(PaymentEntity.class, Payment.class).byDefault().register();
}
}
客户道:
@RequiredArgsConstructor
@Component
public class ClientDao implements ClientPersistence {
private final ClientRepository clientRepository;
private final Converter converter;
@Override
public long save(Client client) {
var clientEntity = converter.getMapper().map(client, ClientEntity.class);
return clientRepository.save(clientEntity).getId();
}
@Override
public Client getClientById(long id) {
var client = clientRepository.getOne(id);
return converter.getMapper().map(client, Client.class);
}
}
客户端测试:
// this test fails
@Test
public void shouldNotIncrementChildVersionAfterParentDtoSaved() {
// given
var client = Client.builder()
.firstName("John")
.lastName("Smith")
.language("pl")
.debts(Set.of(
Debt.builder()
.amount(BigDecimal.valueOf(25000))
.payments(Set.of(Payment.builder().amount(BigDecimal.valueOf(2500)).date(Date.from(Instant.now())).build()))
.build()))
.build();
// when
long id = clientDao.save(client);
var clientCopy1 = clientDao.getClientById(id);
clientCopy1.setLanguage("en");
id = clientDao.save(clientCopy1);
var clientCopy2 = clientDao.getClientById(id);
clientCopy2.setLanguage("en");
id = clientDao.save(clientCopy2);
var clientCopy3 = clientDao.getClientById(id);
// then
assertEquals(3, clientCopy3.getVersion());
assertEquals(0, clientCopy3.getDebts().iterator().next().getVersion());
}
// this test passes
@Test
public void shouldNotIncrementChildVersionAfterParentEntitySaved() {
// given
var clientEntity = ClientEntity.builder()
.firstName("John")
.lastName("Smith")
.language("pl")
.debts(Set.of(
DebtEntity.builder()
.amount(BigDecimal.valueOf(25000))
.payments(Set.of(PaymentEntity.builder().amount(BigDecimal.valueOf(2500)).date(Date.from(Instant.now())).build()))
.build()))
.build();
// when
var clientEntityCopy = clientRepository.save(clientEntity);
clientEntityCopy.setLanguage("en");
clientEntityCopy = clientRepository.save(clientEntityCopy);
clientEntityCopy.setLanguage("ru");
clientEntityCopy = clientRepository.save(clientEntityCopy);
// then
assertEquals(2, clientEntityCopy.getVersion());
assertEquals(0, clientEntityCopy.getDebts().iterator().next().getVersion());
}
编辑
我终于找到了解决这个问题的办法。我将带有@JoinColumn的单向@OneToMany关系更改为双向@OneToMany。
但是我仍然不知道为什么它不适用于单向@OneToMany。您正在为
BaseDto.version使用一个原语类型,它的默认值为0。因此,如果映射该对象,则为该版本初始化0。如果您直接创建实体,则会为BaseEntity.version初始化null,Hibernate会给它第一个值,我认为是1。您正在为BaseDto.version使用一个原语类型,它的默认值为0。因此,如果映射该对象,则为该版本初始化0。如果直接创建实体,则会为BaseEntity初始化null。version
和Hibernate将为其提供第一个值,我认为是1。我将其更改为整数,但不幸的是仍然无法工作。有趣的是,当我没有填写最后@OneToMany collection(payments)时,它工作正常。您可以切换到在实体上使用属性访问器,即将JPA注释放在getter上,而不是字段上。然后,您可以在setter中设置断点,并查看为什么要设置version字段以及设置哪个值。我想这应该可以让你继续。我试过了,但在这个例子中,JPA迭代所有的setter并设置相同的值。我认为这个问题可能与嵌套的@OneToMany关系密切相关。正如我所说,当我将付款留空时,问题不会发生。我甚至尝试在没有Orika的情况下编写自己的映射程序,但结果是一样的。我终于找到了解决这个问题的方法。我将与JoinColumn的单向一元关系更改为双向一元关系。但我仍然不知道为什么它不适用于单向的OneToMany。我将它改为整数,不幸的是仍然不起作用。有趣的是,当我没有填写最后@OneToMany collection(payments)时,它工作正常。您可以切换到在实体上使用属性访问器,即将JPA注释放在getter上,而不是字段上。然后,您可以在setter中设置断点,并查看为什么要设置version字段以及设置哪个值。我想这应该可以让你继续。我试过了,但在这个例子中,JPA迭代所有的setter并设置相同的值。我认为这个问题可能与嵌套的@OneToMany关系密切相关。正如我所说,当我将付款留空时,问题不会发生。我甚至尝试在没有Orika的情况下编写自己的映射程序,但结果是一样的。我终于找到了解决这个问题的方法。我将与JoinColumn的单向一元关系更改为双向一元关系。但我仍然不知道为什么它不适用于单向的OneToMany。
@RequiredArgsConstructor
@Component
public class ClientDao implements ClientPersistence {
private final ClientRepository clientRepository;
private final Converter converter;
@Override
public long save(Client client) {
var clientEntity = converter.getMapper().map(client, ClientEntity.class);
return clientRepository.save(clientEntity).getId();
}
@Override
public Client getClientById(long id) {
var client = clientRepository.getOne(id);
return converter.getMapper().map(client, Client.class);
}
}
// this test fails
@Test
public void shouldNotIncrementChildVersionAfterParentDtoSaved() {
// given
var client = Client.builder()
.firstName("John")
.lastName("Smith")
.language("pl")
.debts(Set.of(
Debt.builder()
.amount(BigDecimal.valueOf(25000))
.payments(Set.of(Payment.builder().amount(BigDecimal.valueOf(2500)).date(Date.from(Instant.now())).build()))
.build()))
.build();
// when
long id = clientDao.save(client);
var clientCopy1 = clientDao.getClientById(id);
clientCopy1.setLanguage("en");
id = clientDao.save(clientCopy1);
var clientCopy2 = clientDao.getClientById(id);
clientCopy2.setLanguage("en");
id = clientDao.save(clientCopy2);
var clientCopy3 = clientDao.getClientById(id);
// then
assertEquals(3, clientCopy3.getVersion());
assertEquals(0, clientCopy3.getDebts().iterator().next().getVersion());
}
// this test passes
@Test
public void shouldNotIncrementChildVersionAfterParentEntitySaved() {
// given
var clientEntity = ClientEntity.builder()
.firstName("John")
.lastName("Smith")
.language("pl")
.debts(Set.of(
DebtEntity.builder()
.amount(BigDecimal.valueOf(25000))
.payments(Set.of(PaymentEntity.builder().amount(BigDecimal.valueOf(2500)).date(Date.from(Instant.now())).build()))
.build()))
.build();
// when
var clientEntityCopy = clientRepository.save(clientEntity);
clientEntityCopy.setLanguage("en");
clientEntityCopy = clientRepository.save(clientEntityCopy);
clientEntityCopy.setLanguage("ru");
clientEntityCopy = clientRepository.save(clientEntityCopy);
// then
assertEquals(2, clientEntityCopy.getVersion());
assertEquals(0, clientEntityCopy.getDebts().iterator().next().getVersion());
}