My Spring boot application has a multitenancy architecture and I'm using Javers to audit some data models.
The issue I'm facing is that Javers is not able to resolve the database based on my MongoDatabaseFactory implementation.
So far I've tried creating a Javers Configuration Bean which looks like this:
#Component
public class JaversMongoConfiguration {
#Autowired
CachedMongoClients cachedMongoClients;
#SneakyThrows
public Javers javers() {
MongoRepository javersMongoRepository =
new MongoRepository(cachedMongoClients.getMongoDatabaseForCurrentContext()); // Custom method to fetch the MongoDataBase based on current TenantContext
return JaversBuilder.javers()
.registerJaversRepository(javersMongoRepository)
.build();
}
}
This isn't working as during project build time, Javers instantiates the connection to the default database and dynamically doesn't switch to the tenant database as intended during run time.
Hence, all my audit logs are getting saved at the default database and not in tenant database.
Note: I'm using Javers Spring Boot Mongo starter and Javers Mongo Persistence Maven dependencies.
In Javers there is no integration with Spring multitenancy. Javers is open source, you are encouraged to contribute https://github.com/javers/javers/
Related
In addition to a non-reactive JPA repository, I introduced a reactive repository in my Spring Boot app with H2 database.
com.app.respository.BusinessRepository extends JpaRepository
com.app.respository.r2dbc.PendingBusinessRepository extends ReactiveCrudRepository
And I added a connection factory for reactive H2.
#Configuration
#EnableR2dbcRepositories
public class R2DBCConfiguration extends AbstractR2dbcConfiguration {
#Bean
public H2ConnectionFactory connectionFactory() {
return new H2ConnectionFactory(
H2ConnectionConfiguration.builder()
.url("jdbc:h2:file:~/data/demo-rxdb")
.username("sa")
.password("password")
.build());
}
}
After this change, my application is not able to find the non-reactive repository. It says:
Field businessRepository in
com.app.service.BusinessServiceImpl required a bean
of type 'com.app.repository.BusinessRepository' that could not be
found.
The injection point has the following annotations:
#org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type
'com.app.repository.BusinessRepository' in your configuration.
I can only guess that maybe my H2 database is now reactive (non-blocking) which is not supported by a JPARepository (blocking). But is my assumption correct?
I have mixed needs. I need only PendingBusiness table data in a non-blocking way (which is constantly being fluxed out on UI through event stream) and I need the rest of the tables' data in a traditional blocking way. Is it possible to achieve what I want through a single H2 database instance?
I do have a multi-tenant-aware Spring Boot application (see https://medium.com/swlh/multi-tenancy-implementation-using-spring-boot-hibernate-6a8e3ecb251a) with a bunch of jpa entities that do exist per database. I do have one entity table Tenant though, that is supposed to exist in the default database only and store information about the various tenants (e.g. database name). How do i fix the database for the entity class in a tenant-per-database setup?
In a tenant-per-schema you could conveniently use the #Table annotation for that like follows for example:
#Entity
#Table(name="tenant", schema = "public")
public class Tenant {
...
Does there exist a similar approach for per-database setups?
You'll need to setup multiple datasources, then connect dedicated entity manager to each datasource, then attach JPA repositories for each dedicated entity manager.
Here is a good sample:
https://medium.com/#joeclever/using-multiple-datasources-with-spring-boot-and-spring-data-6430b00c02e7
I'm following an online tutorial on how to connect a Java class to a my database using hibernate, however I can't find how to create a SessionFactory since I'm not using a hibernate config file, so
SessionFactory factory = new Configuration().configure()
doesn't work for me. I used spring initializr with Web, JPA, MySQL to create the project, and I've set up my application.properties accordingly (so hibernate IS connected to the database)
I've tried looking at some other answers such as
Spring Boot - Handle to Hibernate SessionFactory
However the answers are a bit outdated/unclear, I'm trying to create an object and begin a transaction with it in my session in my main.java and the only problem I have is getting the FactorySession to work.
In the tutorial it shows a simple solution of
SessionFactory factory = new Configuration().configure("hibernate-cfg.xml.).addAnnotatedClass(Student.class).buildSessionFactory();
Session session = factory.getCurrentSession();
this creates a sessionfactory from the configuration file, and creates a session from that, How can I achieve the same?
You should use spring data jpa , it will greatly simply your job.When spring boot will see data JPA in ur classpath it will try to automatically auto wire entity manager for u.Of course it would need database bean as well which in you case should be coming from MYSQL datasource bean.You have to define ur entity classes with #Entity annotation and follow the standard convention of JPA bean. Then u have to extend CrudRepository in your dedicated repository
Lets say you have Person entity with Long as ID of the bean then ur person repository would look like below
#Repository
public interface PersonRepository
extends CrudRepository < Person , Long> {
}
Then you can inject this repository in ur service classes or if its small project then in controller. Please check out methods in CrudRepository for more information. It has basic methods for save , update ,delete etc for the entity.
Hope that helps.
Is it possible to load initial data in a MongoDB database using src/main/resources/data.sql or by any other file?
I understand that data.sql is used for SQL DB's whereas MongoDB is a NOSQL DB. But just wanted to know if there is any equivalent of data.sql for NOSQL DB's.
While googling I found out this SO link (Spring Boot - Loading Initial Data) which does what I am looking for but still it's not a standalone file data.sql.
To load initial data you can use db migration tool like MongoBee
It's very useful option to handle data initialization in java. You just need to configure #Bean public Mongobee mongobee in your spring boot and setup component scan for data ChangeLogs where data creation actually happens.
You can use a repository populator with Spring Data MongoDB. Let me demonstrate this with a code sample in Kotlin:
#Configuration
class TestApplicationConfig {
#Value("classpath:test_data.json")
private lateinit var testData: Resource
#Bean
#Autowired
fun repositoryPopulator(objectMapper: ObjectMapper): Jackson2RepositoryPopulatorFactoryBean {
val factory = Jackson2RepositoryPopulatorFactoryBean()
// inject your Jackson Object Mapper if you need to customize it:
factory.setMapper(objectMapper)
factory.setResources(arrayOf(testData))
return factory
}
}
Put test_data.json in resources directory.
you can define your data in json/xml and use populator elements of the repository to load the data.
https://docs.spring.io/spring-data/mongodb/docs/2.0.9.RELEASE/reference/html/#core.repository-populators
I have a need to dynamically create and connect to potentially hundreds of databases using from a single ResourceServer (REST Server). The REST controller would do something like this:
#RequestMapping("/teachers")
public List<Teacher> teachers(#RequestParam(value="db", defaultValue="db") String db) {
//Look up the correct datasource
DataSource ds = DSSources.get(db);
//Associate the datasource with the repository
...
//Return the teachers from the database using
//the TeacherRepository (Spring Data JPA Repository)
return TeacherRepository.getAllTeachers();
}
I'm thinking that the DSSources is a Map<String, Datasource that contains the DataSource instances. How do I programmatically create the datasources? Once they are created, how are they associated with the Spring Data JPA Repositories? All databases will share a common set of repositories.
Storing Datasource in a Map is not a good solution has you have to think of Datasources failing and recreating those as well.
What you need is Multi Tenancy. If you use Hibernate underneath JPA you can use Hibernate Multi Tenancy. I am pretty sure other ORMs also do provide Multi Tenancy capabilities.