测试 Hibernate Envers
Testing Hibernate Envers
我想写关于修改的测试。在控制台中,我看到 Hibernate 的更新调用,但没有插入 AUD-Table.
测试方法:
@DataJpaTest
class JPAHistoryTest {
@Test
public void test() {
def entity = // create Entity
def entity2 = // create same Entity
entity2.setPrice(entity2.getPrice() + 10)
entity2.setLastUpdate(entity2.lastUpdate.plusSeconds(10))
service.save(entity)
service.save(entity2)
repository.flush() // Hibernate updates changes
assert repository.findRevisions(entity.id).content.empty == false // FAIL!
}
}
我的实体看起来像:
@Entity
@Audited
class Entity {
@Id @GeneratedValue Long id
@Column(nullable = false) BigDecimal price
}
非常感谢。
据我所知,我保留了 @DataJpaTest
并添加了 @Transactional(propagation = NOT_SUPPORTED)
以确保测试方法不会启动事务。因为如果他们会 运行 在交易中,那么当测试关闭交易时,将写入 envers 历史条目。
@RunWith(SpringRunner)
@DataJpaTest
@Transactional(propagation = NOT_SUPPORTED)
class JPAHistoryTest {
@After
void after() {
repository.deleteAll()
}
@Test
public void testTwoInsert() {
def entity1 = // ...
def entity2 = // ...
sut.save(entity1 )
sut.save(entity2 )
assert repository.findRevisions(entity1.id).content.size() == 1
assert repository.findRevisions(entity2.id).content.size() == 1
}
}
老实说,我不遵循这里的命名约定。通常,service 方法调用由生产代码中的某些事务上下文包装。
@Service
public class Service1 {
@Transactional
public void save(Object object) {
}
}
@Service
public class Service2 {
@Transactional
public void save(Object object) {
}
}
对于需要在单个事务上下文中绑定两个服务方法调用的情况,您可以通过另一个服务调用包装这两个单独的服务。
@Service
public class ServiceWrapper {
@Autowired
private Service1 service1;
@Autowired
private Service2 service2;
@Transactional
public void save(Object object1, Object object2) {
service1.save( object1 );
service2.save( object2 );
}
}
需要指出的是,此 ServiceWrapper
不一定必须存在于您的生产代码中。这个 bean 可能是您专门为测试创建的。重点是这个 bean 是以非常 Spring 的方式构造的,以满足您需要的事务边界。
现在在您的测试中,您可以使用 存储库 来查找实体,因为包装器内的事务提交了数据,允许按预期在 Envers 审计表中进行查找.
@Test
public void testSomething() {
Object entity1 = //
Object entity2 = //
serviceWrapper.save( entity1, entity2 );
// now use the repository to find the entity revision
}
我发现了和你一样的问题并尝试了@michael-hegner 的回答,但是我使用了 TestEntityManager
class 所以当没有交易时我无法得到 EntityManager
(propagation
设置为 NOT_SUPPORTED
).
在我的例子中,解决方案是从 TestEntityManager
手动提交事务,以首先保存实体和更改,然后查询修订。
这是一个测试class:
@DataJpaTest
class UserRepositoryTest {
@Autowired
private TestEntityManager testEntityManager;
@Test
public void shouldBeAudited() {
User user = getTestUser();
testEntityManager.persistAndFlush(user);
user.setPassword("tset");
testEntityManager.merge(user);
// This line is critical here to commit transaction and trigger audit logs
testEntityManager.getEntityManager().getTransaction().commit();
// With propagation = NOT_SUPPORTED this doesn't work: testEntityManager.getEntityManager()
AuditReader auditReader = AuditReaderFactory.get(testEntityManager.getEntityManager());
List revisions = auditReader.createQuery()
.forRevisionsOfEntity(User.class, true)
.add(AuditEntity.id().eq(user.getId()))
.getResultList();
assertEquals(1, revisions.size());
}
private User getTestUser() {
User user = new User();
user.setUsername("test");
user.setEmail("test");
user.setPassword("test");
return user;
}
}
测试后可能必须手动删除用户,因为事务已提交,而在其他测试中可能会导致一些问题。
如果您不想像 Michael Hegner 的回答那样改变整个测试的事务行为 class,您可以执行以下操作:
@RunWith(SpringRunner)
@DataJpaTest
class JPAHistoryTest {
@Autowired
private MyRepository repository;
@Autowired
private PlatformTransactionManager platformTransactionManager;
@After
void after() {
repository.deleteAll()
}
@Test
public void testTwoInsert() {
TransactionTemplate tx = new TransactionTemplate(platformTransactionManager);
tx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
def entity1 = // ...
def entity2 = // ...
tx.execute(status -> repository.save(entity1))
tx.execute(status -> repository.save(entity2))
assert repository.findRevisions(entity1.id).content.size() == 1
assert repository.findRevisions(entity2.id).content.size() == 1
}
}
因此,使用 PROPAGATION_NOT_SUPPORTED
创建一个 TransactionTemplate
并使用该事务模板进行保存。这也会触发保存审核信息。
我想写关于修改的测试。在控制台中,我看到 Hibernate 的更新调用,但没有插入 AUD-Table.
测试方法:
@DataJpaTest
class JPAHistoryTest {
@Test
public void test() {
def entity = // create Entity
def entity2 = // create same Entity
entity2.setPrice(entity2.getPrice() + 10)
entity2.setLastUpdate(entity2.lastUpdate.plusSeconds(10))
service.save(entity)
service.save(entity2)
repository.flush() // Hibernate updates changes
assert repository.findRevisions(entity.id).content.empty == false // FAIL!
}
}
我的实体看起来像:
@Entity
@Audited
class Entity {
@Id @GeneratedValue Long id
@Column(nullable = false) BigDecimal price
}
非常感谢。
据我所知,我保留了 @DataJpaTest
并添加了 @Transactional(propagation = NOT_SUPPORTED)
以确保测试方法不会启动事务。因为如果他们会 运行 在交易中,那么当测试关闭交易时,将写入 envers 历史条目。
@RunWith(SpringRunner)
@DataJpaTest
@Transactional(propagation = NOT_SUPPORTED)
class JPAHistoryTest {
@After
void after() {
repository.deleteAll()
}
@Test
public void testTwoInsert() {
def entity1 = // ...
def entity2 = // ...
sut.save(entity1 )
sut.save(entity2 )
assert repository.findRevisions(entity1.id).content.size() == 1
assert repository.findRevisions(entity2.id).content.size() == 1
}
}
老实说,我不遵循这里的命名约定。通常,service 方法调用由生产代码中的某些事务上下文包装。
@Service
public class Service1 {
@Transactional
public void save(Object object) {
}
}
@Service
public class Service2 {
@Transactional
public void save(Object object) {
}
}
对于需要在单个事务上下文中绑定两个服务方法调用的情况,您可以通过另一个服务调用包装这两个单独的服务。
@Service
public class ServiceWrapper {
@Autowired
private Service1 service1;
@Autowired
private Service2 service2;
@Transactional
public void save(Object object1, Object object2) {
service1.save( object1 );
service2.save( object2 );
}
}
需要指出的是,此 ServiceWrapper
不一定必须存在于您的生产代码中。这个 bean 可能是您专门为测试创建的。重点是这个 bean 是以非常 Spring 的方式构造的,以满足您需要的事务边界。
现在在您的测试中,您可以使用 存储库 来查找实体,因为包装器内的事务提交了数据,允许按预期在 Envers 审计表中进行查找.
@Test
public void testSomething() {
Object entity1 = //
Object entity2 = //
serviceWrapper.save( entity1, entity2 );
// now use the repository to find the entity revision
}
我发现了和你一样的问题并尝试了@michael-hegner 的回答,但是我使用了 TestEntityManager
class 所以当没有交易时我无法得到 EntityManager
(propagation
设置为 NOT_SUPPORTED
).
在我的例子中,解决方案是从 TestEntityManager
手动提交事务,以首先保存实体和更改,然后查询修订。
这是一个测试class:
@DataJpaTest
class UserRepositoryTest {
@Autowired
private TestEntityManager testEntityManager;
@Test
public void shouldBeAudited() {
User user = getTestUser();
testEntityManager.persistAndFlush(user);
user.setPassword("tset");
testEntityManager.merge(user);
// This line is critical here to commit transaction and trigger audit logs
testEntityManager.getEntityManager().getTransaction().commit();
// With propagation = NOT_SUPPORTED this doesn't work: testEntityManager.getEntityManager()
AuditReader auditReader = AuditReaderFactory.get(testEntityManager.getEntityManager());
List revisions = auditReader.createQuery()
.forRevisionsOfEntity(User.class, true)
.add(AuditEntity.id().eq(user.getId()))
.getResultList();
assertEquals(1, revisions.size());
}
private User getTestUser() {
User user = new User();
user.setUsername("test");
user.setEmail("test");
user.setPassword("test");
return user;
}
}
测试后可能必须手动删除用户,因为事务已提交,而在其他测试中可能会导致一些问题。
如果您不想像 Michael Hegner 的回答那样改变整个测试的事务行为 class,您可以执行以下操作:
@RunWith(SpringRunner)
@DataJpaTest
class JPAHistoryTest {
@Autowired
private MyRepository repository;
@Autowired
private PlatformTransactionManager platformTransactionManager;
@After
void after() {
repository.deleteAll()
}
@Test
public void testTwoInsert() {
TransactionTemplate tx = new TransactionTemplate(platformTransactionManager);
tx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
def entity1 = // ...
def entity2 = // ...
tx.execute(status -> repository.save(entity1))
tx.execute(status -> repository.save(entity2))
assert repository.findRevisions(entity1.id).content.size() == 1
assert repository.findRevisions(entity2.id).content.size() == 1
}
}
因此,使用 PROPAGATION_NOT_SUPPORTED
创建一个 TransactionTemplate
并使用该事务模板进行保存。这也会触发保存审核信息。