I am building a simple web service in Java using Jersey to add and delete users from a DB.
Where is the best way to do the preprocessing for this, e.g. if i want to establish a connection with the DB?
#Path("/user/service")
public class UserService
{
private Connection connect = null;
final private String host = "localhost";
final private String user = "qwerty";
final private String passwd = "mysql";
final private String database = "user_db";
public void connectToDB() throws Exception
{
Class.forName("com.mysql.jdbc.Driver");
connect = DriverManager.getConnection("jdbc:mysql://" + host + "/"
+ database + "?" + "user=" + user + "&password=" + passwd);
}
#PUT
#Path("/create")
public void createUser(){
System.out.println("Inside Create User method");
}
#GET
#Path("/get/{id}")
public String getUser(#PathParam("id")String userid, #QueryParam("first")String first){
System.out.println("GET: " + first);
}
}
I want to call connectToDB() once at the start and not inside every request.
Thanks
You can create singleton class and move your connectToDB logic into that singleton class and when invoked that singleton class will make a DB connection which can be used in all subsequent calls from the API.
You can also create a connection inside a static block but that's not a clean way imo.
More on singleton
Related
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 using the DataSourceFactory of dropwizard and H2 to create an in-memory db for testing. Here is what I have
private static final String DBNAME = String.format("JDBITest-%d", System.currentTimeMillis());
protected final DataSourceFactory config = new DataSourceFactory();
{
final String url = String.format("jdbc:h2:mem:%s;", DBNAME) +
"MODE=MySQL;" +
"TRACE_LEVEL_FILE=3;" +
"DB_CLOSE_DELAY=-1;" +
"IGNORECASE=TRUE";
System.out.println("Creating in memory H2 using " + url);
BootstrapLogging.bootstrap();
config.setUrl(url);
config.setUser("sa");
config.setDriverClass("org.h2.Driver");
config.setValidationQuery("SELECT 1");
}
#Before
public void setUp() throws Exception {
Server server = Server.createTcpServer().start(); // (4)
System.out.println("Server started and connection is open.");
System.out.println("URL: jdbc:h2:" + server.getURL() + "/mem:" + DBNAME);
}
When this runs I see
Creating in memory H2 using jdbc:h2:mem:JDBITest-1541641621470;MODE=MySQL;TRACE_LEVEL_FILE=3;DB_CLOSE_DELAY=-1;IGNORECASE=TRUE
Server started and connection is open.
URL: jdbc:h2:tcp://0.0.17.56:9092/mem:JDBITest-1541641621470
Why is the TCP 0.0.17.56? I cannot access this nor can I use the H2.jar to access the shell.
There are several wrong things here.
H2 has different connections modes. In your example you configure DataSourceFactory for in memory connection, but then in #Before method you create new instance of H2 with tcp based connection. More about connection modes here.
So now you have basically 2 instances of H2, in memory and tcp and they are completely unrelated. So you probably need to have only 1 connection type configured for you tests.
If you want to connect to your H2 db outside of JVM (from browser for example) - then you need to have tcp-based connection.
To be able to connect to db from browser you also need to run console application. Which should be inside h2.jar with command like that java -jar h2*.jar. More about this here.
And finally, this peace of code should suitable for you (with in-memory connection):
private static final String DBNAME = String.format("JDBITest-%d", System.currentTimeMillis());
private ManagedDataSource dataSource;
#Before
public void setUp() {
System.out.println("Server started and connection is open.");
final String url = String.format("jdbc:h2:mem:%s;", DBNAME) +
"MODE=MySQL;" +
"TRACE_LEVEL_FILE=3;" +
"DB_CLOSE_DELAY=-1;" +
"IGNORECASE=TRUE";
System.out.println("Creating in memory H2 using " + url);
DataSourceFactory config = new DataSourceFactory();
BootstrapLogging.bootstrap();
config.setUrl(url);
config.setUser("sa");
config.setDriverClass("org.h2.Driver");
config.setValidationQuery("SELECT 1");
dataSource = config.build(null, "test");
}
#Test
public void test() throws SQLException {
Connection connection = dataSource.getConnection();
connection.createStatement().executeUpdate("CREATE TABLE TEST (`id` INT)");
connection.createStatement().executeUpdate("INSERT INTO TEST (`id`) VALUES (1)");
ResultSet resultSet1 = connection.createStatement().executeQuery("SELECT * FROM TEST WHERE `id` = 1");
resultSet1.next();
resultSet1.getInt(1);
System.out.println("Found ID: " + resultSet1.getInt(1));
}
Out:
Server started and connection is open.
Creating in memory H2 using jdbc:h2:mem:JDBITest-1541649996267;MODE=MySQL;TRACE_LEVEL_FILE=3;DB_CLOSE_DELAY=-1;IGNORECASE=TRUE
Found ID: 1
I am developing a web project with HSQLDB persistence. My database instance is on server mode, therefore, I need to run a cmd script/ Java method to access my schema.
Tomcat is the container I use to drop my war on port 8080. Gradle is my build system.
Currently I am using the following main method before I deploy my app to properly access my database on runtime:
public static void main(String[] args) throws IOException, ServerAcl.AclFormatException {
final String URL = "file:~/db/cursago";
String user = "user";
String password = "password";
HsqlProperties p = new HsqlProperties();
p.setProperty("server.database.0",URL+";user="+user+";password="+password);
p.setProperty("server.dbname.0","cursago");
Server server = new Server();
server.setProperties(p);
server.setLogWriter(null);
server.setErrWriter(null);
server.start();
System.out.println("Database is running with path: " + URL);
System.out.println("Username: " + user+", Password: " + password);
}
I would like to know if there's a way of making Tomcat/Gradle/IntelliJ IDEA run this main method before a project deploy, instead of running this script by hand.
from here:
you could run the content of your main in the onApplicationEvent like:
public class ApplicationListenerBean implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
final String URL = "file:~/db/cursago";
String user = "user";
String password = "password";
HsqlProperties p = new HsqlProperties();
p.setProperty("server.database.0",URL+";user="+user+";password="+password);
p.setProperty("server.dbname.0","cursago");
Server server = new Server();
server.setProperties(p);
server.setLogWriter(null);
server.setErrWriter(null);
server.start();
System.out.println("Database is running with path: " + URL);
System.out.println("Username: " + user+", Password: " + password);
}
}
This will trigger on every event, that could be application started or redeployed and happens before your application handles requests.
You also can wire in your properties.
There are some difficulties if the server is already running and it can't run in this scope only.
The error is that I cant open the connection to mysql database, it must be an error in parameters but I am confused , I have no idea where is the problem.
First you need to create a MySQL schema. Secondly, use JDBC to connect to your recently created database (via localhost - make sure you get the user/password right).
After that you should use DAO-like classes. I'll leave here a Connect class:
public class Connect {
private static final String USERNAME = "root";
private static final String PASSWORD = "12345";
private static final String URL = "localhost";
private static final String SCHEMA = "new_schema";
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection connect() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://"+URL+"/"+SCHEMA+"?user="+USERNAME+"&password="+PASSWORD);
}
}
After you have the Connect class, you should connect to the database using Connection c = Connect.connect(). Here's a class that implements it.
public static List<Album> list() throws SQLException {
Connection c = Connect.connect();
ResultSet rs = c.createStatement().executeQuery("SELECT * FROM Albums");
List<Album> list = new ArrayList<>();
while (rs.next()) {
String name = rs.getString("nome"); // first table column (can also use 1)
String artist = rs.getString("artista"); // second table column (can also use 2)
Album a = new Album(name, artist);
list.add(a);
}
return list;
}
It should also give you an insight as to how you should use SQL commands.
If you'd like a more in-depth help you should post the code you used, otherwise it's difficult to give you a more "to-the-point" explanation.
JDBC URLs can be confusing. Suggest you try using a SQL tool that understands the JDBC protocol (such as the database development perspective in Eclipse) to validate the URL and make sure you can connect to the database before you start coding. Cutting and pasting a URL known to work into your code can avoid many problems.
I have created a java login application with mysql database and deployed it on OpenShift.
URL: http://passwordbucket-king003.rhcloud.com/SimpleLoginTest/
while trying to running its simply showing a blank page with URL-http://passwordbucket-king003.rhcloud.com/SimpleLoginTest/loginservlet .But ideally it should show the home page. Here is my database connection file, please let me know that where I wrong?
package database;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DatabaseConnection
{
// private final String host = "jdbc:mysql://localhost:3306/logindemo";
// private final String username = "root";
// private final String password = "root";
private final String OPENSHIFT_MYSQL_DB_PORT = "3306";
private final String OPENSHIFT_MYSQL_DB_HOST= "127.3.110.129";
private final String OPENSHIFT_MYSQL_DB_PASSWORD= "root";
private final String OPENSHIFT_MYSQL_DB_USERNAME= "root";
private final String OPENSHIFT_MYSQL_DB_URL= "jdbc:mysql://${OPENSHIFT_MYSQL_DB_HOST}:${OPENSHIFT_MYSQL_DB_PORT}/logindemo";
public Connection con = null;
public Connection openConnection()
{
try
{
Class.forName("com.mysql.jdbc.Driver");
// con = DriverManager.getConnection(host, username, password);
con = DriverManager.getConnection(OPENSHIFT_MYSQL_DB_URL);
}
catch(ClassNotFoundException ex)
{
System.out.println(ex);
}
catch (SQLException ex)
{
System.out.println(getClass().getClass()+" = " +ex.toString());
}
return con;
}
public void closeConnection() throws SQLException
{
if(!con.isClosed()){
con.close();
}
}
}
First of all, on OpenShift your application should be deployed as ROOT.war and there should be content at the "/" context.
Second, if you want to read and use environment variables, you should use System.getenv(). For example:
private final String OPENSHIFT_MYSQL_DB_HOST = System.getenv("OPENSHIFT_MYSQL_DB_HOST);
Your connection URL should also contain the credentials given to you when you created the database.
The database name is probably "passwordbucket" the same as your application name.
In stead of using a jdbc connection you should consider using the JNDI datasource that comes preconfigured on OpenShift. Please check the documentation. You disd not specify the container type you are using.