我在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;
}
}
DISCLAIMER:请不要过多注意使用变量的命名约定使用snake_case(更多的Javascript / PHP / Python / R-ish),使用Throwable
代替Exception
,我正在使用它们的方法,属性以及其域(公共,私有,受保护等)的所有内容正确建立实用程序类而不是full-fledged类的事实,并且应该正确地建立许多其他内容而事实并非如此。这(实际上)是我第二周的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等),但我可能会得到关于以错误方式进行通用访问的想法。
你能给我一个提示吗?
您给出的异常提示您试图在类getConnection()
中找到方法java.lang.String
。我怀疑您没有把它放在那里,所以找不到任何东西。
在Database#getConnection
类中,我注意到以下语句
Class<?> target_class = Class.forName(parameters[0].getClass().getCanonicalName());
这基本上意味着您将第一个参数作为类Object处理。 (首先获取类类型实例,然后从那里获取名称)。因此,这里有一些注意事项。每个对象(即每个非原始值)都有一个类类型,甚至返回的类类型本身也是如此。如果不谨慎的话,这会造成混乱。
所以我可以想到有3种情况,您可以在几秒钟内处理这个特殊情况:
传递Class
实例,例如
Database.getConnection(MySQL.class,...);
//在#getConnection类中class target_class = parameters [0] //类型已经是一个类,因此只需赋值
传递所需的类类型的实例,如
Database.getConnection(new MySQL(),...); //绝对不建议使用,仅在需要实例本身时才真正可用(例如,非静态访问)
//在#getConnection类中class target_class = parameters [0] .getClass()//获取类类型的实例
传递所需类类型的字符串表示形式(规范名称)
Database.getConnection(new MySQL(),...); //绝对不建议使用,仅在需要实例本身时才真正可用(例如,非静态访问)
//在#getConnection类中class target_class = Class.forName(parameters [0])// #forName需要一个String参数,因此我们可以直接传递它。
在最新的示例中,您可以使用ClassLoader
等。它提供了不错的功能,例如缓存和类卸载。但是它相当复杂,因此您的第一个副手可能就不算什么了。
最后,作为一般建议。 Java是强类型的,具有诸如方法重载之类的功能。因此,为了您自己的理智,请尝试尽可能地滥用它。上面的3可以很容易地被重载,从而使参数验证变得不那么费力。它使API用户可以很好地使用它“万无一失”,因为在编译过程中会注意到类型缺失匹配。