Hibernate Envers with Spring Boot - configuration - spring

I'm trying to setup Hibernate Envers to work with my Spring Boot application.
I've included the Envers dependency and added #Audited annotations and it works fine, but I'm unable to configure specific Envers properties, Spring Boot doesn't seem to pick them up.
Specifically, I've tried to set the different db schema for audit tables by putting these to application.properties, but without luck:
hibernate.envers.default_schema=app_audit
or
org.hibernate.envers.default_schema=app_audit
or
spring.jpa.hibernate.envers.default_schema=app_audit
Neither of these work. Does anyone know how to set these?
EDIT.
As M. Deinum suggested I tried:
spring.jpa.properties.org.hibernate.envers.default_schema=app_audit
and it worked!

For all those configuration settings that aren't by default available you can specify them by simply prefixing them with spring.jpa.properties. Those properties will be added, as is, to the EntityManagerFactory (as JPA Properties).
spring.jpa.properties.org.hibernate.envers.default_schema=app_audit
Adding the above to the application.properties will add the properties and should configure Hibernate Envers.
This is also documented in the Spring Boot reference guide.
Links
Configure JPA properties
Envers Properties

Looking through the HibernateJpaAutoConfiguration class I can't see any support for envers properties. The following might not be the best solution but nevertheless your can give it a try.
In order to have Spring Boot support the envers properties you have to:
override the current AutoConfiguration class that Spring Boot uses to configure the Hibernate properties, so it will read the envers properties from your property files.
This will read the spring.jpa.hibernate.envers.default_schema from your file and add it to the properties of the entityManagerFactoryBean:
#Configuration
public class HibernateEnversAutoConfiguration extends HibernateJpaAutoConfiguration {
private RelaxedPropertyResolver environment;
public HibernateEnversAutoConfiguration() {
this.environment = null;
}
#Override
public void setEnvironment(Environment environment) {
super.setEnvironment(environment);
this.environment = new RelaxedPropertyResolver(environment, "spring.jpa.hibernate.");
}
#Override
protected void configure(LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
super.configure(entityManagerFactoryBean);
Map<String, Object> properties = entityManagerFactoryBean.getJpaPropertyMap();
properties.put("hibernate.envers.default_schema", this.environment.getProperty("envers.default_schema"));
}
}
exclude the original HibernateJpaAutoConfiguration that Spring Boot uses and add your own as a bean so it will be replaced:
#EnableAutoConfiguration(exclude = HibernateJpaAutoConfiguration.class)
#EnableJpaRepositories(basePackages = "com.gabrielruiu.test")
#EntityScan(basePackages = "com.gabrielruiu.test")
#ComponentScan(basePackages = "com.gabrielruiu.test")
#Configuration
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
#Bean
public HibernateEnversAutoConfiguration hibernateEnversAutoConfiguration() {
return new HibernateEnversAutoConfiguration();
}
}

For those using MySQL and Spring Boot, the suggestion of using:
spring.jpa.properties.org.hibernate.envers.default_schema=yourAuditSchema will not work.
Use this instead:
spring.jpa.properties.org.hibernate.envers.default_catalog=yourAuditSchema

I use with yaml format:
spring:
jpa:
properties:
org:
hibernate:
format_sql: false
envers:
audit_table_suffix: AUDIT
revision_field_name: NRO_ID_REVISAO_AUDITORIA
revision_type_field_name: TPO_REVISAO_AUDITORIA

Related

Programmatic RedissonClient in Spring boot project

I am trying to implement Hibernate second level caching in a Spring boot project using Redisson.
I have followed this blog as a reference
https://pavankjadda.medium.com/implement-hibernate-2nd-level-cache-with-redis-spring-boot-and-spring-data-jpa-7cdbf5632883
Also i am trying to initialize the RedissionClient programmatically and not through declaratively /through a config file
Created a spring bean to be initialized which should create the RedissonClient instance.
#Configuration
#Lazy(value = false)
public class RedissonConfig {
#Bean
public RedissonClient redissionClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
}
However this bean is never intialized and i get the following error while application startup.
Caused by: org.hibernate.cache.CacheException: Unable to locate Redisson configuration
at org.redisson.hibernate.RedissonRegionFactory.createRedissonClient(RedissonRegionFactory.java:107) ~[redisson-hibernate-53-3.12.1.jar:3.12.1]
at org.redisson.hibernate.RedissonRegionFactory.prepareForUse(RedissonRegionFactory.java:83) ~[redisson-hibernate-53-3.12.1.jar:3.12.1]
It seems Spring boot Hibernate still trying to load the Redisson config through a config file.
is it possible to load the Redission config in spring boot programmatically ?
Best Regards,
Saurav
I just did exactly this, here is how:
you need a custom RegionFactory that is similar to the JndiRedissonRegionFactory but gets its RedissonClient injected somehow.
an instance of this Class, fully configured, is put into the hibernate-properties map. Hibernates internal code is flexible: if the value of hibernate.cache.region.factory_class is a string it is treated as a FQDN. If it is an instance of Class<?>, it will be instantiated. If it is an Object, it will be used.
Spring offers a rather simple way to customize hibernate properties with a bean:
#AutoConfiguration(after = RedissonAutoConfiguration.class, before = JpaAutoConfiguration.class)
#ConditionalOnProperty("spring.jpa.properties.hibernate.cache.use_second_level_cache")
public class HibernateCacheAutoConfiguration {
#Bean
public HibernatePropertiesCustomizer setRegionFactory(RedissonClient redisson) {
return hibernateProperties -> hibernateProperties.put(AvailableSettings.CACHE_REGION_FACTORY, new SpringBootRedissonRegionFactory(redisson));
}
}
My RegionFactory is really simple:
#AllArgsConstructor
public class SpringBootRedissonRegionFactory extends RedissonRegionFactory {
private RedissonClient redissonClient;
#Override
protected RedissonClient createRedissonClient(Map properties) {
return redissonClient;
}
#Override
protected void releaseFromUse() {
}
}
I used the redisson-starter to get a RedissonClient, hence the reference to RedissonAutoConfiguration, but you could just create an instance by hand.
It is possible, but then you need to provide a custom implementation of RegionFactory to Hibernate, which can extends RedissonRegionFactory but uses your own client instance.

