I'm working on a webapp where i manually create my DataSource. (also see my other question why: How to use Spring to manage connection to multiple databases) because I need to connect to other databases (dev, prod, qa, test).
Now I have solved it to choose and switch between databases. But if a user logs out of my app. He wants to try to connect to an other database. He is still connected to the same datasource because at runtime the myDs is not null. How can I properly dispose of this Datasource when user logs out? I don't want the user to create the datasource every time he queries the database.
private DataSource createDataSource(Environment e) {
OracleDataSource ds = null;
String url = null;
try {
if (myDs != null) {
logger.info("myDs connection: " + etmetaDs.getConnection().getMetaData().getURL());
url = myDs.getConnection().getMetaData().getURL();
}
} catch (SQLException exc) {
// TODO Auto-generated catch block
exc.printStackTrace();
}
if (myDs == null) {
try {
ds = new OracleDataSource();
} catch (SQLException ex) {
ex.printStackTrace();
}
ds.setDriverType("oracle.jdbc.OracleDriver");
ds.setURL(e.getUrl());
try {
Cryptographer c = new Cryptographer();
ds.setUser(c.decrypt(e.getUsername()));
ds.setPassword(c.decrypt(e.getPassword()));
} catch (CryptographyException ex) {
logger.error("Failed to connect to my environment [" + e.getName() + "]");
ex.printStackTrace();
return null;
}
logger.info("Connecting to my environment [" + e.getName() + "]");
myDs = ds;
} else if (url.equals(e.getUrl())) {
} else {
}
return myDs;
}
If you read the answer of Reza in you other question you can see how to create multiple DataSource.
I think here that the problem is not the DataSource but the way you store information in your code. I suppose that your etmetaDs is shared but all your users, so dispose it when a user log out (= set it to null) is not the good option.
What you have to do, is to maintain the status of the connection for each user. And when a user log off, you can reset is status in order to obtain a new connection the next time it connects.
Update: There are many way to achieve this. I give here an example of what I imagine, but you have to adapt it to your needs. Suppose that you have a UserData object that holds information :
public class UserData
{
String id;
String name;
String database;
}
You may have in your application a dropdown with the name of the database (dev, test, ...) with an empty first item. When the user selects a database, you get the connection with createDataSource(). If it already exists you returns the DataSource else you create a new one. When your user disconnect (or when the user log on), you set the database to "" to force him to select the database in the dropdown. There is no need to reset the datasource.
Related
So I'm using Java and MySQL, I created a database with different roles (user/manager/admin etc....) By default, I'm connected as a simple user, but how can I switch my user to the super admin after he authenticates?
Because right now, my connection with MySQL is made in a private constructor with private methods, so at first I thought about putting setters, but I can't access them given that I can't instantiate an object from my class with the private constructor.
ConnexionMySQL.java:
private ResourceBundle bundle =
ResourceBundle.getBundle("domaine.properties.config");
private String url = bundle.getString("sgbd.url");
private String driver = bundle.getString("sgbd.driver");
private String mysqlUser = bundle.getString("sgbd.login");
private String mysqlPassword = bundle.getString("sgbd.password");
private ConnexionMysql(){
try {
session = doSshTunnel(this.sshUser, this.sshPassword, this.sshHost, this.sshPort, this.url, this.sshLocalPort,this.sshRemotePort);
System.out.println("Opened SSH on " + sshHost);
Class.forName(driver);
connect = DriverManager.getConnection(url, mysqlUser ,mysqlPassword);
System.out.println("connect� Mysql");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public static Connection getInstance(){
if(instance == null){
instance = new ConnexionMysql();
System.out.println("INSTANCIATION DE LA CONNEXION SQL ! ");
}
else{
System.out.println("CONNEXION SQL EXISTANTE ! ");
}
return connect;
}
public static void disconnect(){
try {
connect.close();
session.disconnect();
instance = null;
System.out.println("Deconnexion r�ussie");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
I have another config.properties file in which I specified the login and password.
But what if now I wanna change those values in a controller for example after a user authenticates ?
I would say that the easiest way to solve this problems will be to make as many clases as roles you want to create (of course if you want to leave your constructor private).
In my opinion it should look something like this:
ConnexionMySQLuser;
ConnexionMySQLmanager;
ConnexionMySQLadmin;
Of course if you have numerous of roles it can create problems in future.
Im working on a spring project ran on a local environment using SpringToolSuite. I'm using Putty to create a tunel to access an app server from which i can query my MySQL database (i have to use SSH). So i'm running this simple code:
public static void getConnection() {
try {
connection = DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
DSLContext create = DSL.using(connection, SQLDialect.MYSQL);
Personne personne = Personne.PERSONNE.as("personne");
Evenement evenement = Evenement.EVENEMENT.as("evenement");
Genealogie genealogie = Genealogie.GENEALOGIE.as("genealogie");
Lieu lieu = Lieu.LIEU.as("lieu");
Result<Record3<Integer,Integer,String>> result = create
.select(DSL.countDistinct(personne.ID).as("countRs"),
evenement.IDGROUPE2.as("group2Rs"),
lieu.LIBELLE.as("libelleRs"))
.from(evenement.innerJoin(personne)
.on(personne.ID.eq(evenement.IDPERS))
.innerJoin(genealogie)
.on(genealogie.ID.eq(personne.IDGEN))
.innerJoin(lieu)
.on(lieu.ID.eq(evenement.IDGROUPE2)))
.where(personne._NOM.eq(" ")
.and((personne._PRENOM.eq(" ")
.or(personne._PRENOM.like(" -%"))))
.and(evenement.IDPERS.isNotNull())
.and(lieu.LIBELLE.isNotNull())
.and(genealogie.STATUS.ge(Byte.valueOf("1")))
.and(personne.CONTEMPORAIN.eq(Byte.valueOf("0"))))
.groupBy(evenement.IDGROUPE2)
.fetch();
System.out.println("countRs group2Rs libellesRs");
System.out.println("---------------------------");
for (Record r : result) {
System.out.println(r.get("countRs")+" "+r.get("group2Rs")+" "+r.get("libelleRs"));
}
try {
create.close();
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
As you can see it's basicly just opening the connection with the database, making a query and closing the connection/query, nothing special.
But when i check the client connections to my database using MySQL Workbench i can see that my code opened 10 connection:
And as you can see, only a single one of these connections is actualy executing the query.
Is there something i don't know about how JOOQ executes queries? Or mabe it is because i'm using Putty to access my remote server and it somehow creates many connections?
Since you're using HikariCP in your project, you should not create new connections manually using DriverManager:
// Don't do this
connection = DriverManager.getConnection(url, user, password);
DSLContext create = DSL.using(connection, SQLDialect.MYSQL);
Instead, use HikariCP as a DataSource and pass that to jOOQ:
// Do this
DSLContext create = DSL.using(dataSource, SQLDialect.MYSQL);
Now, you don't have to do any resource management anymore, because jOOQ / Hikari do this for you, behind the scenes (i.e. no close() calls)
I got to use MariaDB for my University Project.
it's my first time doing it, so I dont't know well how to use and code JDBC Driver and mariaDB.
Now I'm implementing the code in many places while looking at examples.
As I see, All the examples seems to creating Statement and making connection by using "DriverManager.getConnection"
Now I have a question.
I'm going to create a DBmanager Class that can connect, create tables, execute queries, and execute the code that updates data on tables in a single line.
I thought all the examples would run alone in one method and came from different places, so I could only try a new connection and create a code that would not close. But I have a gut feeling that this will be a problem.
Is there any way I can leave a connection connected at a single connection to send a command, and disconnect it to DB.disconnect()? And I'd appreciate it if you could tell me whether what I'm thinking is right or wrong.
The code below is the code I've written so far.
I am sorry if you find my English difficult to read or understand. I am Using translator, So, my English could not be display as I intended.
import java.sql.*;
import java.util.Properties;
public class DBManager {
/*********INNITIAL DEFINES********/
final static private String HOST="sumewhere.azure.com";//Azure DB URL
final static private String USER="id#somewhere";//root ID
final static private String PW="*****";//Server Password
final static private String DRIVER="org.mariadb.jdbc.Driver";//DB Driver info
private String database="user";
/***************API***************/
void setDB(String databaseinfo){
database=databaseinfo;
}
private void checkDriver() throws Exception
{
try
{
Class.forName("org.mariadb.jdbc.Driver");
}
catch (ClassNotFoundException e)
{
throw new ClassNotFoundException("MariaDB JDBC driver NOT detected in library path.", e);
}
System.out.println("MariaDB JDBC driver detected in library path.");
}
public void checkOnline(String databaseinfo) throws Exception
{
setDB(databaseinfo);
this.checkDriver();
Connection connection = null;
try
{
String url = String.format("jdbc:mariadb://%s/%s", HOST, database);
// Set connection properties.
Properties properties = new Properties();
properties.setProperty("user", USER);
properties.setProperty("password", PW);
properties.setProperty("useSSL", "true");
properties.setProperty("verifyServerCertificate", "true");
properties.setProperty("requireSSL", "false");
// get connection
connection = DriverManager.getConnection(url, properties);
}
catch (SQLException e)
{
throw new SQLException("Failed to create connection to database.", e);
}
if (connection != null)
{
System.out.println("Successfully created connection to database.");
}
else {
System.out.println("Failed to create connection to database.");
}
System.out.println("Execution finished.");
}
void makeCcnnection() throws ClassNotFoundException
{
// Check DB driver Exists
try
{
Class.forName("org.mariadb.jdbc");
}
catch (ClassNotFoundException e)
{
throw new ClassNotFoundException("MariaDB JDBC driver NOT detected in library path.", e);
}
System.out.println("MariaDB JDBC driver detected in library path.");
Connection connection = null;
}
public void updateTable(){}
public static void main(String[] args) throws Exception {
DBManager DB = new DBManager();
DB.checkOnline("DB");
}
}
For a studying project it's okay to give a connection from your DB Manager to client code and close it there automatically using try-with-resources construction.
Maybe you will find it possible to check Connection Pool tools and apply it further in your project or use as example (like HikariCP, here is a good introduction).
Read about Java try with resources. I think that this link could be usefull for your problem.
JDBC with try with resources
I am dealing with high traffic in my Spring Boot project and my goal is serving clients as much fast as possible. In this case, I have more than 500 requests per second. In each rest endpoint call, I should connect my schema and gather multiple information from multiple tables. To be able to do that, should I create new connection for each eendpoint call or create & close before each db query?
I wrote a JDBC connection class but I am not sure that it is a good way. Maybe you can give me some opinion.
JDBC Connection Class
#PropertySource({"classpath:application.properties"})
#Configuration
public class FraudJDBConfiguration {
private final Logger LOGGER = LogManager.getLogger(FraudJDBConfiguration.class);
private final Environment env;
#Autowired
public FraudJDBConfiguration(Environment env) {
this.env = env;
}
#Bean
public Connection getFraudConnection() {
// Step 1: Loading or
// registering Oracle JDBC driver class
String connectionClass = env.getProperty("fraud.db.driver-class-name");
try {
Class.forName(connectionClass);
} catch (ClassNotFoundException cnfex) {
LOGGER.error(cnfex.getMessage());
throw new RuntimeException("JDBC driver class'ı bulunamadı");
}
// Step 2: Opening database connection
try {
String environmentType = env.getProperty("environment");
if (environmentType == null) {
LOGGER.error("environment Tip Hatası (TEST - UAT - LIVE)");
throw new RuntimeException("environment Tip Hatası (TEST - UAT - LIVE)");
} else {
String connectionString = null;
String username = null;
String password = null;
switch (environmentType.toLowerCase()) {
case "dev":
connectionString = env.getProperty(/*someurl*/);
username = env.getProperty(/*someusername*/);
password = env.getProperty(/*somepassword*/);
break;
case "tst":
connectionString = env.getProperty(/*someurl*/);
username = env.getProperty(/*someusername*/);
password = env.getProperty(/*somepassword*/);
break;
case "liv":
connectionString = env.getProperty(/*someurl*/);
username = env.getProperty(/*someusername*/);
password = env.getProperty(/*somepassword*/);
break;
case "uat":
connectionString = env.getProperty(/*someurl*/);
username = env.getProperty(/*someusername*/);
password = env.getProperty(/*somepassword*/);
break;
}
// Step 2.A: Create and
// get connection using DriverManager class
if (connectionString == null) {
LOGGER.error("fraud şeması için connection string bulunamadı");
throw new RuntimeException("fraud şeması için connection string bulunamadı");
}
return DriverManager.getConnection(connectionString, username, password);
}
} catch (SQLException e) {
LOGGER.error(e.getMessage());
}
return null;
}
}
DAO
#Component
public interface FraudCommTransactionsDao {
Long count();
}
DAO IMPL
#Service
public class FraudCommTransactionsDaoImpl implements FraudCommTransactionsDao {
private final FraudJDBConfiguration fraudJDBConfiguration;
#Autowired
public FraudCommTransactionsDaoImpl(FraudJDBConfiguration fraudJDBConfiguration) {
this.fraudJDBConfiguration = fraudJDBConfiguration;
}
#Override
public Long count() {
try(Connection connection = fraudJDBConfiguration.getFraudConnection()) {
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(/*some query*/);
if (rs.next()) {
return rs.getLong("transaction_id");
} else {
return 0L;
}
} catch (SQLException ex) {
ex.printStackTrace();
}
return null;
}
}
No, establishing a new physical connection to a database server is costly. It involves multiple steps: user authorization, establishing session defaults, allocating memory on both client and server, etc. This overhead should not be added to every single request.
It's a common practice to create a connection pool to share the physical connections between application threads. This introduces a concept of logical connections e.g. a Connection object created with DriverManager.getConnection() is a physical connection while DataSource.getConnection() returns a logical connection which is a proxy.
There are multiple database connection pooling libraries for Java that you can use e.g. HikariCP. Don't write your own, this is not simple.
Get fast data and deliver to client could be possible using the simplest way of using application.properties file. You may use this to get database connection to your datasource.
I'm trying to create a simple plugin where I can paste in an sql query from our logs and have it display the column names and inserted values in a table. I've got it working, except for dates and times, because our DB2 database uses special values like {d: '2014-07-03'} rather than '2014-07-03'.
How do I figure out what values I need to pass to SQLQueryParserManagerProvider.getInstance().getParserManager(dbProduct,
dbVersion);
to get the right parser that can handle these values?
Set up a connection in the Database Development view (I called mine QA), double click to open a connection, then run:
IConnectionProfile profile = ProfileManager.getInstance().getProfileByName("QA");
Database db = getDatabase(profile);
if (db != null) {
System.out.println("DB Vendor: " + db.getVendor());
System.out.println("DB Version: " + db.getVersion());
}
getDatabase method was taken from the end of this page:
private Database getDatabase(IConnectionProfile profile) {
IManagedConnection managedConnection = ((IConnectionProfile) profile)
.getManagedConnection("org.eclipse.datatools.connectivity.sqm.core.connection.ConnectionInfo");
if (managedConnection != null) {
try {
ConnectionInfo connectionInfo = (ConnectionInfo) managedConnection.getConnection().getRawConnection();
if (connectionInfo != null) {
Database database = connectionInfo.getSharedDatabase();
return database;
}
}
catch (Exception e) {
e.printStackTrace();
}
}
return null;
}