I am working with Spring Boot and Dart. When I hit the URL (using POSTMEN/Browser) to insert some data in MySql I got the response correctly. But WHen I send the 3 requests consecutively from Flutter Front-end using Dart language it most of the time returned the result of 2 GET request and through the error for the 3rd request and most of the time it works for all request.
Following is the connection service that I am using on backend to store the data.
ConnectionService.java
#Service
public class ConnectionService {
private static final Logger log = LoggerFactory.getLogger(ConnectionService.class);
Connection connection = null;
#Value("${spring.datasource.url}")
String datasourceUrl = "jdbc:mysql://localhost/databaseName?autoReconnect=true&useSSL=false";
public Connection createConnection() throws SQLException {
connection = DriverManager.getConnection(datasourceUrl, "root", "root");
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
return connection;
}
public void closeConnection() {
try {
connection.close();
} catch (Exception e) {
log.error(e.toString());
}
}
}
I am creating the object of the Connection service class and call the createConnection() to create the connection and closeConnection() to close that one.
Controller.java
#RestController
#RequestMapping("/user")
public class UserController {
#Autowired
ConnectionService connectionService;
#GetMapping(path = "/test")
public void testFunction(#RequestParam(name = "abc") String abc) throws SQLException
{
Connection connection = connectionService.createConnection();
if (abc.isExist(param1,param2,connection))
{
//some code
connectionService.closeConnection();
} else
{
//some operation
connectionService.closeConnection();
}
}
Guide me to resolve this issue.
Thanks
They problem is that instead of closing the instance connection that I have created locally I am closing the main connectionService instance. This is the problem.
Now connectionService.closeConnection();
It should be connection.closeConnection();
Related
I'm trying to create a database connection library to be used in all my apps. I want to make sure that this library is fully unit tested and so i'm trying to use dependency injection.
I have this class which i want to ensure is tested:
public class ConnectionFactory {
private String dataSourceName;
public ConnectionFactory(String dataSourceName) {
if(dataSourceName == null) {
throw new NullPointerException("dataSourceName can't be null");
}
this.dataSourceName = dataSourceName;
}
public Connection getConnection() throws SQLException {
Connection connection = getDataSource(dataSourceName).getConnection();
if(connection != null) {
return connection;
}
...
}
// Get a datasource object
private DataSource getDataSource(String dataSourceName) {
...
try {
Context ctx = new InitialContext();
dataSource = (DataSource) ctx.lookup("java:comp/env/" + dataSourceName);
} catch (NamingException e) {
...
}
return dataSource;
}
}
I want to be able to simply call this class from all my apps with something as simple as this:
public class MyApp {
public static void main(string[] args) {
ConnectionFactory connectionFactory = new ConnectionFactory("jdbc/myDataSource");
Connection connection = connectionFactory.getConnection();
}
}
I've started writing unit tests for this ConnectionFactory, but quickly realized that with my current code I can't mock the DataSource object so it's trying to actually connect to a real data source.
#RunWith(Nested.class)
public class ConnectionFactoryTest {
public class Constructor {
#Test
public void shouldThrowNullPointerIfParamIsNull() {
assertThatExceptionOfType(NullPointerException.class)
.isThrownBy(() -> { new ConnectionFactory(null); })
.withMessage("dataSourceName can't be null");
}
}
public class GetConnection {
public class WithDataSourceAvailable {
#Test
public void shouldErrorIfParamIsNull() {
ConnectionFactory connectionFactory = new ConnectionFactory("jdbc/myDataSource"); // <-- This is going to fail b/c it's trying to actually fetch a real data source
}
}
}
}
How can I properly use Dependency Injection so that I can write unit tests that don't actually try to connect to a data source?
Take a look at Mockito I find it easy to use for this type of mocking.
I'm creating a JavaFX application, I've connected to the database fine. However when i look to get data from the tables i get the error
org.h2.jdbc.JdbcSQLException: Table "LECTURE" not found; SQL
statement: SELECT NAME FROM Lecture [42102-192]
and I'm 100% sure i'm connected to the database and the table is definitely there, any suggestions on why this is?
hear is my connection code and the code i am running just so you can see
public class ConnectionFactory {
//static reference to itself
private static ConnectionFactory instance = new ConnectionFactory();
public static final String URL = "jdbc:h2:file:~/db\\.";
public static final String USER = "notepad";
public static final String PASSWORD = "password";
public static final String DRIVER_CLASS = "org.h2.Driver";
//private constructor
private ConnectionFactory() {
try {
Class.forName(DRIVER_CLASS);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private Connection createConnection() {
Connection connection = null;
try {
connection = DriverManager.getConnection(URL, USER, PASSWORD);
} catch (SQLException e) {
System.out.println("ERROR: Unable to Connect to Database.");
}
return connection;
}
public static Connection getConnection() {
return instance.createConnection();
}
}
And the query being run
private void onLoadYearSelect() {
try {
Connection con = ConnectionFactory.getConnection();
Statement stat = con.createStatement();
String query = "SELECT NAME FROM Lecture";
ResultSet years = stat.executeQuery(query);
while(years.next()){
yearSelect.setValue(years.getString("NAME"));
System.out.println(years.getString("NAME"));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public void initialize(){
onLoadYearSelect();
}
If it says the table doesn't exist, then it really doesn't exist.
Most likely, you are not actually connecting to the correct database. In fact, by default, if the connection string points to a non-existent database, it just creates an empty database on the fly, which would explain your error.
It's probably too late now (because there is probably a 2nd database created already somewhere), but to avoid this confusion, it's not a bad idea to include IFEXISTS=TRUE in the connection string so that it fails if the database doesn't exist, rather than creating an empty one that will mask the true problem.
public static final String URL = "jdbc:h2:file:~/db\\.;IFEXISTS=TRUE";
However, one thing you can still try to debug the problem, is to add IFEXISTS=TRUE to the connection string. Then move or rename the database you think it should be connecting to so as to make the connection string invalid. Basically, force it to fail. If the code still connects to the database successfully, then you'll know the connection string is not pointing to the location you think it is.
I've implemented an RMI interface with these current files;
MyClient.java - Clientside code
MyServer.java - Serverside code
Adder.java - Interface
AdderRemote.java - Remote _implements Adder_
DataAccess.java - Contains all the methods to interveen between server and client
I have a vps which contains all files except the Client file in the directory
vps:~/rmi#
When testing this on it's own, ie: compiling on the server, doing
rmic AdderRemote
rmiregistry 5000&
(the port I have chosen)
java -classpath .:mysql-connector... MyServer
and then locally doing the same process but running the MyClient java instead, it works. The problem I am facing is now implementing this into a project I have running in eclipse as a part of this;
I have an instance of MyClient in the main file which is then passed as parameters to the certain classes (This project implements the MVC pattern and is passed to fellow model class'), and I am now getting the error
java.rmi.NotBoundException: xox
after googling, the only response I could find was "Attempt to look up a name that is not bound.", But I'm not really sure what this means? I'll attach my code, and any help would be much appreciated.
MyClient.java
public class MyClient
{
public Adder stub;
public MyClient ()
{
try
{
stub = (Adder)Naming.lookup("rmi://92.222.2.96:5000/xox");
}
catch (Exception e)
{
System.out.println(e.toString());
}
}
public static void connect(Adder stub) throws RemoteException
{
System.out.println(stub.connect());
}
Adder.java
public interface Adder extends Remote
{
public String connect() throws RemoteException;
}
AdderRemote.java
public class AdderRemote extends UnicastRemoteObject implements Adder
{
public AdderRemote() throws RemoteException
{
super();
da = new DataAccess();
}
DataAccess da;
public String connect() throws RemoteException
{
return da.connect();
}
DataAccess.java
public class DataAccess
{
// Connection info
static final String URL = "jdbc:mysql://92.222.2.96:3306/groupproj";
static final String DRIVER = "com.mysql.jdbc.Driver";
static final String USERNAME = "root";
static final String PASSWORD = "*****";
Connection c = null;
public String connect()
{
try
{
Class.forName(DRIVER).newInstance(); //Load DB driver
c = DriverManager.getConnection(URL, USERNAME, PASSWORD); //Establish connection to DB2
return "Connected.";
}
catch(Exception e)
{
return e.toString();
}
}
MyServer.java
public class MyServer
{
public static void main ( String args[] )
{
try
{
Adder stub = new AdderRemote();
Naming.rebind("rmi://92.222.2.96:5000/xox", stub);
}
catch ( Exception e )
{
System.out.println(e);
}
}
public static void connect(Adder stub) throws RemoteException
{
try
{
stub.connect();
}
catch(Exception e)
{
System.out.println("Could not connect to the DB.");
}
}
I gathered that because the files on the server are located in the directory "rmi" I renamed the xox to this, but this did not solve the problem, so I reverted it back to xox, which it worked before putting it into a java project.
Thank you
You must have got an exception doing the bind.
If you got a NotBoundException when looking up the same name in the same Registry you're supposed to have bound it to, you didn't bind it at all.
Notes:
You can only bind to a Registry that is running in the same host as yourself. For that reason it is convenient to always use "localhost" as the hostname when calling bind(), rebind(), or unbind().
You'd be better off letting the RemoteException and NotBoundException be thrown from the constructor of MyClient.
MyClient.connect() should not be static. In fact it cannot be static. Ergo this cannot be the real code.
From what we can see so far, your system isn't correctly designed. Your server should get a DBMS connection when it needs one, inside a remote method, on behalf of the client that is calling that method, and release it before exiting the method. Opening a new connection every time a client asks for one explicity and storing it into an instance variable of the remote object (a) will leak connections and (b) won't work when concurrent clients come to execute a query or update on the same connection.
I have a scenario where I have to connect to virtual center and get data. I have implemented a singleton class so that two threads cannot access the VC at the same time, as it has give concurrent access issue. My code is as follows:
public class Connector {
private static Connector instance ;
private Connector(String urlStr, String username, String password) {
connect(urlStr, username, password);
}
public static synchronized Connector getInstance(String urlStr, String username, String password) {
if (instance == null){
instance = new Connector(urlStr,username,password);
System.out.println("creating instance");
}
return instance ;
}
public void connect(String urlStr, String username, String password) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
//code to connect to VC
}
} catch (RuntimeException e) {
connectException = e;
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
public void disconnect() throws RuntimeFault, RemoteException {
//code for disconnect
}
}
}
I called this from another class in the following way:
Connector c = Connector.getInstance(dburl, dbuser, dbpass);
c.connect(dburl, dbuser, dbpass);
//code for getting data
c.disconnect();
Now if I have 2 simultaneous requests to get data from viruatal center, one of them fails saying "session is not authenticated".
Can you help we with a better way to deal with the issue.
And as the same instance is used always, how can we differentiate if its a different Virtual Center.
Your sequence
connect
do stuff
disconnect
is not atomic which means it is possible that
Thread 1 connects
Thread 2 connects
Thread 1 does stuff
Thread 1 disconnects
Thread 2 tries to do stuff and fails.
Structure your class so that clients cannot fail to connect, fail to disconnect, or interleave operations with another.
Your API could allow code that wants to use a connection to pass in an object that uses a connection while it is connected, and returns a result of using that connection:
public interface ConnectionTask<T> {
public T useConnection(Connection c);
}
and then instead of getInstance write a useConnection that does
public synchronized <T> T useConnection(ConnectTask<T> c) {
connect();
try {
return c.useConnection(this);
} finally {
disconnect();
}
}
and make connect and disconnect private so that clients cannot misuse them.
You are only retaining exclusive access to the connection in the first method.
I suggest you add a Lock like ReentrantLock which you lock in the first method and unlock when you release the connection.
Another approach is to use a visitor pattern which may be safer.
interface UsesConnector {
public void useConnector(Connector connector);
}
public class Connector {
private static final Connector connector = new Connector();
public static synchronized void useConnector(String urlStr, String username, String password, UsesConnector usesConnector) {
connector.connect(urlStr, username, password);
try {
usesConnector.useConnector(connector);
} finally {
connector.disconnect();
}
}
}
I was trying to write a separate database connection method in .java file which can be called upon by any servlet or jsp file needing the database connection. My code is
import java.sql.*;
import java.lang.*;
public class ConnectionClass {
private String username="root";
private String password="passwd";
/* Adjust the above two as per the username
* password combination of your MySql databse */
public Connection connect()
{
try
{
Class.forName("com.mysql.jdbc.Driver");
String url="jdbc:mysql://localhost/schooldatabase";
Connection con = DriverManager.getConnection(url,username,password);
return con;
}
catch(Exception e)
{
response.sendRedirect("studentserr.html");
out.println(e);
}
}
}
Now, the problem is that i'll be returning a Connection type so that all the servlets (which require database connection) may use it to execute various statements. However, in my code what should i return in the catch block ( which means that the connection to the database could not be established) ? Also, in case of a connection failure, I'm redirecting the user to the following page:
"studentserr.html"
This works fine if i use it in a servlet but not in .java class. What should i do for this ??
You should only be catching exceptions at exactly that moment where you can sensibly deal with them. You can't sensibly deal with them in the getConnection() method, so you should instead throw it so that the caller itself needs to deal with it.
Displaying an error page in case of a specific exception is however the responsibility of the servlet container itself. You normally configure the error page in web.xml as follows:
<error-page>
<exception-type>java.sql.SQLException</exception-type>
<location>/WEB-INF/errorpages/database.jsp</location>
</error-page>
You only need to change your code accordingly that you never catch the exception, or at least rethrow as ServletException where necessary.
Here's a minor rewrite:
public class Database {
private String url = "jdbc:mysql://localhost/schooldatabase";
private String username = "root";
private String password = "passwd";
static {
try {
Class.forName("com.mysql.jdbc.Driver"); // You don't need to load it on every single opened connection.
} catch (ClassNotFoundException) {
throw new ExceptionInInitializerError("MySQL JDBC driver missing in classpath", e);
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
}
And here's how you should use it in your DAO classes:
public List<Student> list() throws SQLException {
List<Student> students = new ArrayList<Student>();
Connection connection = null;
// ...
try {
connection = Database.getConnection();
// ...
} finally { // Note: no catch block!
// ...
if (connection != null) try { connection.close(); } catch (SQLException ignore) {}
}
return students;
}
And here is how you should use the DAO class in your servlet's doGet() or doPost().
try {
List<Student> students = studentDAO.list();
request.setAttribute("students", students);
request.getRequestDispatcher("/WEB-INF/students.jsp").forward(request, response);
} catch (SQLException e) {
throw new ServletException(e);
}
It has to be rethrown as ServletException simply because you can't add SQLException to the throws clause of any HttpServlet method. The servletcontainer will unwrap the SQLException while locating the error page.
Return a null value in case an exception is thrown. In case of getting a null value from your method, handle the exception and don't perform any database operation.
Also, remember to separate the database logic (connection, statement execution) from the business logic and the presentation. In your actual method, this code
response.sendRedirect("studentserr.html");
should never be in the database logic, because is presentation logic.
More info:
Java 101: Layered Architecture
How to avoid Java Code in JSP-Files?
Try moving class "ConnectionClass" into some package. and then call using this package in your java class where you require this (seems to be classpath issue in java file) or in jsp or servlet page.
Example
You need this in Demo.java as
pkg1.ConnectionClass obj = new pkg1.ConnectionClass ();
It is recommended to have database connection class as singleton. So that through out the application only a single instance of connection will be created and shared.