我在 Java 中构建了一个 _VERY_ 基本实用程序类来处理数据库操作(连接检索、插入等),如下所示:
// 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;
}
}
除了数据库助手之外,我还有两个像这样的数据库连接器(MySQL 和 SQLite)(显示 MySQL 连接器):
// 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;
}
}
免责声明:请不要过分关注诸如使用snake_case(更多的是Javascript / PHP / Python / R-ish)变量命名约定,使用Throwable
而不是,我正在构建实用程序类而不是完整Exception
的事实- 成熟的类及其方法、属性以及其域(公共、私有、受保护等)正确设置的所有内容,以及许多其他应该存在但不存在的东西。这是(实际上)我使用 Java 的第二周,我非常愿意提出改进建议,我承认这里还有很多工作要做,所以要仁慈:P
也就是说,如果我尝试这样做:
// 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!");
}
}
我明白了:
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)
如果我确实正确阅读了文档,我需要获取我打算反映的类的实例,获取我想要使用的特定方法getDeclaredMethod
(因为我的任何实用程序类中的每个方法都是静态的),方法名称为String
和可变数量的参数(或一个数组,如果我使用正确的话)以及我将要调用该方法的每个参数的类类型。
完成后,我需要调用null
作为第一个参数传递的方法(因为它是一个静态方法,而静态方法不需要我试图为其调用特定方法的类的实例)和可变数量的参数(相同和以前一样)与参数本身。
我得到的那个错误e.printStackTrace()
告诉我方法获取失败,或者是因为我没有正确指定类类型(我很怀疑使用Class<?>[]
而不是,Class[]
但 IntelliJ 抱怨Raw use of parameterized class 'Class'
)或者我没有真正得到一个实例我打算从中获取实例的类,而我得到了某种通用类对象(所以我看不到我正在寻找的方法)。
或者可能是因为我声明了一个私有构造函数来避免实例化(但我认为,在阅读了一些文章之后,实用程序类(如果你真的需要使用它们)应该有一个来避免实例化......因此私有构造函数声明)但是,不管怎样,我现在有点搞砸了:(
这个想法是能够连接到任何给定的数据库(因为现在只有 MySQL 和 SQLite,但将来可能是 Amazon Redshift、BigQuery、PostgreSQL、Oracle 等),但我可能会想到以错误的方式进行通用访问。
你能给我一个提示吗?