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;
}
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.
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.
I am trying to hold some variables in the second level cache. I have read and applied the instructions in Baeldung. However, it keeps giving this error:
Caused by: org.hibernate.cache.NoCacheRegionFactoryAvailableException: Second-level cache is used in the application, but property hibernate.cache.region.factory_class is not given; please either disable second level cache or set correct region factory using the hibernate.cache.region.factory_class setting and make sure the second level cache provider (hibernate-infinispan, e.g.) is available on the classpath.
at org.hibernate.cache.internal.NoCachingRegionFactory.buildTimestampsRegion(NoCachingRegionFactory.java:88)
at org.hibernate.cache.spi.UpdateTimestampsCache.(UpdateTimestampsCache.java:57)
at org.hibernate.internal.CacheImpl.(CacheImpl.java:53)
at org.hibernate.engine.spi.CacheInitiator.initiateService(CacheInitiator.java:28)
at org.hibernate.engine.spi.CacheInitiator.initiateService(CacheInitiator.java:20)
at org.hibernate.service.internal.SessionFactoryServiceRegistryImpl.initiateService(SessionFactoryServiceRegistryImpl.java:49)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:254)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:228)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:207)
at org.hibernate.service.internal.SessionFactoryServiceRegistryImpl.getService(SessionFactoryServiceRegistryImpl.java:68)
at org.hibernate.internal.SessionFactoryImpl.(SessionFactoryImpl.java:244)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:444)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:879)
I added this dependency to pom.xml:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>5.2.2.Final</version>
</dependency>
Then, added these lines to application.properties:
hibernate.cache.use_second_level_cache=true
hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
hibernate.cache.use_query_cache=true
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
At the top of the entity classes, I added these caching annotations:
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
In the configuration file, I added #EnableCaching annotation.
Here are other beans and methods I added to configuration file:
#Bean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(Preconditions.checkNotNull(env.getProperty("spring.datasource.driver-class-name")));
dataSource.setUrl(Preconditions.checkNotNull(env.getProperty("spring.datasource.url")));
dataSource.setUsername(Preconditions.checkNotNull(env.getProperty("spring.datasource.username")));
dataSource.setPassword(Preconditions.checkNotNull(env.getProperty("spring.datasource.password")));
return dataSource;
}
final Properties additionalProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", env.getProperty("hibernate.cache.use_second_level_cache"));
hibernateProperties.setProperty("hibernate.cache.use_query_cache", env.getProperty("hibernate.cache.use_query_cache"));
return hibernateProperties;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
#Bean
public PlatformTransactionManager transactionManager(final EntityManagerFactory emf) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
Is there something wrong with the configuration methods?
Note: This is a spring-boot project.
This might sound like a novice question. I want to inject datasource properties (which I am getting at runtime) and inject it to the bean..
I have a method in my javaclass...
public <String,String>map myMethod(Map<String, String> model) {
Map mapA = new HashMap();
mapA.put("username", "element 1");
mapA.put("password", "element 2");
mapA.put("host", "element 3");
return map;
}
I want to inject these values to my datasource bean in application-context.xml
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value=""/> // inject values here
<property name="url" />
<property name="username" />
<property name="password" />
</bean>
I have seen numerous example on injecting values to beans using properties file but I could not figure out on how to inject a value from java class to the bean properties.
Thanks
You need to create a #Configuration class with a method annotated with #Bean returning an instance of org.apache.commons.dbcp.BasicDataSource.
#Configuration
public class DatasourceConfiguration {
#Bean
public BasicDataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(""); // you can call your code here
ds.setUrl(""); // to get these configuration values
ds.setUsername("");
ds.setPassword("");
return ds;
}
}
It can be a not so elegant solution, but what about this approach?
You can try to return a String from your method.
#Configuration
public class DatasourceConfiguration2 {
#Bean
public String getDataSourceSetting() {
Map<String, String> map = myMethod(model); //assuming that you are not able to edit the original method
StringBuilder sb = new StringBuilder();
for (Entry<String, String> e : map.entrySet()) {
sb.append(e.getKey()).append('=').append(e.getValue()).append(';');
}
}
}
In your xml you can define the property like:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="connectionProperties" value="dataSourceSetting"/>
</bean>
Based on dbcp api:
The "user" and "password" properties will be added explicitly, so they do not need to be included here.
Checking the source code you can see if user and password are null a message like log("DBCP DataSource configured without a 'username'"); will be printed. But the property will be available there.
Finally, in case of url property, there is no option, you need to set it up explicitly.
I have next db.changelog-master.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.7"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.7
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.7.xsd">
<changeSet id="19082014-1" author="autor">
<sql>
CREATE TABLE testings (
id character varying(80) NOT NULL
)
</sql>
</changeSet>
</databaseChangeLog>
My Spring configuration file is looking like this:
#Configuration
#PropertySource("classpath:user.properties")
public class LiquibaseConfig {
#Autowired
private Environment env;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource.drivername"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
#Bean
public SpringLiquibase liquibase() {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource());
liquibase.setChangeLog("classpath:db.changelog-master.xml");
return liquibase;
}
}
So I'm expecting that new table should be created. But my database is still empty. I don't see any errors in logs, what am I doing wrong? Should I change my spring configuration class?
Okay, I think I fixed this problem. Everytime when you run your script you should change id tag in db.changelog-master.xml file.