how does spring boot auto configure the driver of a special datasource?

Without spring boot ,we must specify the detail of a data source,right?
#Configuration
public class DataSourceConfig {
#Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.h2.Driver");
dataSourceBuilder.url("jdbc:h2:mem:test");
dataSourceBuilder.username("SA");
dataSourceBuilder.password("");
return dataSourceBuilder.build();
}
}
With spring boot,we even do not need do anything,I know spring boot will detect whether there is a jar contains a data source to decide create a data source bean or not.I see the source code from org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration :
#Configuration(proxyBeanMethods = false)
#Conditional(PooledDataSourceCondition.class)
#ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
#Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}
But my question is how does spring boot know the driver class or url and else for every different database?I can not find any specification from spring-boot-autoconfigure-2.5.0.jar
From DataSource Configuration in the docs:
Spring Boot can deduce the JDBC driver class for most databases from the URL. If you need to specify a specific class, you can use the spring.datasource.driver-class-name property.
So start without configuring anything and just putting the JDBC driver on the classpath. If it would not work, you can manually configure it.

Setting up in-memory H2 database without Spring Boot

I am working in a spring 5 (Not Sprig Boot) project. I need to test my application with in-memory H2 database. I am using Spring with Java Config on maven build tool. Is there any way I can configure in-memory H2 DB?
Usually I use this in my #Config class:
#Bean
public DataSource h2TestDataSource(){
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
}
So I use Spring Embedded DB in my spring projects (I don't use spring boot)
I hope it's useful.
You can add the DataSource bean using the EmbeddedDatabaseBuilder as follows:
#Bean
public DataSource dataSource(
#Value("${datasource.dbname}") String dbname,
#Value("${datasource.script}") String script) {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.setName(dbname)
.addScript(script)
.build();
}
application.properties
datasource.dbname=users
datasource.script=classpath:resources/users.sql
Also you can register h2-console servlet in the application configuration class as follows:
#Configuration
public class WebAppConfig implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) {
. . .
servletContext
.addServlet("H2Console", WebServlet.class)
.addMapping("/console/*");
. . .
}
}
Then you can open http://localhost:8080/console and connect to the jdbc:h2:mem:users database as follows:
See also How to enable h2-console in spring-webmvc without spring-boot?

How to exclude/disable a specific auto-configuration in Spring boot 1.4.0 for #DataJpaTest?

