【问题标题】:Convert methods to a generic method in DAO layer在 DAO 层将方法转换为泛型方法
【发布时间】:2018-07-28 07:35:34
【问题描述】:

目前,我的 DAO 层中有一些方法具有多个选择查询。我的想法是为所有这三种方法提供一个通用方法,以便它也可以进一步使用。这是我的方法。

public List<Customer> findAll(){

    String sql = "SELECT * FROM CUSTOMER";

    List<Customer> customers  = getJdbcTemplate().query(sql,
        new BeanPropertyRowMapper(Customer.class));

    return customers;
} 

用于查找客户的电话号码。

public List<Phone> findPhoneNumbers(int custId){

    String sql = "SELECT * FROM PHONE WHERE CUST_ID="+custId;

    List<Phone> phoneNumbers  = getJdbcTemplate().query(sql,
        new BeanPropertyRowMapper(Phone.class));

    return phoneNumbers;
} 

等等。

能否将这些方法转换为单个泛型方法,以便可以从我的服务层调用。任何建议或想法将不胜感激。

【问题讨论】:

  • 你的两个方法有不同的参数。你可以有一个单一的遗传方法&lt;T&gt; List&lt;T&gt; findAll(String sql, Class&lt;T&gt; clazz) {...}。但是你必须在调用方法之前构造sql语句。
  • @zhh,您能否在答案中详细说明这两种方法适用于哪种方法?

标签: java generic-programming


【解决方案1】:

你可以使用泛型方法,让调用者指定类。

findAll 很容易

public <T> List<T> findAll(Class<T> entityClass, String tableName){

    String sql = "SELECT * FROM " + tableName;

    return getJdbcTemplate().query(sql,
        new BeanPropertyRowMapper(entityClass));

    return phoneNumbers;
} 

您可以通过使用类元数据来进一步改进这一点,例如指定注释:

@Target(ElementType.Type)
public @interface MappedTable {
    String tableName();
}

//apply the annotation to bean classes:
@MappedTable(tableName="CUSTOMERS")
public class Customer {}

这将使您的 findAll 方法看起来像:

public <T> List<T> findAll(Class<T> entityClass){

    String sql = "SELECT * FROM " + entityClass
      .getAnnotation(MappedTable.class).tableName();

    return getJdbcTemplate().query(sql,
        new BeanPropertyRowMapper(entityClass));

    return phoneNumbers;
} 

实现findByXyZ 会比较棘手,但您可以使用类似的方法,从调用者那里获取完整查询,或者使用额外的基于注释的元数据。

注意这种代码的复杂性增长得非常快。这就是为什么考虑使用 ORM 工具而不是重新发明它们是一个好主意(以上只是问题中简单案例的想法)。

【讨论】:

  • 欧内斯特,我对“您可以通过使用类元数据进一步改进这一点,例如指定注释:”感到困惑。
  • @Syed 我已经编辑以显示完整的方法。事实是,使方法泛型不仅仅具有返回类型的含义。您还需要更改正在查询的表名。有很多方法可以处理这个问题,其中一种是从调用者那里获取完整的查询。对于findAll,也可以只取表名。使用在实体类上使用的注解可以通过删除table name 参数来简化方法...
  • 如果我有选择查询,里面有连接怎么办?我觉得让 zhh 回答来实现这一点很简单。在自己之前创建 SQL 查询
  • @Syed 当然这会变得非常复杂。这就是为什么我们应该坚持使用成熟的工具,比如 JPA/Hibernate、GORM 等。但是如果你只是想要一种简单的调用方式,比如你的例子,那么这只是为了让你知道如何接近它。
  • 我很欣赏你的想法。但不幸的是,我无权决定采用任何 ORM 技术进行集成。
【解决方案2】:
public <T> List<T> findAll(String sql, Class<T> clazz) {
   return getJdbcTemplate().query(sql, new BeanPropertyRowMapper(clazz));
}

然后你打电话

String sql = "SELECT * FROM CUSTOMER";
List<Customer> customers = findAll(sql, Customer.class);

或者

String sql = "SELECT * FROM PHONE WHERE CUST_ID="+custId;
List<Phone> phoneNumbers = findAll(sql, Phone.class);

问题是你必须在调用方法之前构造sql语句。

也许你想要一个类似android sqldatabase的接口并在方法中构造sql语句。

public <T> List<T> findAll(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy,  Class<T> clazz) {
    String sql = // construct sql statement
    return getJdbcTemplate().query(sql, new BeanPropertyRowMapper(clazz));
}

我对sql语句不熟悉,你可以在Android SDKSQLiteQueryBuilder.buildQueryString(...)找到一些东西。

如果您真的想返回单个项目而不是包含一个项目的列表,则需要另一种方法。

public <T> T findOne(String sql, Class<T> clazz) {
    // your sql statement should contains something like "limit 1"
    List<T> result = findAll(sql, clazz);
    return result.isEmpty() ? null : result.get(0);
}

如果您需要一种方法,但需要为多个数据返回List&lt;T&gt;,为单个数据返回T。试试这个:

public <T> Object findAll(String sql, Class<T> clazz, bool one) {
    List<T> all = getJdbcTemplate().query(sql, new BeanPropertyRowMapper(clazz));
    return one ? all.get(0) : all;
}

但我并不真正建议这种方法。我认为没有必要返回单个项目而不是包含单个项目的列表。

【讨论】:

  • zhh,如果我想要一个单行,比如只返回客户对象而不是列表,我该如何处理?
  • 刚刚看到..这里我需要有两个方法,比如 findAll 和 findOne ,只有一个区别是 "return result.isEmpty() ? null : result.get(0)";我们仍然可以优化 findAll 方法吗?
  • 是的,findOne 依赖于 findAll
  • 那么,我们应该使用 findOne 还是 findAll?我想对所有选择查询使用一种方法,而不是两种方法。
  • 查看我的更新答案。为什么必须返回单个项目而不是单个项目的列表?
【解决方案3】:

您可以使用继承来实现这一点

1) class Customer

2)class PhoneNumber extends Customer

3)class Address extends Customer

4) 将方法声明为

public List<T> findPhoneNumbers(Customer customer){ 
if(customer instanceOf PhoneNumber) {sql ="get phone number query ...."}
else if(customer instanceOf Address) {sql ="get address query ...."}
else {sql = get customer query}
}

现在在调用方法(ServiceLayer)中,你可以传递适当的对象来选择查询并得到结果

【讨论】:

  • Ashish,这应该也适用于其他类型。这不是通用方式,因为还有一些其他类与 Customer 或 PhoneNumber 无关
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多