Setting the revision date manually with Hibernate Envers - java

As far as I know, Hibernate Envers stores a revision when you create, change or delete an object annotated with #Audited.
Envers sets automatically the revision date to the current time. Is it possibile to set this time manually?
I'd need this to handle a temporal collection where data has valid time, which I'd need to set manually.

You can, but it may not seem intuitive at first.
When Envers creates its revision-entity instance, several things happen.
The #RevisionTimestamp annotated property is set with the current time.
The optional RevisionListener is called and supplied the revision-entity instance.
You can specify a RevisionListener in two ways and this really depends on whether or not your currently supplying a custom revision-entity instance or using the instance Envers resolves based on your setup.
Supplying Custom Revision Entity
In this situation, you can specifying your RevisionListener by setting it on the #RevisionEntity class annotation on the entity class.
#RevisionEntity(YourCustomRevisionListener.class)
public class CustomRevisionEntity {
...
}
Supplying RevisionListener via configuration
In this situation, you'll want add an additional bootstrap configuration property for Hibernate, either via your hibernate.properties file or in your code where you explicitly set the hibernate configuration properties:
org.hibernate.envers.revision_listener=com.company.envers.YourCustomRevisionListener
Regardless of which approach you take, you'll then implement the listener's contract and explicitly set the timestamp value based on whatever rules your application needs:
public class YourCustomRevisionListener implements RevisionListener {
#Override
public void newRevision(Object revisionEntity) {
// I am going to assume here you're using a custom revision entity.
// If you are not, you'll need to cast it to the appropriate class implementation.
final CustomRevisionEntity revisionEntityImpl = (CustomRevisionEntity) revisionEntity;
revisionEntityImpl.setTimestamp( resolveValidTimestampValue() );
}
private long resolveValidTimestampValue() {
// implement your logic here.
}
}
There are a couple caveats here. If you need to resolve the value from some bean in your application space, you'll need to determine which of the following applies to you:
Using Hibernate Envers version prior to 5.3
In this case you'll have to use the legacy approach of ThreadLocal variables to pass application scope instances/values to access those inside the listener.
Using Hibernate Envers 5.3 or later with CDI
In this case you can simply inject the CDI bean using CDI's injection since we added support to automatically resolve CDI beans when we create the listener instance.
Using Hibernate Envers 5.3 or later with Spring 5.1+
You can inject spring beans directly into the listener using Spring's injection annotations just like the listener were a spring-bean.
Using Hibernate Envers 5.3 or later with Spring prior to 5.1
In this case, you'll need to use the legacy approach of ThreadLocal variables since Spring Framework didn't add support for injecting beans into Hibernate beans until 5.1.

Related

Postgres Hibernate set session variables for row level security

I am having trouble finding information about this issue I am running into. I am interested in implementing row level security on my Postgres db and I am looking for a way to be able to set postgres session variables automatically through some form of an interceptor. Now, I know that with hibernate you are able to do row-level-security using #Filter and #FilterDef, however I would like to additionally set policies on my DB.
A very simple way of doing this would be to execute the SQL statement SET variable=value prior to every query, though I have not been able to find any information on this.
This is being used on a spring-boot application and every request is expected to will have access to a request-specific value of the variable.
Since your application uses spring, you could try accomplishing this in one of a few ways:
Spring AOP
In this approach, you write an advice that you ask spring to apply to specific methods. If your methods use the #Transactional annotation, you could have the advice be applied to those immediately after the transaction has started.
Extended TransactionManager Implementation
Lets assume your transaction is using JpaTransactionManager.
public class SecurityPolicyInjectingJpaTransactionManager extends JpaTransactionManager {
#Autowired
private EntityManager entityManager;
// constructors
#Override
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
super.prepareSynchronization(status, definition);
if (status.isNewTransaction()) {
// Use entityManager to execute your database policy param/values
// I would suggest you also register an after-completion callback synchronization
// This after-completion would clear all the policy param/values
// regardless of whether the transaction succeeded or failed
// since this happens just before it gets returned to the connection pool
}
}
}
Now simply configure your JPA environment to use your custom JpaTransactionManager class.
There are likely others, but these are the two that come to mind that I've explored.

#DataJpaTest equivalent for elasticsearch [duplicate]

