获取 class 的参数名称以将其传递给 java 中的其他方法
Get parameter name of class to pass it to other method in java
我想将参数名称作为参数传递给其他方法,f.e:
我有 class:
public class Foo() {
public Bar bar;
public Bar anotherBar;
public Bar yetAnotherBar;
public void doSomethingWithBar() {
common.doingSomething(
getMostImportantBarParameterName()
);
}
}
在这个 class 中我将有方法:
public String getMostImportantBarParameterName() {
return Foo.bar;
}
但是 而不是 returning 值 柱线,我想得到一个 参数名称 柱线,所以它应该只是 return "bar"
.
现在我必须这样做:
public String getMostImportantBarParameterName() {
return "bar";
}
为什么我想实现这样的目标?
我尽我所能避免在我的代码中使用字符串,因为在重构过程中我会不小心绕过(跳过)它。
但是如果我以这种方式使用 "hard coded" 参数,当我稍后重命名此参数时,它将在所有情况下被 Eclipse IDE 自动替换(使用 LALT+LSHIFT+R)
还有我的方法:common.doingSomething()
在运行时使用参数,所以我不会得到编译错误,这使得这种方法很难维护。
我不写单元测试,因为我还不会。
请给我一些帮助。谢谢
----------------编辑------------------------
现实生活中的用法。
我想要以通用方式访问数据库记录的方法。
我的应用程序中常见的数据库操作是:
从 TableName
获取记录,其中 Parameter
= SomeValue
所以我想在下面列出的通用实体中使用通用方法:
@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;
}
由真实实体扩展f.e.:
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;
}
}
回答你关于泛型的问题。您可以通过索引或名称 select 字段。两者都不健壮,因为当你改变字段名时,通过反射获取它的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();
编辑(阅读更新后的问题):
在任何对象关系映射解决方案中,都存在面向对象领域结束和关系领域开始的边界。使用您的解决方案,您将该边界进一步拉入您的代码,以便轻松使用您的特定模型 classes 和查询。这样做的结果是,您将获得更多 'boiler plate' 样式代码作为应用程序的一部分(GenericModel class),并且边界变得更加可见(使用反射通过索引或名称引用字段) .这种类型的代码通常更难理解、测试和维护。另一方面,一旦你做对了,它就不会经常改变(如果你对你通常需要的查询类型的假设被证明是有效的)。
所以我认为这不是一个可笑的反射用例,尽管我自己可能仍会坚持使用 JPA 并接受查询的相似性。使用良好的 JPA 框架,表达这些查询不会产生大量代码。
关于硬编码的字段名称与索引,我建议您使用字段名称,因为它们更容易理解和调试您的继任者。我会确保字段名称在字段所在的模型 class 中表示,以尽可能清楚地表明两者属于一起,类似于您给出的示例:
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>()
。如果查询成功或 returns 没有结果,您可以在 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);
这是我刚才的另一个(有点疯狂的)想法。每个模型class也是一个查询模板:
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
我想将参数名称作为参数传递给其他方法,f.e:
我有 class:
public class Foo() {
public Bar bar;
public Bar anotherBar;
public Bar yetAnotherBar;
public void doSomethingWithBar() {
common.doingSomething(
getMostImportantBarParameterName()
);
}
}
在这个 class 中我将有方法:
public String getMostImportantBarParameterName() {
return Foo.bar;
}
但是 而不是 returning 值 柱线,我想得到一个 参数名称 柱线,所以它应该只是 return "bar"
.
现在我必须这样做:
public String getMostImportantBarParameterName() {
return "bar";
}
为什么我想实现这样的目标? 我尽我所能避免在我的代码中使用字符串,因为在重构过程中我会不小心绕过(跳过)它。
但是如果我以这种方式使用 "hard coded" 参数,当我稍后重命名此参数时,它将在所有情况下被 Eclipse IDE 自动替换(使用 LALT+LSHIFT+R)
还有我的方法:common.doingSomething()
在运行时使用参数,所以我不会得到编译错误,这使得这种方法很难维护。
我不写单元测试,因为我还不会。
请给我一些帮助。谢谢
----------------编辑------------------------
现实生活中的用法。
我想要以通用方式访问数据库记录的方法。 我的应用程序中常见的数据库操作是:
从 TableName
获取记录,其中 Parameter
= SomeValue
所以我想在下面列出的通用实体中使用通用方法:
@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;
}
由真实实体扩展f.e.:
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;
}
}
回答你关于泛型的问题。您可以通过索引或名称 select 字段。两者都不健壮,因为当你改变字段名时,通过反射获取它的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();
编辑(阅读更新后的问题):
在任何对象关系映射解决方案中,都存在面向对象领域结束和关系领域开始的边界。使用您的解决方案,您将该边界进一步拉入您的代码,以便轻松使用您的特定模型 classes 和查询。这样做的结果是,您将获得更多 'boiler plate' 样式代码作为应用程序的一部分(GenericModel class),并且边界变得更加可见(使用反射通过索引或名称引用字段) .这种类型的代码通常更难理解、测试和维护。另一方面,一旦你做对了,它就不会经常改变(如果你对你通常需要的查询类型的假设被证明是有效的)。
所以我认为这不是一个可笑的反射用例,尽管我自己可能仍会坚持使用 JPA 并接受查询的相似性。使用良好的 JPA 框架,表达这些查询不会产生大量代码。
关于硬编码的字段名称与索引,我建议您使用字段名称,因为它们更容易理解和调试您的继任者。我会确保字段名称在字段所在的模型 class 中表示,以尽可能清楚地表明两者属于一起,类似于您给出的示例:
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>()
。如果查询成功或 returns 没有结果,您可以在 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);
这是我刚才的另一个(有点疯狂的)想法。每个模型class也是一个查询模板:
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