Does anyone knows to configure a EmbeddedDatabaseBuilder Datasource with Simple-JNDI?
I have a DataSource for testing purposes that I am building like this:
public DataSource dataSource() {
EmbeddedDatabase datasource = new EmbeddedDatabaseBuilder()
.setType(HSQL)
.setSeparator(";")
.addScript("classpath:/tables-definitions.sql")
.build();
return datasource;
}
And I want to bind this to a JNDI name with Simple-JNDI. Do you know how to do this?
Finally I found the answer how to use Simple-JNDI to bind to a jndi name a data source that you already have, in general for testing purposes.
Just an observation if you try to use SimpleNamingContextBuilder:
SimpleNamingContextBuilder is deprecated in spring 5.2 and above in favour of Simple-JNDI
Unfortunately I did not find a great source of documentation for Simple-JNDI which make things for more advanced stuff a bit cumbersome.
Also SimpleNamingContextBuilder does not work with JTA only with JPA - it does not cover all the naming needs for JTA
Now, how to bind a JNDI name with Simple-JNDI to a data source:
you need to set Context.INITIAL_CONTEXT_FACTORY to the factory class that you want to do the job for you, in my case I choose org.osjava.sj.memory.MemoryContextFactory - if you look into the location of this class in Simple-JNDI library, you will find more options there, depending of your needs
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.osjava.sj.memory.MemoryContextFactory");
Then you create a Hashtable and add all the properties you need to set as you set them through properties.
Hashtable env = new Hashtable();
env.put("org.osjava.sj.jndi.shared", "true");
Then you bind the DS to JNDI name as you would do it in old plain way:
create the initial context with the properties from the Hashtable
create the sub-context
then bind the data source to JNDI name/context:
InitialContext ic = new InitialContext(env);
ic.createSubcontext("java:/comp/env/jdbc");
ic.bind("java:/comp/env/jdbc/"+dataSourceJndiname, datasource);
Related
Spring Boot : How to add new Datasource at runtime
My project want to connect two datasource.
The first datasource I can Config in application.properties but the second datasource can't config because this config is in the tableConfig from DB of the first datasource.
So,
config the 1st datasource.
query data from the 1st datasource for get config of 2nd datasource (url, username, password).
add new 2nd datasource
Now, I config two Datasource from application.properties and it's work.
But the requirement want to change the 2nd datasource from table of 1st datasource. T.T
Please, gives me some suggestions.
Thank you.
A Spring configuration like this should work (consider it pseudo code):
#Bean("secondDatasource")
public Datasource secondDatasource(#Qualifier("firstDatasource") Datasource ds){
// use `ds` to obtain the necessary information to obtain a datasource ...
return DataSourceBuilder
.create()
.username(username)
.password(pwd)
.url(url)
.driverClassName(driver)
.build();
}
I would at least start without using Spring Data JPA in the configuration class and operate directly on the data source to keep things simple.
You already got pointers, how to set up Spring Data JPA to then use the different data sources: http://www.baeldung.com/spring-data-jpa-multiple-databases
The code above is mainly just copied from: https://stackoverflow.com/a/28822145
I have multitenant database in Spring Boot. I store multi spring JDBC templates (based on tomcat Data Sources, configured manually) in map (immutable bean). And I choose proper data source based on uuid in a request (connection pool per database). I have disabled standard configuration in Spring Boot by:
#SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
What is the proper way of transaction manager configuration? With single data source I can use PlatformTransactionManager, but how it should be done with multiple jdbc templates/data sources in spring? It would be the best if I could set everything dynamically. Thanks in advance.
Here a solution for using multiple datasources
http://www.baeldung.com/spring-data-jpa-multiple-databases
Configure Two DataSources
If you need to configure multiple data sources, you can apply the same tricks that are described in the previous section. You must, however, mark one of the DataSource #Primary as various auto-configurations down the road expect to be able to get one by type.
If you create your own DataSource, the auto-configuration will back off. In the example below, we provide the exact same features set than what the auto-configuration provides on the primary data source
#Bean
#Primary
#ConfigurationProperties("app.datasource.foo")
public DataSourceProperties fooDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
#ConfigurationProperties("app.datasource.foo")
public DataSource fooDataSource() {
return fooDataSourceProperties().initializeDataSourceBuilder().build();
}
#Bean
#ConfigurationProperties("app.datasource.bar")
public BasicDataSource barDataSource() {
return (BasicDataSource) DataSourceBuilder.create()
.type(BasicDataSource.class).build();
}
fooDataSourceProperties has to be flagged #Primary so that the database initializer feature uses your copy (should you use that).
app.datasource.foo.type=com.zaxxer.hikari.HikariDataSource
app.datasource.foo.maximum-pool-size=30
app.datasource.bar.url=jdbc:mysql://localhost/test
app.datasource.bar.username=dbuser
app.datasource.bar.password=dbpass
app.datasource.bar.max-total=30
So, after searching Google and Github for answers, I'm left confounded as to how most people know how to use HikariCP. I can't seem to find any straight up documentation on HikariCP.
My question is: how do I specify the host address without a JDBC URL? The main page of HikariCP on Github clearly says that JDBC URL specification is optional and instead to simply use HikariConfig#setDataSourceClassName(String). However, I'm confused then as to how I would specify my address and I can't seem to find the answer anywhere. Like with SQLite, where do I specify the path to where the database file should go?
This is the code I currently have:
final HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setPoolName("SQLite");
hikariConfig.setDataSourceClassName("org.sqlite.SQLiteDataSource");
HikariDataSource ds = new HikariDataSource(hikariConfig);
If I was to not use HikariCP I would simply specify the JDBC URL like such:
jdbc:sqlite:path/to/database.db. However, how do you do this without using a JDBC URL?
Thanks for any help.
When you use DataSource-style instead of URL-style configuration, all datasource properties translate to setters on the DataSource class. So, since you seem to be using the org.sqlite.SQLiteDataSource, the various setters on that class are what is relevant.
Here is an example of setting not only the URL of the DataSource, but also setting the Journal Mode and enabling full column name support.
final HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setPoolName("SQLite");
hikariConfig.setDataSourceClassName("org.sqlite.SQLiteDataSource");
hikariConfig.addDataSourceProperty("url", "jdbc:sqlite:C:/work/mydatabase.db");
hikariConfig.addDataSourceProperty("journalMode", "WAL");
hikariConfig.addDataSourceProperty("fullColumnNames", "true");
HikariDataSource ds = new HikariDataSource(hikariConfig);
This reason DataSource-style is preferable is two-fold:
The use of reflection ensures that any typo in a property name causes a failure. Whereas, properties with typos specified in the JDBC URL itself are typically ignored by drivers.
When there are a large number of properties specified, a JDBC URL connection string can get extremely long -- several hundred characters for some drivers -- which makes reading/understanding what properties are actually set extremely cumbersome.
I'm trying to load in a property file provided by JNDI which should be platform independent. I know I can do it in the following ways, dependent on the platform:
For Weblogic:
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
InitialContext context = new InitialContext(properties);
context.lookup(propertiesLocation);
For Tomcat:
Context context = new InitialContext();
Context envCtx = (Context) context.lookup("java:comp/env");
final Object lookup = envCtx.lookup(propertiesLocation);
The core problem is that in Tomcat the prefix java:comp/env/ is needed. Since Spring is able to load all this platform independently, I tried looking into the possibilities of Spring loading my JNDI resources.
I found out I can use the JndiTemplate of Spring in the following way:
JndiTemplate jndiTemplate = new JndiTemplate();
Object lookup = jndiTemplate.lookup(propertiesLocation);
This is still platform dependent however, needing to use java:comp/env as a prefix during the lookup on tomcat. Looking further on StackOverflow and in the Spring javadocs, I found the class JndiLocatorSupport, which has the following:
JNDI names may or may not include the "java:comp/env/" prefix expected by J2EE applications when accessing a locally mapped (ENC - Environmental Naming Context) resource. If it doesn't, the "java:comp/env/" prefix will be prepended if the "resourceRef" property is true (the default is false) and no other scheme (e.g. "java:") is given.
So I created a JndiObjectFactoryBean which extends JndiLocatorSupport, enabled setResourceRef but it doesn't seem to append the prefix.
Core problem:
When using the following code:
JndiObjectFactoryBean factoryBean = new JndiObjectFactoryBean();
factoryBean.setResourceRef(true);
Object lookup = factoryBean.getJndiTemplate().lookup(propertiesLocation);
I'd expect it to have the same effect as:
JndiObjectFactoryBean factoryBean = new JndiObjectFactoryBean();
Object lookup = factoryBean.getJndiTemplate().lookup("java:comp/env/" + propertiesLocation);
But it doesn't. It seems to have no effect at all. But if I look through the source code, it does go like this:
JndiObjectFactoryBean.lookup() -> JndiObjectLocator.lookup() -> JndiLocatorSupport.lookup(), which does call the right methods.
JndiObjectFactoryBean factoryBean = new JndiObjectFactoryBean();
factoryBean.setResourceRef(true);
Object lookup = factoryBean.getJndiTemplate().lookup(propertiesLocation);
and
JndiTemplate jndiTemplate = new JndiTemplate();
Object lookup = jndiTemplate.lookup(propertiesLocation);
Are the same with regards to the lookup. The first is only a very complex way to obtain a JndiTemplate. All the settings you do are for the JndiObjectFactoryBean NOT for the internal JndiTemplate. Basically your whole approach doesn't add anything.
Instead use a JndiLocatorDelegate and let that do the lookup (don't try to get the JndiTemplate!).
JndiLocatorDelegate jndi = JndiLocatorDelegate.createDefaultResourceRefLocator();
Object lookup = jndi.lookup(propertiesLocation);
This will by default do a lookup in java:comp/env and if not found do a fallback to a plain propertiesLocation (what you passed in).
I'm trying to follow Sun's JDBC tutorial at http://java.sun.com/docs/books/tutorial/jdbc/basics/connecting.html
It gives the following example code:
DataSource ds = (DataSource) org.apache.derby.jdbc.ClientDataSource()
ds.setPort(1527);
ds.setHost("localhost");
ds.setUser("APP")
ds.setPassword("APP");
Connection con = ds.getConnection();
This code doesn't compile because the DataSource interface has none of these methods, except for the getConnection() method invoked last.
(Here's the javadoc: http://java.sun.com/javase/6/docs/api/javax/sql/DataSource.html)
What am I missing?
Edit:
I'm actually trying to connect to MySQL (com.mysql.jdbc) and I can't find the javadoc for that. I'll accept an answer that points me to either:
1) documentation for com.mysql.jdbc regarding a DataSource that I can understand, or
2) gives an example to follow for what the tutorial's code should be, for any database.
One thing you might want to look at is the Commons DBCP project. It provides a BasicDataSource that is configured fairly similarly to your example. To use that you need the database vendor's JDBC JAR in your classpath and you have to specify the vendor's driver class name and the database URL in the proper format.
Edit:
If you want to configure a BasicDataSource for MySQL, you would do something like this:
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("username");
dataSource.setPassword("password");
dataSource.setUrl("jdbc:mysql://<host>:<port>/<database>");
dataSource.setMaxActive(10);
dataSource.setMaxIdle(5);
dataSource.setInitialSize(5);
dataSource.setValidationQuery("SELECT 1");
Code that needs a DataSource can then use that.
DataSource is vendor-specific, for MySql you could use MysqlDataSource which is provided in the MySql Java connector jar:
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setDatabaseName("xyz");
dataSource.setUser("xyz");
dataSource.setPassword("xyz");
dataSource.setServerName("xyz.yourdomain.com");
Basically in JDBC most of these properties are not configurable in the API like that, rather they depend on implementation. The way JDBC handles this is by allowing the connection URL to be different per vendor.
So what you do is register the driver so that the JDBC system can know what to do with the URL:
DriverManager.registerDriver((Driver) Class.forName("com.mysql.jdbc.Driver").newInstance());
Then you form the URL:
String url = "jdbc:mysql://[host][,failoverhost...][:port]/[database][?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]"
And finally, use it to get a connection:
Connection c = DriverManager.getConnection(url);
In more sophisticated JDBC, you get involved with connection pools and the like, and application servers often have their own way of registering drivers in JNDI and you look up a DataSource from there, and call getConnection on it.
In terms of what properties MySQL supports, see here.
EDIT: One more thought, technically just having a line of code which does Class.forName("com.mysql.jdbc.Driver") should be enough, as the class should have its own static initializer which registers a version, but sometimes a JDBC driver doesn't, so if you aren't sure, there is little harm in registering a second one, it just creates a duplicate object in memeory.
use MYSQL as Example:
1) use database connection pools: for Example: Apache Commons DBCP , also, you need basicDataSource jar package in your classpath
#Bean
public BasicDataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/gene");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
2)use JDBC-based Driver it is usually used if you don't consider connection pool:
#Bean
public DataSource dataSource(){
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/gene");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
I think the example is wrong - javax.sql.DataSource doesn't have these properties either. Your DataSource needs to be of the type org.apache.derby.jdbc.ClientDataSource, which should have those properties.
The javadoc for DataSource you refer to is of the wrong package. You should look at javax.sql.DataSource. As you can see this is an interface. The host and port name configuration depends on the implementation, i.e. the JDBC driver you are using.
I have not checked the Derby javadocs but I suppose the code should compile like this:
ClientDataSource ds = org.apache.derby.jdbc.ClientDataSource()
ds.setHost etc....
For postgres, the below works. I actually used it in integ tests. I guess there should be some more consideration for production usage.
PGSimpleDataSource ds = new PGSimpleDataSource() ;
ds.setServerName( "localhost" );
ds.setDatabaseName( "your_db_name_here" );
ds.setUser( "scott" );
ds.setPassword( "tiger" );
The class is bundled in the postgres jdbc driver.
The original stackoverflow post i followed: https://stackoverflow.com/a/45091982/3877642