In our Spring 4 application, we currently configure database connection in applicationContext.xml :
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="poolName" value="springHikariCP" />
<property name="dataSourceClassName" value="org.postgresql.ds.PGSimpleDataSource" />
<property name="maximumPoolSize" value="10" />
<property name="idleTimeout" value="30000" />
<property name="dataSourceProperties">
<props>
<prop key="url">jdbc:postgresql://google/mydb?cloudSqlInstance=project:region:myinstance&socketFactory=com.google.cloud.sql.postgres.SocketFactory</prop>
<prop key="user">postgres</prop>
<prop key="password">password</prop>
</props>
</property>
</bean>
Instead on defining this in applicationContext.xml, can I define the database configuration in a class such as the following:
HikariConfig config = new HikariConfig();
config.setJdbcUrl(JDBC_URL);
config.setUsername(DB_USER);
config.setPassword(DB_PASS);
....
Is it possible to do this?
You can define properties on HikariDataSource
public DataSource getDataSource(){
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(JDBC_URL);
dataSource.setUsername(DB_USER);
dataSource.setPassword("DB_PASS);
return dataSource;
}
public class HikariDataSource extends HikariConfig implements DataSource, Closeable
Then declare your method as a #Bean in #Configuration class
#Bean
public DataSource dataSource() {
return DataSourceClass.getDataSource();
}
This is a king of more general solution, not specific for your DataSource but it can be useful:
#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();
}
}
What you actually need is a DataSource bean (in XML you define a bean right?) and also put it into #Configuration class so the Spring could pick it up.
What you put in that method should return pre-configured for your specific case data source.
Also, you could try something like this:
#Bean
public DataSource dataSource() throws SQLException {
HikariConfig config = new HikariConfig("/hikari.properties");
HikariDataSource dataSource = new HikariDataSource(config);
return dataSource;
}
But then you need hikari.properties file in the classpath. Example:
driverClassName=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/myDb
connectionTestQuery=SELECT 1
maximumPoolSize=20
username=...
password=...
Remember to keep #Bean in #Configuration :)
Simply annotate a class with #Configuration, making sure that this class is scanned by Spring when the application starts.
Within this class declare a datasource as #Bean in this way:
#Configuration
public class DataSourceConfig {
// More code...
#Bean(name="datasource")
public DataSource dataSource() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName(org.postgresql.Driver.class.getName());
hikariConfig.setJdbcUrl("jdbc:postgresql://....");
hikariConfig.setUsername("postgres");
hikariConfig.setPassword("password");
hikariConfig.setMaximumPoolSize(10);
hikariConfig.setIdleTimeout(30000);
hikariConfig.setPoolName("springHikariCP");
HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
return hikariDataSource;
}
// More code...
}
And that's all.
Related
I am trying to configure HikariCp in MyBatis using XML configuration
I want to know how to set the hikariCongig object in object in the mapper configuration.
my config looks like this :
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="com.xyz.config.HikariCPDataSourceFactory" >
<property name="jdbcUrl" value="jdbc:postgresql://localhost:5432/beta-prod-db" />
<property name="username" value="postgres" />
<property name="password" value="${password}" />
<property name="poolName" value="test"/>
<property name="maxPoolSize" value="20" />
<property name="registerMbeans" value="true"/>
<property name="minimumIdle" value="5"/>
</dataSource>
</environment>
HikariCPDataSourceFactory.java
public class HikariCPDataSourceFactory extends PooledDataSourceFactory {
public HikariCPDataSourceFactory() {
// HikariConfig hikariConfig = new HikariConfig();
this.dataSource = new HikariDataSource();
}
}
I don't find any online article that shows how to set the hikariConfig object in the hikariDataSource object through XML configuration.
using Spring I can create a bean for hikariConfig and pass it as a parameter in the hikariDataSource object, but here I am not using spring so need to find a way with XML.
Without the hikariConfig object, if I try to get the HikariPoolMXBean object from dataSource I get the exception
org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.
HikariCP 1.4.0 MBean InstanceNotFoundException
this article says it only works when I set the hikariConfig Object
You need to implement DataSourceFactory and pass the properties you specified in your MyBatis XML Configuration file to the new data source:
public class HikariCPDataSourceFactory implements DataSourceFactory {
private HikariDataSource dataSource;
#Override
public void setProperties(Properties props) {
HikariConfig config = new HikariConfig(props);
this.dataSource = new HikariDataSource(config);
}
#Override
public DataSource getDataSource() {
return dataSource;
}
}
i couldnt find a way to configure the hikariConfig in xml
Here is the workaround i used that works well for me.
HikariDataSource hikariDataSource = null;
HikariConfig hikariConfig = new HikariConfig();
dataSource.copyStateTo(hikariConfig);
hikariDataSource = new HikariDataSource(hikariConfig);
once i get the dataSource object i copy the state to a hikariConfig object and create new dataSource object using it.
Also we can make it singleton so only one instance is created.
I'm injecting #PersistenceContext into my DAO classes like
#PersistenceContext
private EntityManager em;
#PersistenceContext is configured in my project via spring xml configuration. I'm trying to convert the following spring xml configuration to annotations
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:packagesToScan="com.company.myagentmonitor.model"
p:dataSource-ref="companyDataSource"
>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false" />
<property name="showSql" value="true" />
</bean>
</property>
</bean>
<!-- Transactions -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
I'm trying to convert this config to #Beans I can autowire in my DAO classes. What I have currently is not working
#Bean
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(companyDataSource());
entityManagerFactory.setPackagesToScan(new String[] { "com.company.myagentmonitor.model" });
entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter());
return entityManagerFactory.getObject();
}
#Bean
JpaTransactionManager transactionManager(final EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory());
return transactionManager;
}
#Bean
HibernateJpaVendorAdapter jpaVendorAdapter(){
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(false);
jpaVendorAdapter.setShowSql(true);
return jpaVendorAdapter;
}
I basically get a null pointer exception at runtime. I think it has something to do with the way I'm creating or using EntityManagerFactory.
Caused by: java.lang.NullPointerException: null
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.initProxyClassLoa
der(SharedEntityManagerCreator.java:199) ~[spring-orm-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.<init>(SharedEnti
tyManagerCreator.java:191) ~[spring-orm-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
at org.springframework.orm.jpa.SharedEntityManagerCreator.createSharedEntityManager(SharedEntityManagerCreator.j
ava:163) ~[spring-orm-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityMa
nager(PersistenceAnnotationBeanPostProcessor.java:719) ~[spring-orm-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToIn
ject(PersistenceAnnotationBeanPostProcessor.java:680) ~[spring-orm-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:
169) ~[spring-beans-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-bea
ns-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(Persiste
nceAnnotationBeanPostProcessor.java:354) ~[spring-orm-4.3.6.RELEASE.jar!/:4.3.6.RELEASE]
... 64 common frames omitted
Can someone take a look? Not sure what I'm doing wrong. Thank you!
change this
#Bean
public EntityManagerFactory entityManagerFactory() {
return entityManagerFactory.getObject();
}
to this
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
return entityManagerFactory;
}
and in your transactionManager you also change
transactionManager.setEntityManagerFactory(entityManagerFactory());
to
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
First off all you shouldn't be calling getObject in your entityManagerMethod() let Spring handle that for you. Just return the LocalContainerEntityManagerFactoryBean.
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(companyDataSource());
entityManagerFactory.setPackagesToScan(new String[] { "com.company.myagentmonitor.model" });
entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter());
return entityManagerFactory;
}
The LocalContainerEntityManagerFactoryBean implements several interfaces which spring detects and as such will register callbacks for (init and destruction) and also inject some needed dependencies.
Next where you need the EntityManagerFactory just inject it into your method instead of calling entityManagerFactory().
#Bean
JpaTransactionManager transactionManager(final EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
Currently I am moving my beans creation and config settings from XML based to JAVA based spring config. I am stuck with moving db settings? How can I write these hibernate db settings in JAVA based config file?
<bean
class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="hibernateProperties">
<value>
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.url=jdbc:mysql://url
hibernate.connection.username=username
hibernate.connection.password=password
hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.show_sql=false
</value>
</property>
<property name="packagesToScan" value="com.test" />
</bean>
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven proxy-target-class="true" />
I have added Transaction details. I am getting this exception:
Caused by: java.lang.ClassNotFoundException:
org.hibernate.context.spi.CurrentSessionContext
Here is the config. I have also added transaction management features which you will definately need in the final solution.
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableTransactionManagement
public class DbConfig{
#Bean
public DataSource getDatasource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://url");
dataSource.setUsername("username");
dataSource.setPassword("password");
return dataSource;
}
#Bean
public SessionFactory getSessionFactory() throws IOException{
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setPackagesToScan("com.test");
//getHibernateProperties method is a private method
sessionFactoryBean.setHibernateProperties(getHibernateProperties());
sessionFactoryBean.setDataSource(getDatasource());
sessionFactoryBean.afterPropertiesSet();
return sessionFactoryBean.getObject();
}
#Bean
public HibernateTransactionManager getTransactionManager() throws IOException{
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(getSessionFactory());
return transactionManager;
}
private static Properties getHibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
hibernateProperties.put("hibernate.show_sql", false);
// other properties
return hibernateProperties;
}
The accepted answer isn't full. This code helped me out:
#Configuration
#ComponentScan(basePackageClasses = {массив пакетов с классами #Component, #Service, #Repository, #Controller})
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "com.jdev.blog.admin.crud.repositories", entityManagerFactoryRef = "entityManagerFactory")
public class ApplicationConfiguration {
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
entityManagerFactoryBean.setJpaProperties(hibProperties());
return entityManagerFactoryBean;
}
private Properties hibProperties() {
Properties properties = new Properties();
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
return properties;
}
#Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
}
import com.sda.hibernate.associations.one_to_many_bi.Child;
import com.sda.hibernate.associations.one_to_many_bi.Parent;
import com.sda.hibernate.associations.one_to_many_uni.Daughter;
import com.sda.hibernate.associations.one_to_many_uni.Mother;
import com.sda.hibernate.associations.one_to_many_uni_join.Father;
import com.sda.hibernate.associations.one_to_many_uni_join.Son;
import com.sda.hibernate.types.Player;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.service.ServiceRegistry;
import java.util.Properties;
public class HibernateUtil {
// get a session factory
public static SessionFactory getSessionFactory() {
Configuration configuration = createConfig();
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties()).build();
return configuration.buildSessionFactory(serviceRegistry);
}
private static Configuration createConfig() {
Configuration configuration = new Configuration();
Properties settings = new Properties();
settings.put(Environment.DRIVER, "com.mysql.cj.jdbc.Driver");
settings.put(Environment.URL, "jdbc:mysql://localhost:3306/hibernate?serverTimezone=UTC");
settings.put(Environment.USER, "root");
settings.put(Environment.PASS, "root");
settings.put(Environment.DIALECT, "org.hibernate.dialect.MySQL8Dialect");
settings.put(Environment.SHOW_SQL, "true");
settings.put(Environment.CURRENT_SESSION_CONTEXT_CLASS, "thread");
settings.put(Environment.HBM2DDL_AUTO, "create-drop");
configuration.setProperties(settings);
// add annotated classes
configuration.addAnnotatedClass(Player.class);
configuration.addAnnotatedClass(Mother.class);
configuration.addAnnotatedClass(Daughter.class);
configuration.addAnnotatedClass(Father.class);
configuration.addAnnotatedClass(Son.class);
configuration.addAnnotatedClass(Child.class);
configuration.addAnnotatedClass(Parent.class);
return configuration;
}
}
I want to get a JNDI value in my java conf Spring.
the context.xml file:
<Environment name="foo" type="java.lang.String" value="bar" />
the xml spring config:
<jee:jndi-lookup id="foobar" jndi-name="java:comp/env/foo" default-value="nothing"/>
the java spring config:
#Bean
public String foobar() {
???
}
If someone could you give me an example it'll be very useful. Thanks
Edit:
Try autowiring JndiObjectFactoryBean it will have a getJndiName() method, which is inherited from JndiObjectLocator will expose your jndi name
Try this
#Bean
public DataSource dataSource() {
final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
dsLookup.setResourceRef(true);
DataSource dataSource = dsLookup.getDataSource("jdbc/yourJdbcGoesHere");
return dataSource;
}
Hi I have a legacy code which gets its jdbc connections through
DataSource.getConnection()
The DataSource is bounded to Jndi namespace.,
Suppose that I have a function which gets its connections like that:
foo(){
...
Connection con = DataSource.getConnection()
...
}
And I want to run this foo method into a well defined spring transaction. How would I do that ?
I have used TransactionAwareDataSourceProxy and It worked quite well before I move onto something like JPA
At first I could synronize foo's transaction with my spring transaction with this configuration.
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations"><list><value>classpath:/db.properties</value></list></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg ref="dbcpDataSource"/>
</bean>
<bean id="dbcpDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>oracle.jdbc.driver.OracleDriver</value></property>
<property name="url"><value>jdbc:oracle:thin:#${jdbc.url}:1521:${jdbc.db}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
<!-- <property name="defaultAutoCommit"><value>true</value></property> -->
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
After that I move onto JPA using LocalContainerEntityManagerFactoryBean and JpaTransactionManager.
package setup;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
#Configuration
#EnableJpaRepositories
public class SpringContextConfiguration {
#Bean
public TestsSetup testSetup(){
return new TestsSetup();
}
#Bean
public TransactionAwareDataSourceProxy dataSource(){
TransactionAwareDataSourceProxy tp = new TransactionAwareDataSourceProxy();
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
ds.setUrl("jdbc:oracle:thin:#a.a.a.a:port:some");
ds.setUsername("user");
ds.setPassword("paswd");
ds.setDefaultAutoCommit(true);
tp.setTargetDataSource(ds);
return tp;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource);
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("setup");
return lef;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(false);
hibernateJpaVendorAdapter.setDatabase(Database.ORACLE);
return hibernateJpaVendorAdapter;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
}
Now my legacy code blocks do not synchronize with my managed transactions. How can I overcome this problem. Any tools or comments is greatly appreciated.
EDIT
Inject the DataSource and EntityManagerFactory into the JpaTransactionManager bean. See class-level comment # http://docs.spring.io/spring/docs/3.2.5.RELEASE/javadoc-api/org/springframework/orm/jpa/JpaTransactionManager.html for more info.
The above configuration is actually correct and the underlying legacy functions is being called inside the created spring transaction. What I miss is that on the upper layer I needed to flush the underlying session to the datastore. When I did that the legacy transaction begins to aware of the changed data and everything works perfect and also the transactions can be rolled back.
I flush my session to the datastore with this :
#Autowired
PlatformTransactionManager pt;
TransactionDefinition td = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_MANDATORY);
pt.getTransaction(td).flush();