Spring Boot 1.4 offers some fantastic testing improvements. One is the #DataJpaTest annotation where it wires up just the parts needed for JPA testing. What would the equivalent look like for just wiring up the parts needed for JdbcTemplate tests?
I'm fine constructing my own composite annotation that mimics the #DataJpaTest one.
Good question. Ironically enough, that one was raised during the testing talk yesterday at SpringOne Platform. Let's see what it takes to implement such dedicated test annotation.
TL;DR check the code on github
First of all you need to create the annotation. This annotation reuses some bits from the spring-boot-test-autoconfigure module. You may want to auto-configure an in-memory database (like DataJpaTest does). You also want to make sure that caching is configured and disabled by default (in case you have #EnableCaching on your Spring Boot application). You also want that all your tests are #Transactional by default so you should add that.
Next, you want that slicing effectively kicks in. All you need at this point is a DataSource, a JdbcTemplate, database migrations (flyway/liquibase) and a transaction manager to process #Transactional. To avoid the other auto-configurations to kick in you should add the following:
#OverrideAutoConfiguration(enabled = false)
Then, you want to explicitly enable the auto-configurations above. In order to do so, you add #ImportAutoConfiguration and you add the following content in META-INF/spring.factories
# AutoConfigureDataJpa auto-configuration imports
com.example.test.autoconfigure.jdbc.DataJdbcTest=\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
The key in spring.factories should match the FQN of your annotation. Whenever Spring Boot finds #ImportAutoConfiguration with no extra attributes, it will look for a key matching the annotation type in spring.factories.
Next up you want to be able to include additional components (component scan) with a filter. In order to do that, you can add #TypeExcludeFilters(DataJdbcTypeExcludeFilter.class) where DataJdbcTypeExcludeFilter is pretty much the same thing as DataJpaTypeExcludeFilter (so we might want to extract a common class for that).
Once you've done that, you only need to add your annotation and your JdbcTemplate is auto-configured for you
#RunWith(SpringRunner.class)
#DataJdbcTest
public class DataJdbcSampleTests {
#Autowired
private JdbcTemplate jdbcTemplate;
...
}
I think the option will be #JdbcTest, you could found further info on doc.

Execute initialization code in EJB3.1

I am currently migrating to EJB3.1 after using Spring for many years. One thing I would like to implement in EJB, for which I couldn't find a matching pattern yet is my MigrationManager.
In Spring I had a bean that dealt with database schema and data migration. For this I implemented a Spring BeanFactoryPostProcessor because this way I had the database connection injected, but the JPA system is not yet initialized. So I could perform all migration steps and then have the application finishing starting.
How can I do something like this in EJB3.1 (Using CDI ... if this is of importance)
Chris
This is the way to run some initialization code from an EJB:
#Singleton
#Startup
public class MigrationManager {
#PostConstruct
public void migrate() {
// do work
}
}
You don't need a separate app for that (as suggested in a comment above).
EntityManagers get instantiated lazily, so as long as you don't inject an EntityManager into some other startup code, this should give you a chance to update your database schema before an EntityManager is actually hitting the database.
By the way, for database schema migration I'd recommend Liquibase, which can be triggered by a ServletContextListener.

How to always enable my Hibernate filters in a spring application?

Having a Hibernate (3.5)/Spring (3.0)/BlazeDS/Flex stack-based application, I need to apply filters for some of my domain classes as shown below.
#FilterDef(name="notDeletedFilter")
#Filter(name="notDeletedFilter", condition="deleted=0")
public class Item {
private boolean deleted;
//setter and getter
}
These filters should always be applied in my application.
However, according to the hibernate documentation, by default, filters are not enabled for a given hibernate session.
So my question is very simple: How can I enable all defined hibernate filters as above for all Hibernate sessions? Is there anyway to configure my Hibernate Session factory in a spring xml configuration file in order to apply these filters?
If you are using Spring's HibernateTemplate, one solution is to extend it and override the enableFilters method. In it, explicitly enable the filters you need.
You could use AOP (aspect oriented programming) to configure the filter.

Specifying Group for Bean Validation with SimpleFormController

I'm working with an older spring project that's using the SimpleFormController. It was easy to switch in the beanvalidation.LocalValidatorFactoryBean validator bean to look at the annotations on the command object, but I can't see how I would override one of the methods to enable the validator to look at a specified group of validation rules. Any ideas?
You could conceivably write a spring Validator which thinly wraps the javax.validation.Validator, which would allow you to call the rules you would like; and just inject this into the controller.

Categories