泛型方法在实例化方法中传递的泛型类型时返回列表
Generic method returning a List while instantiating the generic Type passed in the method
我已经苦苦挣扎了很长时间,并查阅了一些资源,试图理解我如何使用泛型来实现我想要实现的目标,但一直无法找到提供的示例我需要什么。
一些示例包括使用 Supplier 接口或建议使用通用 class 然后实例化它。
我想要实现的是指定我希望方法 return 的类型,同时在方法中实例化相同类型并将其添加到列表,然后 return 该列表。
提前致谢。非常感谢任何帮助
public List<Doctor> queryDoctor() {
Statement statement = null;
ResultSet results;
try {
statement = connection.createStatement();
results = statement.executeQuery("SELECT * FROM " + TABLE_DOCTOR);
List<Doctor> doctors = new ArrayList<>();
while (results.next()) {
Doctor doctor = new Doctor();
doctor.setId(results.getInt(1));
doctor.setUserName(results.getString(2));
doctor.setPassword(results.getString(3));
doctor.setPassword_salt(results.getString(4));
doctor.setRole(results.getInt(5));
doctors.add(doctor);
}
return doctors;
} catch (SQLException e) {
System.out.println("Query failed: " + e.getMessage());
return null;
} finally {
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
您需要传入 Function<ResultSet, T>
:
<T> List<T> queryDoctor(Function<ResultSet, T> fn)
并在循环中使用它:
doctors.add(fn.apply(results));
此函数将创建适当的具体类型:
List<Doctor> doctors = queryDoctor(results -> {
Doctor doctor = new Doctor();
doctor.setId(results.getInt(1));
doctor.setUserName(results.getString(2));
doctor.setPassword(results.getString(3));
doctor.setPassword_salt(results.getString(4));
doctor.setRole(results.getInt(5));
return doctor;
});
或
List<Nurse> nurses = queryDoctor(results -> {
Nurse nurse = new Nurse();
// ...
return nurse;
});
听起来您正在尝试自己的简单 ORM 解决方案,您在其中提供一个带有 SQL 字符串和类型的方法,该方法加载结果然后实例化类型,将结果集映射到通过 setter 的对象字段。
如果是这样,你就是在重新发明轮子。
一个完整的解决方案是使用 Hibernate 或 Top Link 之类的 ORM 工具,但这可能 运行 在您可以走路之前。如果您更喜欢小步骤,请考虑使用 Spring 的 Jdbc 模板来处理大量样板代码。
更新
好的,我问您是否正在寻找一种方法,该方法采用结果集并将其映射到任意 class,您说是的。这应该做这样的事情:
public <T> List<T> map(ResultSet rs, Class<T> clazz) throws Exception {
//// Build a list of columns that are in the resultset
List<String> columns = new LinkedList<>();
ResultSetMetaData metaData = rs.getMetaData();
for(int i=0; i < metaData.getColumnCount(); i++) {
columns.add(metaData.getCatalogName(i));
}
//// Create a list to hold all the beans we are going to create from the resultset
List<T> rows = new LinkedList<>();
//// Iterate over each row in the resultset, creating beans for each row and setting the values
while(rs.next()) {
//// Create a new instance of the class passed in (must have a no-args constructor)
T row = clazz.newInstance();
//// Now for the magic. This bit is very noddy and ORM packages will do this much better
for(String col : columns) {
//// For each column that is in the resultset, find the setter in the bean class
String columnValue = rs.getString(col);
String mutator = "set"+capitalizeFirstCharacter(col);
Method m = clazz.getMethod(mutator, String.class);
//// Invoke the setter, passing in the value from the resultset
m.invoke(row, columnValue);
}
rows.add(row);
}
return rows;
}
private static String capitalizeFirstCharacter(String s) {
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
请注意,此代码不处理异常,仅适用于填充有字符串的结果集,并且仅处理具有要填充的字符串字段的 bean。一旦您开始尝试编写更复杂的代码来处理更复杂的类型和反射……您就是在重新发明 ORM。尽量不要那样做。
我已经苦苦挣扎了很长时间,并查阅了一些资源,试图理解我如何使用泛型来实现我想要实现的目标,但一直无法找到提供的示例我需要什么。
一些示例包括使用 Supplier 接口或建议使用通用 class 然后实例化它。
我想要实现的是指定我希望方法 return 的类型,同时在方法中实例化相同类型并将其添加到列表,然后 return 该列表。
提前致谢。非常感谢任何帮助
public List<Doctor> queryDoctor() {
Statement statement = null;
ResultSet results;
try {
statement = connection.createStatement();
results = statement.executeQuery("SELECT * FROM " + TABLE_DOCTOR);
List<Doctor> doctors = new ArrayList<>();
while (results.next()) {
Doctor doctor = new Doctor();
doctor.setId(results.getInt(1));
doctor.setUserName(results.getString(2));
doctor.setPassword(results.getString(3));
doctor.setPassword_salt(results.getString(4));
doctor.setRole(results.getInt(5));
doctors.add(doctor);
}
return doctors;
} catch (SQLException e) {
System.out.println("Query failed: " + e.getMessage());
return null;
} finally {
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
您需要传入 Function<ResultSet, T>
:
<T> List<T> queryDoctor(Function<ResultSet, T> fn)
并在循环中使用它:
doctors.add(fn.apply(results));
此函数将创建适当的具体类型:
List<Doctor> doctors = queryDoctor(results -> {
Doctor doctor = new Doctor();
doctor.setId(results.getInt(1));
doctor.setUserName(results.getString(2));
doctor.setPassword(results.getString(3));
doctor.setPassword_salt(results.getString(4));
doctor.setRole(results.getInt(5));
return doctor;
});
或
List<Nurse> nurses = queryDoctor(results -> {
Nurse nurse = new Nurse();
// ...
return nurse;
});
听起来您正在尝试自己的简单 ORM 解决方案,您在其中提供一个带有 SQL 字符串和类型的方法,该方法加载结果然后实例化类型,将结果集映射到通过 setter 的对象字段。
如果是这样,你就是在重新发明轮子。
一个完整的解决方案是使用 Hibernate 或 Top Link 之类的 ORM 工具,但这可能 运行 在您可以走路之前。如果您更喜欢小步骤,请考虑使用 Spring 的 Jdbc 模板来处理大量样板代码。
更新
好的,我问您是否正在寻找一种方法,该方法采用结果集并将其映射到任意 class,您说是的。这应该做这样的事情:
public <T> List<T> map(ResultSet rs, Class<T> clazz) throws Exception {
//// Build a list of columns that are in the resultset
List<String> columns = new LinkedList<>();
ResultSetMetaData metaData = rs.getMetaData();
for(int i=0; i < metaData.getColumnCount(); i++) {
columns.add(metaData.getCatalogName(i));
}
//// Create a list to hold all the beans we are going to create from the resultset
List<T> rows = new LinkedList<>();
//// Iterate over each row in the resultset, creating beans for each row and setting the values
while(rs.next()) {
//// Create a new instance of the class passed in (must have a no-args constructor)
T row = clazz.newInstance();
//// Now for the magic. This bit is very noddy and ORM packages will do this much better
for(String col : columns) {
//// For each column that is in the resultset, find the setter in the bean class
String columnValue = rs.getString(col);
String mutator = "set"+capitalizeFirstCharacter(col);
Method m = clazz.getMethod(mutator, String.class);
//// Invoke the setter, passing in the value from the resultset
m.invoke(row, columnValue);
}
rows.add(row);
}
return rows;
}
private static String capitalizeFirstCharacter(String s) {
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
请注意,此代码不处理异常,仅适用于填充有字符串的结果集,并且仅处理具有要填充的字符串字段的 bean。一旦您开始尝试编写更复杂的代码来处理更复杂的类型和反射……您就是在重新发明 ORM。尽量不要那样做。