I have been using a real simple H2 DB on a file. I had my setup like this:
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:"+dbFileName);
Statement stat = conn.createStatement();
and on application startup, I would simply do:
File dbFile = new File("~/mydb.db");
if(!dbFile.exists()) {
String sql = -create my table here, etc...
}
But I am now trying to do this in a "correct" Spring Boot way. So I have my application.properties file to contain this:
# H2
spring.h2.console.enabled=true
spring.h2.console.path=/h2
spring.datasource.url=jdbc:h2:file:~/mydb.db
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
And I am trying to use the JdbcTemplate / Dao way of doing things. But I need to check if the DB is there at startup. So I want to do my previous check in the Application classes event listener for ApplicationReadyEvent. But how do I get a reference to the datasource url? I had is a a configuration property before and was automatically loaded, and I could still do that, but it would be in to places and that would be bad.
So what's the essayist / correct way to ensure this DB file is there when the application starts up. (and I want this in a JDBC way, no JPA please)
You can use ApplicationListener then parse the spring.datasource.url value:
import java.io.File;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
#Component
public class MyApplicationListener implements ApplicationListener<ApplicationStartedEvent> {
#Value("${spring.datasource.url}")
private String databaseUrl;
#Override
public void onApplicationEvent(ApplicationStartedEvent event) {
System.out.println("Application started");
String path = databaseUrl.replace("jdbc:h2:file:", "");
System.out.println(path);
File dbFile = new File(path);
if (!dbFile.exists()) {
String sql = "etc";
}
}
}
Related
I have a ConnectionFactory class in order to use in my Jdbc Appender to save logs in a mysql database, but, I want to use properties in ConnectionFactory class to avoid leaving sensitive data exposed in the code, but, it is a singleton class, so its never get the values of my properties, the properties are always null. Can someone help me, is there any way to bring the application.properties properties into the ConnectionFactory?
I have this ConnectionFactory class:
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnection;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.springframework.beans.factory.annotation.Value;
public class ConnectionFactory {
private static interface Singleton {
final ConnectionFactory INSTANCE = new ConnectionFactory();
}
private final DataSource dataSource;
#Value("${spring.datasource.user}")
private String user;
#Value("${spring.datasource.password}")
private String password;
#Value("${spring.datasource.url}")
private String url;
private ConnectionFactory() {
Properties properties = new Properties();
properties.setProperty("user", user);
properties.setProperty("password", password); // or get properties from some configuration file
GenericObjectPool<PoolableConnection> pool = new GenericObjectPool<PoolableConnection>();
DriverManagerConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
url, properties
);
new PoolableConnectionFactory(
connectionFactory, pool, null, "SELECT 1", 3, false, false, Connection.TRANSACTION_READ_COMMITTED
);
this.dataSource = new PoolingDataSource(pool);
}
public static Connection getDatabaseConnection() throws SQLException {
return Singleton.INSTANCE.dataSource.getConnection();
}
And, my application.properties:
spring.cloud.compatibility-verifier.enabled=false
# database configs
spring.datasource.url=jdbc:mysql://localhost:3306/myDatabase
spring.datasource.username=root
spring.datasource.password=
spring.datasource.testWhileIdle=true
spring.datasource.validationQuery=SELECT 1
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
Is there any way to bring the properties into the ConnectionFactory class?
I found a response, you can add the environment variables in a singleton class using this code:
System.getenv("MY_ENV_VARIABLE");
This way, you get the environment variables.
If you really need to get the properties of applications.properties, you need to create a Properties properties variable, and then you can use this code:
try (InputStream input = YourSingletonClass.class.getClassLoader().getResourceAsStream("application.properties")) {
properties.load(input);
} catch (IOException e) {
e.printStackTrace();
}
To get the value of any propertie, use properties.getProperty(key); where the 'key' is the name of your propertie.
I tried to testing my Dao class but it return this error for the class DbConnection:
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or in an application resource file: java.naming.factory.initial
at java.naming/javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:691)
at java.naming/javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:305)
at java.naming/javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:342)
at java.naming/javax.naming.InitialContext.lookup(InitialContext.java:409)
at model.DbConnection.(DbConnection.java:16)
at model.DbConnection.getInstance(DbConnection.java:30)
at model.ProfileManager.ReturnPatientByKey(ProfileManager.java:27)
at model.ProfileManagerTest.testReturnPatientByKey(ProfileManagerTest.java:32)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
....
my DbConnection class:
package model;
import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
public class DbConnection {
private DbConnection() {
Context ctx;
try {
ctx = new InitialContext();
ds = (DataSource)ctx.lookup("java:comp/env/jdbc/CheckUpDb");
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public Connection getConnection() throws SQLException {
return ds.getConnection();
}
public static DbConnection getInstance () {
if(db==null) {
return new DbConnection();
}
else {
return db;
}
}
private static DataSource ds;
private static DbConnection db;
}
Database connection works in the web application, only the testing return this error.
I don't think the problem si my class ProfileManager because it is only a testing example:
import static org.junit.Assert.assertTrue;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
import model.Bean.PatientBean;
class ProfileManagerTest{
private ProfileManager pm;
String fiscal_code;
String user_password;
PatientBean patient;
#BeforeEach
void setUp() throws Exception {
this.pm = new ProfileManager();
fiscal_code = "aaa";
user_password = "bbb";
patient = mock(PatientBean.class);
}
#AfterEach
void tearDown() throws Exception {
}
#Test
void testReturnPatientByKey() {
patient = (PatientBean) pm.ReturnPatientByKey(fiscal_code, user_password);
assertTrue(true);
}
}
Database connection works in the web application, only the testing return this error.
That's most likely because you have the datasource declared in the server configuration and the server is providing your web app with one, but you don't have the same done in your test.
Do a search in your server files an you will probably discover something like this, or similar, depending on what server you use:
<Resource name="jdbc/CheckUpDb"
driverClassName="..."
type="..."
url="..."
username="..."
password="..."
/>
This is a way to configure a datasource using JNDI. When your web application runs, the server will provide you with this resource by name. This is what ctx.lookup("java:comp/env/jdbc/CheckUpDb"); does. It asks the server to give it that resource.
But when you run your unit tests, you run outside the server environment. That means that any resource you defined in the server (using context.xml for example) doesn't exist when you run your tests. In your tests you have to provide a datasource and make it available to your JNDI context so that this line of code then works:
ds = (DataSource)ctx.lookup("java:comp/env/jdbc/CheckUpDb");
The following post should give you the necessary details to set up your JNDI data source for your test: Setting up JNDI Datasource in jUnit
You will see that the examples there make use of a library called Simple-Jndi that they use to provide a JNDI context and configure it to include the datasource that the tests then try to retrieve by name. You can use any JNDI provider, but you must set up the datasource for your test yourself (inside #BeforeEach or #BeforeAll) because when running unit tests, you don't have the tomcat server doing this for you.
I need to use .properties file in Java to store database information.
Here is my database connector class. It's giving NullPointerException. What is the issue with my code ?
Note, that I haven't' assign those property file values. DB connection values are still hard coded.
import java.io.IOException;
import java.io.InputStream;
import java.sql.Statement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
public final class Database {
public Connection connection;
private Statement statement;
private Properties property;
public static Database database;
private Database() {
String url = "jdbc:mysql://localhost:3306/";
String dbName = "edus";
String driver = "com.mysql.jdbc.Driver";
String userName = "root";
String password = "";
try {
InputStream is = Database.class.getClassLoader().getResourceAsStream(
"config.properties");
property.load(is);
System.out.println(property.getProperty("db_user"));
System.out.println(property.getProperty("db_password"));
System.out.println(property.getProperty("db_name"));
Class.forName(driver).newInstance();
this.connection = (Connection) DriverManager.getConnection(url + dbName,
userName, password);
}catch (IOException ex) {
Logger.getLogger(Database.class.getName()).log(Level.SEVERE, null, ex);
} catch (ClassNotFoundException e) {
System.out.println("JDBC driver is missing");
} catch (InstantiationException | IllegalAccessException | SQLException e) {
e.printStackTrace();
}
}
public static synchronized Database getDatabaseConnection() {
if (database == null) {
database = new Database();
}
return database;
}
}
config.properties is not lying under classpath. It should be under classes folder.
you can also try
Database.class.getClassLoader().getResourceAsStream(
"com/lk/apiit/eduservice/config.properties");
As Roman C pointed out you also need to initialize Properties Object first
Properties property = new Properties();
You forgot to initialize
Properties property = new Properties();
This is an issue of NullPointerException in your code, because you referenced not initialized variable.
If you open a stream you should close it after it's not used. Do it by adding finally block.
The code where you getting a connection to the database you can move to the corresponding method. If the connection is closed you will not reinitialize the database again just reopen a connection or get a new one.
Dont keep config properties file in a package. Keep it directly inside the source folder, so that the config properties file comes directly in the build/classes folder after the build is done.
The issue is that your config properties in in the folder com/ik/apiit/eduservice folder but your code is expecting it to be directly in the classes folder (the root folder of classpath).
try this
FileInputStream in = new FileInputStream(System.getProperty("WEB-INF/dbConnection.properties"));
prop.load(in);
I have a requirement where i need to insert data and retrieve the same during my scheduling process.Though i can create my own connection class and can do the work but i am wondering is there a way to obtain a data base connection using Quartz API.
Since Quartz is efficiently doing data base connection and handling so my intention was to use a well defined structure in stead of creating my own.
I saw the following code in the Quartz
conn = DBConnectionManager.getInstance().getConnection(
getDataSource());
but i am not sure how good this approach is to obtain the connection.Or is there any good example/resource to create an efficient database connection class.
Quartz Property File
org.quartz.scheduler.instanceName=QuartzScheduler
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.threadPool.threadCount=7
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.MSSQLDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = myDS
org.quartz.dataSource.myDS.driver=com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL=jdbc:mysql://localhost:3306/quartz
org.quartz.dataSource.myDS.user=root
org.quartz.dataSource.myDS.password=root
org.quartz.dataSource.myDS.maxConnections=5
You can get connection utilizing Quartz by naming the data-source you have defined in your property file like
conn = DBConnectionManager.getInstance().getConnection("myDS");
here myDS is the name of the data source you have defined in your property file
but since you are using the underlying data pool of quartz make sure that you close the connection so that it should get back to the pool.
This is just an outline based on my knowledge of Quartz and how it get connection.
If you want to get DataSource:
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.quartz.SchedulerException;
import org.quartz.utils.PoolingConnectionProvider;
import org.quartz.utils.PropertiesParser;
/**
* This class just exposes the underlying data source backed by C3PO
* (http://www.mchange.com/projects/c3p0/index.html).
*/
class MyDataSource extends PoolingConnectionProvider {
public MyDataSource(Properties config) throws SchedulerException, SQLException {
super(config);
}
public DataSource dataSource() {
return getDataSource();
}
}
/** This class exposes the data store configured in quartz.properties. */
public class MyDataSourceLoader {
private static final String DATA_SOURCE_CONFIG = "quartz.properties";
private static final String DATA_SOURCE_PREFIX = "org.quartz.dataSource.myDS";
private static final DataSource dataSource;
static {
try {
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(DATA_SOURCE_CONFIG);
Properties quartzConfig = new Properties();
quartzConfig.load(in);
in.close();
PropertiesParser pp = new PropertiesParser(quartzConfig);
Properties dataSourceConfig = pp.getPropertyGroup(DATA_SOURCE_PREFIX, true);
MyDataSource mds = new MyDataSource(dataSourceConfig);
dataSource = mds.dataSource();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static DataSource dataSource() {
return dataSource;
}
}
Currently I have one application in which I am able to access .mdb or .accdb
file with JdbcOdbcDriver to append some data.
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
con = DriverManager.getConnection("jdbc:odbc:MsAccessDSN");
but in this, I need to configure System DSN. We need to add new Data Source (Microsoft Access Driver) and then need to provide location of .mdb file. Only then above code will work.
Suppose I want to run my application on other system then I need to do same thing to that computer.
If I give my application to the client and he/she don't know how to configure .mdb file. Then my whole effort will waste.
So any driver is available by which I create .mdb file by my Java Code and then append all the data into the table of .mdb file.
Or is there any other way, where Java code can create .mdb file and able to access this database file.
I tried this code which append data without configuring System DNS:
public class TestMsAccess {
private static Connection con;
private static Statement stm;
private static String tableName = "EmpDetail";
private static int id_is = 2;
private static String name_is = "Employee1";
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
con = DriverManager.getConnection("jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ=D:\\MSAccessProject/Employee.mdb", "", "");
stm = con.createStatement();
// enter value into table
String addRow = "INSERT INTO " + tableName + " VALUES ( "
+ id_is + ", '"
+ name_is + "')";
stm.execute(addRow);
if (con != null) { con.close(); }
if (stm != null) { stm.close(); }
}
}
But the problem is, this code not create .mdb file automatically but work when I create .mbd file and table before running this code.
Update for Jackcess 2.x: Databases are now created (or opened) using DatabaseBuilder, so to create a new database file we do
import java.io.File;
import java.io.IOException;
import com.healthmarketscience.jackcess.Database;
import com.healthmarketscience.jackcess.Database.FileFormat;
import com.healthmarketscience.jackcess.DatabaseBuilder;
public class JackcessDemoMain {
public static void main(String[] args) {
String dbPath = "C:/Users/Public/newDb.accdb";
// using try-with-resources is recommended to ensure that
// the Database object will be closed properly
try (Database db = DatabaseBuilder.create(FileFormat.V2010, new File(dbPath))) {
System.out.println("The database file has been created.");
} catch (IOException ioe) {
ioe.printStackTrace(System.err);
}
}
}
Original answer for Jackcess 1.x (deprecated):
If you would like to create the “.mdb” file through java, you can use the Jackcess Java library which is one of the pure Java Library for reading from and writing to MS Access databases. Currently supporting versions include 2000-2007 I guess. Please have a look at the below example for better understanding:
Download Jackcess Java library (jackcess-1.2.6.jar)
from http://jackcess.sourceforge.net/
and commons-logging-1.1.jar
from http://commons.apache.org/logging/download_logging.cgi
and commons-lang-2.0.jar
from http://www.findjar.com/index.x?query=commons-lang
Add both jars to your classpath.
Try the below code to create a database automatically:
package com.jackcess.lib;
import com.healthmarketscience.jackcess.ColumnBuilder;
import com.healthmarketscience.jackcess.Database;
import com.healthmarketscience.jackcess.Table;
import com.healthmarketscience.jackcess.TableBuilder;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Types;
/**
*
* #author sarath_ivan
*/
public class JackcessLibrary {
private static Database createDatabase(String databaseName) throws IOException {
return Database.create(new File(databaseName));
}
private static TableBuilder createTable(String tableName) {
return new TableBuilder(tableName);
}
public static void addColumn(Database database, TableBuilder tableName, String columnName, Types sqlType) throws SQLException, IOException {
tableName.addColumn(new ColumnBuilder(columnName).setSQLType(Types.INTEGER).toColumn()).toTable(database);
}
public static void startDatabaseProcess() throws IOException, SQLException {
String databaseName = "C:/Users/compaq/Desktop/employeedb.mdb"; // Creating an MS Access database
Database database = createDatabase(databaseName);
String tableName = "Employee"; // Creating table
Table table = createTable(tableName)
.addColumn(new ColumnBuilder("Emp_Id").setSQLType(Types.INTEGER).toColumn())
.addColumn(new ColumnBuilder("Emp_Name").setSQLType(Types.VARCHAR).toColumn())
.addColumn(new ColumnBuilder("Emp_Employer").setSQLType(Types.VARCHAR).toColumn())
.toTable(database);
table.addRow(122875, "Sarath Kumar Sivan","Infosys Limited.");//Inserting values into the table
}
public static void main(String[] args) throws IOException, SQLException {
JackcessLibrary.startDatabaseProcess();
}
}
Now that the JDBC-ODBC Bridge has been removed from Java (as of Java 8), future readers might be interested in UCanAccess, a free and open-source pure Java JDBC driver for Access databases. UCanAccess includes a newdatabaseversion connection parameter that will create the Access .accdb or .mdb file if it does not already exist.
Sample code:
String dbFileSpec = "C:/Users/Gord/Desktop/myDb.accdb";
try (Connection conn = DriverManager.getConnection(
"jdbc:ucanaccess://" + dbFileSpec +
";newdatabaseversion=V2010")) {
DatabaseMetaData dmd = conn.getMetaData();
try (ResultSet rs = dmd.getTables(null, null, "Clients", new String[] { "TABLE" })) {
if (rs.next()) {
System.out.println("Table [Clients] already exists.");
} else {
System.out.println("Table [Clients] does not exist.");
try (Statement s = conn.createStatement()) {
s.executeUpdate("CREATE TABLE Clients (ID COUNTER PRIMARY KEY, LastName TEXT(100))");
System.out.println("Table [Clients] created.");
}
}
}
conn.close();
}
For details on how to set up UCanAccess see
Manipulating an Access database from Java without ODBC
download jackcess library
use this library whcih will create .mdb file.Bellow is the code snipet
download jackcess libraries from above location. add required jar file in class path.
`
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Types;
import com.healthmarketscience.jackcess.ColumnBuilder;
import com.healthmarketscience.jackcess.Database;
import com.healthmarketscience.jackcess.DatabaseBuilder;
import com.healthmarketscience.jackcess.Table;
import com.healthmarketscience.jackcess.TableBuilder;
public class MDBFileGenerator {
public static void main(String[] args) throws IOException, SQLException {
Database db = DatabaseBuilder.create(Database.FileFormat.V2000,
new File("new.mdb"));
Table newTable = new TableBuilder("NewTable")
.addColumn(new ColumnBuilder("a").setSQLType(Types.INTEGER))
.addColumn(new ColumnBuilder("b").setSQLType(Types.VARCHAR))
.toTable(db);
newTable.addRow(1, "foo");
}
}
`
You can use the below method instead of configuring System DSN in your machine.
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connection = DriverManager.getConnection("jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ=C:/Users/Desktop/your-database-file.mdb", "", "");
Here "your-database-file.mdb" is your MS-Access file. You can give the full path of your database file in your code to establish the connection. You can also keep the database file in your project(application) folder. In this case you will be able to give your database file along with the application to the client and he/she can use your application without anymore DSN configuration.
Hope this serves your purpose!
Thanks you!
It looks like there is at least one option for connecting directly to .mdb without the JdbcOdbcDriver, that that option is commercial. See here. If the setup is what you're trying to avoid, have you considered using something an embedded database like sqlite?