I built a _VERY_ rudimentary utility class in Java to handle database operations (connection retrieval, inserts, etc.) like this:
// define the package name
package com.foo.bar.helpers;
// import all needed resources
import com.foo.bar.helpers.database.MySQL;
import com.foo.bar.helpers.database.SQLite;
import java.lang.reflect.Method;
import java.sql.Array;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
/**
* database
* @author John Doe <...>
*/
class Database {
// private constructor to prevent instantiation
private Database() throws InstantiationException {
// throw the appropriate exception
throw new InstantiationException();
}
// database classes
public static final String SQLITE_CLASS = SQLite.class.getCanonicalName();
public static final String MYSQL_CLASS = MySQL.class.getCanonicalName();
/**
* returns a connection to the database using a set of parameters
* @param parameters the connection parameters
* @return a connection to the database
* @author John Doe <...>
*/
public static Connection getConnection(Object... parameters) {
Connection output = null;
try {
if (parameters.length > 0) {
// create an instance of the target class
Class<?> target_class = Class.forName(parameters[0].getClass().getCanonicalName());
// remove the first parameter (database class)
Object[] class_parameters = Arrays.copyOfRange(parameters, 1, parameters.length);
// retrieve the class type for each parameter
Class<?>[] class_types = new Class[class_parameters.length];
for (int i = 0; i < class_parameters.length; i++) {
class_types[i] = class_parameters[i].getClass();
}
// reflect the target class method
Method class_method = target_class.getDeclaredMethod("getConnection", class_types);
// output the database connection
output = (Connection) class_method.invoke(null, class_parameters);
} else {
throw new Throwable("unable to establish a connection with the database (no parameters were provided)");
}
} catch (Throwable e) {
// print the stack trace
e.printStackTrace();
}
return output;
}
}
Apart from the database helper, I have two database connectors (MySQL and SQLite) like this (showing the MySQL connector):
// define the package name
package com.foo.bar.helpers.database;
// import all needed resources
import com.foo.bar.helpers.Configuration;
import com.foo.bar.helpers.Log;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.HashMap;
/**
* MySQL
* @author John Doe <....>
*/
public class MySQL {
// private constructor to prevent instantiation
private MySQL() throws InstantiationException {
// throw the appropriate exception
throw new InstantiationException();
}
// connection key
public static final String CONNECTION_KEY = "mysql";
// default connection profile
public static final String DEFAULT_CONNECTION_PROFILE = "default";
/**
* returns a connection to the database
* @return a connection to the database
* @author John Doe <....>
*/
public static Connection getConnection() {
Connection output = null;
try {
// compose the database connection profile key
String profile_key = String.format("%s_%s", CONNECTION_KEY, DEFAULT_CONNECTION_PROFILE);
// retrieve the database connection profile keyset
HashMap<String, String> keyset = Configuration.getConfiguration(profile_key);
// output the database connection
output = DriverManager.getConnection(String.format("jdbc:mysql://%s:%s/%s?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC", keyset.get("host"), keyset.get("port"), keyset.get("schema")), keyset.get("username"), keyset.get("password"));
} catch (Throwable e) {
Log.error(MySQL.class, e);
}
return output;
}
/**
* returns a connection to the database
* @param profile the database configuration profile
* @return a connection to the database
* @author John Doe <....>
*/
public static Connection getConnection(String profile) {
Connection output = null;
try {
// compose the database connection profile key
String profile_key = String.format("%s_%s", CONNECTION_KEY, profile);
// retrieve the database connection profile keyset
HashMap<String, String> keyset = Configuration.getConfiguration(profile_key);
// output the database connection
output = DriverManager.getConnection(String.format("jdbc:mysql://%s:%s/%s?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC", keyset.get("host"), keyset.get("port"), keyset.get("schema")), keyset.get("username"), keyset.get("password"));
} catch (Throwable e) {
Log.error(MySQL.class, e);
}
return output;
}
/**
* returns a connection to the database
* @param host the database host
* @param port the database port
* @param schema the database schema
* @param username the database username
* @param password the database user password
* @return a connection to the database
* @author John Doe <....>
*/
public static Connection getConnection(String host, int port, String schema, String username, String password) {
Connection output = null;
try {
// output the database connection
output = DriverManager.getConnection(String.format("jdbc:mysql://%s:%s/%s?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC", host, port, schema), username, password);
} catch (Throwable e) {
Log.error(MySQL.class, e);
}
return output;
}
}
DISCLAIMER: Please, don't pay excessive attention to things like using snake_case (which is more Javascript/PHP/Python/R-ish) naming convention for variables, using Throwable
instead of Exception
, the fact that I'm building utility classes instead of full-fledged classes with their methods, properties and everything with its domain (public, private, protected and the like) correctly set up and many other things that should be there and they aren't. This is (practically) my second week with Java, I'm very open for suggestions on improvement and I admit there's a lot of work to be done here and there so be benevolent :P
That said, if I try to do this:
// define the package name
package com.foo.xxxxxxxx;
// import all needed resources
import com.foo.bar.helpers.Database;
import java.sql.Connection;
/**
* application
* @author John Doe <...>
*/
class Application {
public static void main(String[] args) {
Connection connection = Database.getConnection(Database.MYSQL_CLASS, "localhost", 3306, "foo_db", "john_doe_123", "this_is_not_my_real_password!");
}
}
I get this:
java.lang.NoSuchMethodException: java.lang.String.getConnection(java.lang.String, java.lang.Integer, java.lang.String, java.lang.String, java.lang.String)
at java.base/java.lang.Class.getDeclaredMethod(Class.java:2475)
at com.foo.bar.helpers.Database.getConnection(Database.java:146)
at com.foo.xxxxxxxx.Application.main(Application.java:61)
If I did read the documentation correctly, I need to get an instance of the class I intend to reflect, get the specific method I want to use with getDeclaredMethod
(because every method in any of my utility classes is static) with the method name as String
and a variable number of arguments (or an array, if I'm using it correctly) with the class type for each of the arguments I'm going to invoke the method with.
Done that, I need to invoke the method passing null
as the first argument (because it's a static method and static methods do not need an instance of the class I'm trying to call the specific method for) and a variable number of arguments (same as before) with the parameters themselves.
That error I'm getting from e.printStackTrace()
tells me the method acquisition failed, either because I didn't specified the class types correctly (I'm very doubtful about using Class<?>[]
instead of Class[]
but IntelliJ complains with Raw use of parameterized class 'Class'
) or I didn't really get an instance of the class I intend to get an instance from and I got some sort of a generic class object instead (so I can't really see the method I'm looking for).
Or maybe it's because I declared a private constructor to avoid instantiation (but I thought, after reading some articles, that utility classes (if you really need to use them) should have one to avoid instantiation... hence the private constructor declaration) but, either way, I'm a bit screwed at the moment :(
The idea is to be able to connect to any given database (because, right now, it's just MySQL and SQLite but it could be Amazon Redshift, BigQuery, PostgreSQL, Oracle, etc. in the future) but I may be getting the idea about generic access in the wrong way.
Can you give me a hint?
Aucun commentaire:
Enregistrer un commentaire