我想将参数名称作为参数传递给其他方法,例如:
我有课:
public class Foo() {
public Bar bar;
public Bar anotherBar;
public Bar yetAnotherBar;
public void doSomethingWithBar() {
common.doingSomething(
getMostImportantBarParameterName()
);
}
}
在这个类中,我会有方法:
public String getMostImportantBarParameterName() {
return Foo.bar;
}
但是我想获取参数bar的名称,而不是返回bar的值,因此它应该只返回"bar"
。
现在我必须这样做:
public String getMostImportantBarParameterName() {
return "bar";
}
为什么我想实现这样的事情?我正在尽我所能避免在我的代码中使用字符串,因为在重构过程中我会意外绕过(跳过)它。
但是,如果我以这种方式使用"硬编码"参数,那么当我稍后重命名此参数时,它将在所有实例中自动替换为Eclipse IDE(使用LALT + LSHIFT+ R)。
还有我的方法:common.doingSomething()
运行时使用参数,所以我不会得到编译错误,这使得维护这种方法变得困难。
我不写单元测试,因为我还不能。
请给我一些帮助。谢谢
-----------------编辑------------------------
现实生活中的用法。
我希望有以通用方式访问数据库记录的方法。我的应用程序中常见的数据库操作是:
从Parameter
= SomeValue
TableName
获取记录
因此,我希望在下面列出的通用实体中使用通用方法:
@MappedSuperclass
public abstract class GenericModel<T extends GenericModel> {
@Transient protected Class<T> entityClass;
private List<T> getByParameterAndValue(String parameter, String value) {
List<T> entities = new ArrayList<T>();
String sqlString = "SELECT e FROM " + entityClass.getSimpleName() + " e WHERE e."+ parameter + " = :value";
TypedQuery<T> query = JPA.em().createQuery(sqlString, entityClass).setParameter("value", value);
try {
entities = query.getResultList();
} catch (NoResultException e1) {
entities = null;
} catch (Exception e) {
Index.toLog("error","Unsupported error in Generic model class in " + entityClass);
}
return entities;
}
由真实实体扩展,例如:
public class User extends GenericModel<User> {
public String name;
public String email;
public String date;
public String department;
public List<User> getUsersByDepartments(String dep) {
return getByParameterAndValue("department", dep);
}
}
问题是在JPA TypedQuery
:
TypedQuery<User> query = em.createQuery("SELECT u FROM User u WHERE u.department = :department", User.class);
return query.setParameter("department", department).getSingleResult();
首先,我认为你应该重新考虑你的方法。使用这样的字段名称(通过反射或硬编码字符串)不是很健壮。一般来说,如果可能的话,应该避免反思。
你想实现什么?common.doingSomething
将如何处理字段名称?
使用访问器显式建模重要性:
class Foo {
private Bar bar;
private Bar anotherBar;
private Bar yetAnotherBar;
public Bar getMostImportantBar() {
return bar;
}
}
回答您关于泛型的问题。您可以按索引或名称选择字段。两者都不是健壮的,因为当您更改字段名称时,用于通过反射获取它的 String 不会随之更改,并且如果您更改字段的顺序,索引将是错误的。
具体操作方法如下:
Class foo = Foo.class;
Field[] fields = foo.getFields();
// get by index
Field firstField = fields[0];
String firstFieldName = firstField.getName();
// get by name
Field barField = foo.getField("bar");
String barFieldName = barField.getName();
编辑(阅读更新的问题后):
在任何对象关系映射解决方案中,都有一个面向对象的领域结束和关系领域开始的边界。使用解决方案,可以将该边界进一步拉到代码中,以便轻松用于特定的模型类和查询。这样做的结果是,作为应用程序(GenericModel 类)的一部分,您可以获得更多的"样板"样式代码,并且边界变得更加可见(使用反射按索引或名称引用字段)。这种类型的代码通常更难理解、测试和维护。另一方面,一旦你做对了,它就不会经常改变(如果你对通常需要的查询类型的假设被证明是有效的)。
所以我认为这不是一个荒谬的反思用例,即使我自己可能仍然会坚持 JPA 并接受查询的相似性。使用良好的 JPA 框架,表达这些查询不会产生大量代码。
关于硬编码的字段名称与索引,我建议您使用字段名称,因为它们更易于理解和调试。我会确保字段名称在字段所在的模型类中表示,以尽可能清楚地表明两者属于一起,类似于您给出的示例:
public class User extends GenericModel<User> {
public static final String FIELD_NAME = "name";
public static final String FIELD_EMAIL = "email";
public static final String FIELD_DATE = "date";
public static final String FIELD_DEPARTMENT = "department";
private String name;
private String email;
private String date;
private String department;
// the byXXX naming scheme is a quite common shorthand for lookups
public List<User> byDepartment(String department) {
return getByParameterAndValue(FIELD_DEPARTMENT, department);
}
顺便说一句,我认为getByParameterAndValue不能是私有的(必须至少是默认值)。另外,我认为您不应该在开始时初始化List<T> entities = new ArrayList<T>()
。您可以在catch(Exception e)
中执行此操作,以避免在查询成功或未返回任何结果时进行不必要的初始化。您的字段应该是私有的(如上所示)。
当然,这种方法仍然为每个字段产生一种查找方法。另一种解决方案是为此创建一个服务,并使模型对象保持灌肠状态(不带行为):
public class DaoService {
public <T extends GenericModel> List<T> get(Class<T> entityClass, String fieldName, String value) {
List<entityClass> entities;
String sqlString = "SELECT e FROM " + entityClass.getSimpleName() + " e WHERE e."+ fieldName+ " = :value";
TypedQuery<T> query = JPA.em().createQuery(sqlString, entityClass).setParameter("value", value);
try {
entities = query.getResultList();
} catch (NoResultException e) {
entities = null;
} catch (Exception e) {
entities = new ArrayList<T>()
}
return entities;
}
}
用法:
List<User> = daoService.get(User.class, User.FIELD_DEPARTMENT, value);
这是我刚刚想到的另一个(有点疯狂的)想法。每个模型类也是一个查询模板:
public abstract class ModelQuery<T extends ModelQuery> {
// TODO set from constructor
private Class<T> entityClass;
private Field[] allFields = entityClass.getFields();
private List<T> getByTemplate() {
List<Field> queryFields = new ArrayList<Field>();
String sql = selectFieldsAndCreateSql(queryFields);
TypedQuery<T> query = setQueryParameters(queryFields, sql);
return executeQuery(query);
}
private String selectFieldsAndCreateSql(List<Field> queryFields) throws IllegalAccessException {
StringBuilder sql = new StringBuilder();
sql.append("SELECT e FROM ")
.append(entityClass.getSimpleName())
.append("e WHERE ");
for (Field field : allFields) {
if (field.get(this) != null) {
sql.append("e.")
.append(field.getName())
.append(" = :")
.append(field.getName());
// keep track of the fields used in the query
queryFields.add(field);
}
}
return sql.toString();
}
private TypedQuery<T> setQueryParameters(List<Field> queryFields, String sql) throws IllegalAccessException {
TypedQuery<T> query = JPA.em().createQuery(sql, entityClass);
for (Field field : queryFields) {
query.setParameter(field.getName(), field.get(this));
}
return query;
}
private List<T> executeQuery(TypedQuery<T> query) {
List<T> entities;
try {
entities = query.getResultList();
} catch (NoResultException e1) {
entities = null;
} catch (Exception e) {
entities = new ArrayList<T>();
}
return entities;
}
}
用法:
User userQuery = new User();
userQuery.setDepartment("finance");
List<User> results = userQuery.getByTemplate();
我想还有更多方法可以剥掉这只猫的皮。祝您好运,找到您的最佳解决方案!
获取私有字段名称
use foo.getDeclaredFields(); instead of foo.getFields();
这里你也有一些小问题
fields[0] means, the first declared field, in which 0 is again hard coded
如果您更改声明的顺序,那么这对您来说可能又是一个麻烦,永远不会折射
我建议使用
1.) The Class.forName() SPI logic where you can inject the expected business logic on the fly.
2.) The Spring DI with interfaces and implementations using auto wiring