I am new to Spring boot development.
What I did?
I tested the sample MySQL integration with Spring boot and tested it works fine.
What I am trying?
I am trying to use Hibernate APIs with my existing project. So I create a util class for creating Hibernate sessionFactory.
My code:
public static void initSessionFactory() {
System.out.print("initSessionFactory");
if (sessionFactory == null) {
try {
Configuration configuration = new Configuration();
//***** I don't want configuration here. ******
// Hibernate settings equivalent to hibernate.cfg.xml's properties
//Properties settings = new Properties();
// settings.put(Environment.DRIVER, "com.mysql.cj.jdbc.Driver");
// settings.put(Environment.URL, "jdbc:mysql://localhost:3306/hibernate_db?useSSL=false");
// settings.put(Environment.USER, "root");
// settings.put(Environment.PASS, "root");
// settings.put(Environment.DIALECT, "org.hibernate.dialect.MySQL5Dialect");
// settings.put(Environment.CURRENT_SESSION_CONTEXT_CLASS, "thread");
// settings.put(Environment.HBM2DDL_AUTO, "create-drop");
// settings.put(Environment.SHOW_SQL, "true");
// configuration.setProperties(settings);
//configuration.addAnnotatedClass(Student.class);
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties()).build();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
session = sessionFactory.openSession();
} catch (Exception e) {
e.printStackTrace();
}
}
}
My application.properties:(It works fine for without Hibernate)
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/tcc
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
spring.jpa.show-sql=true
My problem:
I already configured the jdbc environment & other configuration in my application.properties. It works fine. So I don't want to repeat the same configuration in my Java code. So I commented this configuration.
But without Java configuration it throws "The application must supply JDBC connections" error.
My Question:
How to set configuration from application.properties for Hibernate?
When you set the properties below in your app.properties, the Spring Boot will already make your DB connection ready when the application start.
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/db_example
spring.datasource.username=springuser
spring.datasource.password=ThePassword
spring.datasource.driver-class-name =com.mysql.jdbc.Driver
You do not need to create SessionFactory , ServiceRegistery etc. even if you do not want to manage DB connection manually.
This link also explain the steps in more detail. You will see that there is no any custom bean, factory or registerer to establish DB connection. Please see it.
https://spring.io/guides/gs/accessing-data-mysql/
Related
in spring boot project, using custom DB configuration. One method is there where adding all properties in map. SQL Server DB
protected Map<String, Object> jpaProperties() {
HashMap<String, Object> jpaProperties = new HashMap<>();
jpaProperties.put("hibernate.ddl-auto", "validate");
jpaProperties.put("spring.jpa.hibernate.ddl-auto", "validate");
jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.SQLServer2012Dialect");
jpaProperties.put("org.hibernate.envers.store_data_at_delete", "false");
return jpaProperties;
}
envers & those properties are working
"spring.jpa.properties.org.hibernate.envers.store_data_at_delete" for this adding "org.hibernate.envers.store_data_at_delete"
but, when adding "hibernate.ddl-auto", it is not validating schema.
Tried both "hibernate.ddl-auto" & "spring.jpa.hibernate.ddl-auto".
Read some stack overflow questions, saw most are telling to add "hibernate.hbm2ddl.auto", that is giving validating error, but for proper table also it is giving error
Tried adding "spring.jpa.generate-ddl" with value "true" too.
that means properties are getting used for custom db, only the ddl-auto one is not working.
Anything wrong in this?
Implemented Spring's AbstractRoutingDatasource by dynamically determining the actual DataSource based on the current context.
Refered this article : https://www.baeldung.com/spring-abstract-routing-data-source.
Here on spring boot application start up . Created a map of contexts to datasource objects to configure our AbstractRoutingDataSource. All these client context details are fetched from a database table.
#Bean
#DependsOn("dataSource")
#Primary
public DataSource routeDataSource() {
RoutingDataSource routeDataSource = new RoutingDataSource();
DataSource defaultDataSource = (DataSource) applicationContext.getBean("dataSource");
List<EstCredentials> credentials = LocalDataSourcesDetailsLoader.getAllCredentails(defaultDataSource); // fetching from database table
localDataSourceRegistrationBean.registerDataSourceBeans(estCredentials);
routeDataSource.setDefaultTargetDataSource(defaultDataSource);
Map<Object, Object> targetDataSources = new HashMap<>();
for (Credentials credential : credentials) {
targetDataSources.put(credential.getEstCode().toString(),
(DataSource) applicationContext.getBean(credential.getEstCode().toString()));
}
routeDataSource.setTargetDataSources(targetDataSources);
return routeDataSource;
}
The problem is if i add a new client details, I cannot get that in routeDataSource. Obvious reason is that these values are set on start up.
How can I achieve to add new client context and I had to re intialize the routeDataSource object.
Planning to write a service to get all the client context newly added and reset the routeDataSource object, no need to restart the server each time any changes in the client details.
A simple solution to this situation is adding #RefreshScope to the bean definition:
#Bean
#Primary
#RefreshScope
public DataSource routeDataSource() {
RoutingDataSource routeDataSource = new RoutingDataSource();
DataSource defaultDataSource = (DataSource) applicationContext.getBean("dataSource");
List<EstCredentials> credentials = LocalDataSourcesDetailsLoader.getAllCredentails(defaultDataSource); // fetching from database table
localDataSourceRegistrationBean.registerDataSourceBeans(estCredentials);
routeDataSource.setDefaultTargetDataSource(defaultDataSource);
Map<Object, Object> targetDataSources = new HashMap<>();
for (Credentials credential : credentials) {
targetDataSources.put(credential.getEstCode().toString(),
(DataSource) applicationContext.getBean(credential.getEstCode().toString()));
}
routeDataSource.setTargetDataSources(targetDataSources);
return routeDataSource;
}
Add Spring Boot Actuator as a dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Then trigger the refresh endpoint POST to /actuator/refresh to update the DataSource (actually every refresh scoped bean).
So this will depend on how much you know about the datasources to be added, but you could set this up as a multi-tenant project. Another example of creating new datasources:
#Autowired private Map <String, Datasource> mars2DataSources;
public void addDataSourceAtRuntime() {
DataSourceBuilder dataSourcebuilder = DataSourcebuilder.create(
MultiTenantJPAConfiguration.class.getclassloader())
.driverclassName("org.postgresql.Driver")
.username("postgres")
.password("postgres")
.url("Jdbc: postgresql://localhost:5412/somedb");
mars2DataSources("tenantX", datasourcebuilder.build())
}
Given that you are using Oracle, you could also use its database change notification features.
Think of it as a listener in the JDBC driver that gets notified whenever something changes in your database table. So upon receiving a change, you could reinitialize/add datasources.
You can find a tutorial of how to do this here: https://docs.oracle.com/cd/E11882_01/java.112/e16548/dbchgnf.htm#JJDBC28820
Though, depending on your organization database notifications need some extra firewall settings for the communication to work.
Advantage: You do not need to manually call the REST Endpoint if something changes, (though Marcos Barberios answer is perfectly valid!)
I will mention my problem.
I need two databases.
DB1 for my application tables
DB2 for saving only the audited tables jv_...
To solve the problem I did the following
`#Bean
public ConnectionProvider jpaConnectionProvider() {
OtherConnectionProvider other = new OtherConnectionProvider();
try {
other.setConnection(dataSource().getConnection());
} catch (SQLException e) {
e.printStackTrace();
}
return scp;
}`
OtherConnectionProvider is a implementation of org.javers.repository.sql.ConnectionProvider.
dataSource() is the normal javax.sql.Datasource.
After using this, spring ignores the database properties mentioned in application.properties and creates the schema and javers related tables in this new schema since I have the following in my application.properties.
spring.jpa.hibernate.ddl-auto=create
Setting dedicated database for Javers audit data is easy for MongoDB (see https://javers.org/documentation/spring-boot-integration/#starter-repository-configuration), but there is no out-of-the box solution for SQL. The main problem is coordinating transactions in two independent SQL databases.
See How to configure transaction management for working with 2 different db in Spring?
Thanks for the reply. I fixed it the following way. See https://www.baeldung.com/spring-data-jpa-multiple-databases. But in the url, it has mentioned about two database configuration. One of the configuration which is Primary should be picked up from application.properties. Second database configuration can be picked up from spring configuration as mentioned in the URL https://javers.org/documentation/spring-integration/#jpa-entity-manager-integration. The solution is tricky enough since the standard properties of spring.datasource are not applicable here. Moreover, addition of commit properties using javers will help. This will act as tenant information.
Following is the typical code where CustomJpaHibernateConnectionProvider is the implementation of org.javers.repository.sql.ConnectionProvider
#Bean
public ConnectionProvider jpaConnectionProvider() {
CustomJpaHibernateConnectionProvider scp = new
CustomJpaHibernateConnectionProvider();
try {
scp.setConnection(dataSource().getConnection());
} catch (SQLException e) {
e.printStackTrace();
}
return scp;
}
and the datasource would like this.
#Bean
#ConfigurationProperties(prefix="spring.javers-datasource")
public DataSource dataSource(){
return DataSourceBuilder.create().build();
}
The data sources properties are not standard Spring boot properties.
spring.datasource.jdbcUrl = jdbc:postgresql://localhost/test
spring.datasource.username = postgres
spring.datasource.driverClassName=org.postgresql.Driver
Is there a solution that is configured driver properties in the application.properties file?
DB2 HADR using clientRerouteAlternateServerName, clientRerouteAlternatePortNumber, enableClientAffinitiesList to indicate alternate server info.
I tried use below code to config datasource.
```
#Bean
public DataSource initds(){
Properties p = new Properties();
p.setProperty("user", "user");
p.setProperty("password", "password");
p.setProperty("clientRerouteAlternateServerName", "172.30.1.60,172.30.1.61");
p.setProperty("clientRerouteAlternatePortNumber", "53000,53000");
p.setProperty("enableClientAffinitiesList", "1");
DriverManagerDataSource dmds = new DriverManagerDataSource();
dmds.setDriverClassName("com.ibm.db2.jcc.DB2Driver");
dmds.setConnectionProperties(p);
dmds.setUrl("jdbc:db2://172.30.1.60:53000/dbsam");
return dmds;
}
```
I'm using Dynamic DataSource in my Spring Boot application.
The problem is I need to generate tables from my entities. There is a way with
spring.jpa.hibernate.ddl-auto=update
but it doesn't work for me since I need to connect to Database in run-time.
What I need to know is can I call some method to do same things as Spring does on application startup with mentioned option.
Okey after some research I found the answer. All you need is to ask sessionFactoryBuilder to generate update scripts for your database and execute than with JdbcTemplate.
LocalSessionFactoryBuilder sessionFactory = new LocalSessionFactoryBuilder(dataSource);
sessionFactory.scanPackages("su");
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
try{
List<SchemaUpdateScript> scripts = sessionFactory.generateSchemaUpdateScriptList(new PostgreSQL9Dialect(),
new DatabaseMetadata(dataSource.getConnection(), new PostgreSQL9Dialect(), sessionFactory));
log.info("Schema update scripts["+scripts.size()+"]");
for (SchemaUpdateScript script:scripts ) {
log.info(script.getScript());
jdbcTemplate.execute(script.getScript());
}
}catch (Exception e){
log.error("error updating schema",e);
}