I am using the #DataJpaTest from Spring for my test which will then use H2 as in memory database as described here . I'm also using Flyway for production. However once the test starts FLyway kicks in and reads the SQL file. How can I exclude the FlywayAutoConfiguration and keep the rest as described here in spring documentation in order to let Hibernate create the tables in H2 for me?
#RunWith(SpringRunner.class)
#DataJpaTest
public class MyRepositoryTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
private MyRepository triggerRepository;
}
Have you tried the #OverrideAutoConfiguration annotation?
It says it "can be used to override #EnableAutoConfiguration".
I'm assuming that from there you can somehow exclude FlywayAutoConfiguration
like so:
#EnableAutoConfiguration(exclude=FlywayAutoConfiguration.class)
Adding the dependency on an in-memory database to my build.gradle
e.g. testRuntime "com.h2database:h2:1.4.194"
And adding flyway.enabled=false to application.properties in src/test/resources worked for me.
I am converting an old JDBC app into a spring-data-jpa app and I'm working on the first tests now. I kept seeing a security module instantiation error from spring-boot as it tried to bootstrap the security setup, even though #DataJpaTest should theoretically be excluding it.
My problem with the security module probably stems from the pre-existing implementation which I inherited using PropertySourcesPlaceholderConfigurer (via my PropertySpringConfig import below)
Following the docs here:
http://docs.spring.io/spring-boot/docs/1.4.x/reference/htmlsingle/#test-auto-configuration
and your comments on #LiviaMorunianu's answer, I managed to work my way past every spring-boot exception and get JUnit to run with an auto-configured embedded DB.
My main/production spring-boot bootstrap class bootstraps everything including the stuff I want to exclude from my tests. So instead of using #DataJpaTest, I copied much of what it is doing, using #Import to bring in the centralized configurations that every test / live setup will use.
I also had issues because of the package structure I use, since initially I was running the test which was based in com.mycompany.repositories and it didn't find the entities in com.mycompany.entities.
Below are the relevant classes.
JUnit Test
#RunWith(SpringRunner.class)
#Transactional
#Import({TestConfiguration.class, LiveConfiguration.class})
public class ForecastRepositoryTests {
#Autowired
ForecastRepository repository;
Forecast forecast;
#Before
public void setUp() {
forecast = createDummyForecast(TEST_NAME, 12345L);
}
#Test
public void testFindSavedForecastById() {
forecast = repository.save(forecast);
assertThat(repository.findOne(forecast.getId()), is(forecast));
}
Live Configuration
#Configuration
#EnableJpaRepositories(basePackages = {"com.mycompany.repository"})
#EntityScan(basePackages = {"com.mycompany.entity"})
#Import({PropertySpringConfig.class})
public class LiveConfiguration {}
Test Configuration
#OverrideAutoConfiguration(enabled = false)
#ImportAutoConfiguration(value = {
CacheAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
TransactionAutoConfiguration.class,
TestDatabaseAutoConfiguration.class,
TestEntityManagerAutoConfiguration.class })
public class TestConfiguration {
// lots of bean definitions...
}
PropertySpringConfig
#Configuration
public class PropertySpringConfig {
#Bean
static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer()
throws IOException {
return new CorePropertySourcesPlaceholderConfigurer(
System.getProperties());
}
}
In my particular case, i needed to disable the FlywayDB on in-memory integration tests. These are using a set of spring annotations for auto-configuring a limited applicationContext.
#ImportAutoConfiguration(value = TestConfig.class, exclude = FlywayAutoConfiguration.class)
the exclude could effectively further limit the set of beans initiated for this test
I had the same problem with my DbUnit tests defined in Spock test classes. In my case I was able to disable the Flyway migration and managed to initialize the H2 test database tables like this:
#SpringBootTest(classes = MyApplication.class, webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = ["flyway.enabled=false", "spring.datasource.schema=db/migration/h2/V1__init.sql"])
I added this annotation to my Spock test specification class. Also, I was only able to make it work if I also added the context configuration annotation:
#ContextConfiguration(classes = MyApplication.class)
I resolved the same issue by excluding the autoconfiguration from my application definition, i.e.
#SpringBootApplication(exclude = {FlywayAutoConfiguration.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
you can also sue the following annotation:
#RunWith(SpringRunner.class)
#DataJpaTest(excludeAutoConfiguration = {MySqlConfiguration.class, ...})
public class TheClassYouAreUnitTesting {
}
You can just disable it in your test yaml file:
flyway.enabled: false

Spring Boot + Mybatis #MapperScan and SqlSessionFactory

Im developing a new app using Spring Boot. I use Mybatis for persistance. Im using Java Config for everything I can.
I'm getting this exception when the app starts regarding creating my Mybatis mapper interface
exception is java.lang.IllegalArgumentException: Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
My Sring Boot application class is set up like this
#SpringBootApplication
#MapperScan("com.mydomain.admin.service.dao")
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
}
The Mybatis mapper interface class is set up like this
package com.mydomain.admin.service.dao;
public interface AdminClientDAO {
#Select("SELECT clientId, name, enabledFlag as enabled, dateAdded, dateUpdated as updateDate FROM client")
public List<Client> findAll();
}
my datasource is configured with spring boot. I've named the properties
spring.datasource.* so spring boot with auto-configure the data source
Now, Im wondering if Im assuming too much spring boot magic. I assumed that spring boot would configure the sqlSessionFactory because mybatis was in the classpath..
Many examples I see show configuring the sqlSessionFactory as a #Bean in the Java Config.. Is this the way it should be done is should spring boot be doing some magic and auto-config it?
I found my issue. I was missing mybatis-spring-boot-starter
I have
#Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
return sessionFactory.getObject();
}
In class called Application.java which extends
org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
And my Application.java is initialized in class which extends
org.springframework.boot.context.web.SpringBootServletInitializer
And the datasource works fine in my Spring-Boot Application.
Hope this helps somebody searching for Spring Boot, Mybatis and SQLSessionFactory with datasource in spring.datasource.*

Resources