【问题标题】:Bad grammar SQL Exception while reading the values using rowmapper使用 rowmapper 读取值时出现语法错误 SQL 异常
【发布时间】:2017-12-21 08:05:39
【问题描述】:

这是我的模型类

//Model

    public class CustomerData {

        private String locomotive_id;
        private String customer_name;
        private String road_number;
        private String locomotive_type_code;
        private String in_service_date;
        private String part_number;
        private String emission_tier_type;
        private String airbrake_type_code;
        private String lms_fleet;
        private String aar_road;
        private String locomotive_status_code;

        // Getters and Setters

这是我的 RowMapper 实现

//RowMapper

    public class CustomerDataResponseMapper implements RowMapper {

    @Override
    public Object mapRow(ResultSet rs, int count) throws SQLException {
        CustomerData customerData = new CustomerData();

        customerData.setLocomotive_id(rs.getString("locomotive_id"));
        customerData.setCustomer_name(rs.getString("customer_name"));
        customerData.setRoad_number(rs.getString("road_number"));
        customerData.setLocomotive_type_code(rs.getString("locomotive_type_code"));
        customerData.setIn_service_date(rs.getString("in_service_date"));
        customerData.setPart_number(rs.getString("part_number"));
        customerData.setEmission_tier_type(rs.getString("emission_tier_type"));
        customerData.setAirbrake_type_code(rs.getString("airbrake_type_code"));
        customerData.setLms_fleet(rs.getString("lms_fleet"));
        customerData.setAar_road(rs.getString("aar_road"));
        customerData.setLocomotive_status_code(rs.getString("locomotive_status_code"));

        return customerData;
    }

}

最后,我在这里得到了我的 DaoImpl 类

//DaoImpl
    public String getCustomersData(String locoId, String custName, String roadNumber) {
        CustomerData resultSet = null;
        String str = "";
        if (locoId != null && locoId.length() > 0 && !(locoId.equals("0"))) {
            str = "select   locomotive_id,customer_name,road_number,model_type as locomotive_type_code,to_char(in_service_date,'yyyy-mm-dd') as in_service_date,loco_part_number as part_number,    emission_tier_type as emission_tier_type, "
                    + "air_brake_type as airbrake_type_code,lms_fleet,aar_road,locomotive_status_code   from get_rdf_explorer.get_rdf_locomotive_detail  where locomotive_id = ?";
            resultSet = (CustomerData) jdbcTemplate.queryForObject(str, new CustomerDataResponseMapper(), locoId);
        } else if ((custName != null && custName.length() > 0)
                && (roadNumber != null && roadNumber.length() > 0 && roadNumber != "0")) {
            str = "select   locomotive_id,customer_name,road_number,model_type as locomotive_type_code,to_char(in_service_date,'yyyy-mm-dd') as in_service_date,loco_part_number as part_number,    emission_tier_type as emission_tier_type, "
                    + "air_brake_type as airbrake_type_code,lms_fleet,aar_road,locomotive_status_code   from get_rdf_explorer.get_rdf_locomotive_detail  where customer_name = ? and road_number= ?";
            resultSet = (CustomerData) jdbcTemplate.queryForObject(str, new CustomerDataResponseMapper(), custName, roadNumber);
        } else {
            str = "select distinct customer_name from get_rdf_explorer.get_rdf_locomotive_detail order by customer_name asc";
            resultSet = (CustomerData) jdbcTemplate.queryForObject(str, new CustomerDataResponseMapper());
        }
        return resultSet.toString();
    }

如何根据结果集中是否存在特定列有条件地从结果集中获取值。因为我并没有一直通过我的查询获得所有的列。

当结果集中不存在特定列时,我收到 SQL 语法错误异常。例如,当执行获取不同客户名称的第三个查询时,结果集中只有 customerName 会出现,而其他列则不会出现。

这将是一个很大的帮助。提前非常感谢。

【问题讨论】:

  • 欢迎来到 Stack Overflow!寻求调试帮助的问题(“为什么这段代码不起作用?”)必须包括所需的行为、特定问题或错误重现它所需的最短代码在问题本身。没有明确问题陈述的问题对其他读者没有用处。请参阅:How to create a Minimal, Complete, and Verifiable example
  • @JoeC 因为它是我的项目特定的代码,我不能发布完整的代码。
  • 那么具体的问题或错误呢?更不用说必要的最短代码
  • @JoeC 明白了你的意思。非常感谢。请参阅更新的问题。让我知道是否需要添加更多详细信息。

标签: java spring jdbctemplate mapper


【解决方案1】:

我的建议是将您的getCustomersData 拆分为三种不同的方法。如果您绝对想忽略此建议,那么快速而肮脏的解决方案是保护您的 rowMapper 中的 rs.getString(...) 调用。像这样的:

try {
    customerData.setLocomotive_id(rs.getString("locomotive_id"));
} catch (SQLException e) {
    // ignore this exception. DO NOT TRY THIS AT HOME!
}

【讨论】:

  • stackoverflow.com/questions/3599861/… 这是我密切关注的。在这种情况下使用异常可能不是正确的选择,对吧?
  • 当然,正如我所写,这是一个“快速而肮脏”的解决方案。你最好重构你的设计。
  • 嗨,@aaguilera 这个解决方案成本更高且耗时,您可以阅读更多关于 java 异常有多慢here
【解决方案2】:

如果列数不固定,那么您应该根据其实现使用ColumnMapRowMapper,即使您不需要创建单独的RowMapper 的具体类(即CustomerDataResponseMapper),您只需要传递@ 的实例987654325@ 查询如下:

ColumnMapRowMapper rowMapper = new ColumnMapRowMapper();
List<Map<String, Object>> customerDataList =  jdbcTemplate.query(sql,rowMapper, args);

现在您应该创建一种方法来操作此地图,例如

    private CustomerData fillCustomerDataFromMap(List<Map<String, Object>> customerDataList){
            CustomerData customerData = new CustomerData();

            for(Map<String, Object> map: customerDataList ){

               customerData.setColumn(map.get("columnName"));
               customerData.setColumn(map.get("columnName"));
               customerData.setColumn(map.get("columnName"));
               customerData.setColumn(map.get("columnName"));

.........
.........
.........
           }
        return customerData;
    }

这更具可读性并删除了样板代码,并且如果映射中不存在列名,则不会抛出任何异常(如果映射中不存在列名,它将简单地返回 null

ColumnMapRowMapper的引用:

https://github.com/spring-projects/spring-framework/blob/master/spring-jdbc/src/main/java/org/springframework/jdbc/core/ColumnMapRowMapper.java

【讨论】:

  • 完美,这就是我想要的。有什么可行的例子吗?
  • 嗨,@Santosh Anantharamaiah,这毫无疑问应该可以工作,因为我们只使用了 spring jdbc 类库。
【解决方案3】:

您可以使用BeanPropertyRowMapper 直接映射目标类的字段名称。这是javadoc

名称可以直接匹配,也可以通过将带下划线的部分的名称转换为使用“camel”大小写的相同名称来匹配。因此,您可以在任何其他类中使用它来映射直接上课。只需确保所选字段保留在目标类中。以及默认或无参数构造函数。

以下示例使用BeanPropertyRowMapper 获取CustomerData

RowMapper<CustomerData> mapper = new BeanPropertyRowMapper<>(CustomerData.class);
List<CustomerData> result = jdbc.query("your query string...", mapper, query_args...);

所以,你可以返回第一个对象或任何东西。

【讨论】:

  • 嗨,@zico 我认为这对未确定的列数没有帮助
  • @IrfanBhindawala 未确定的列数是什么意思?尽管BeanPropertyRowMapper 自动将选定的列映射到目标类的属性。如果他选择一个字段并将其呈现在具有多个属性的目标类中,那么它将只绑定(调用默认设置器)一个字段。
  • BeanPropertyRowMapper 如何决定调用哪个类的 setter 以及默认 setter 是什么意思
  • @IrfanBhindawala 您可以在文档中获取所有信息。答案中提供的链接
  • 感谢@Zic 的参考
【解决方案4】:

根据您的查询逻辑,我看到在get_rdf_explorer.get_rdf_locomotive_detail 中执行 sql 查询之前,有一些记录和 3 个获取必要(唯一)记录的选项是可能的:

  1. locomotive_id
  2. customer_nameroad_number
  3. ANY 记录(所有记录必须具有相同的customer_name,否则SQL distinct 没有任何条件返回超过1 行)

因此,在第 3 个选项中,您可以获得所有属性等于 NULLNOT NULL customer_name 值的任何 1 条记录:

str = "select null as locomotive_id, customer_name, null as road_number,     
<other attributes> from get_rdf_explorer.get_rdf_locomotive_detail where 
rownum = 1";`

【讨论】:

    【解决方案5】:

    Santosh,一个快速的解决方法可能是在将标志提供给 jdbcTemplate 的同时将标志传递给您的行映射器。我已经做了很多次以避免多个行映射器。 resultSet = (CustomerData) jdbcTemplate.queryForObject(str, new CustomerDataResponseMapper(1), custName, roadNumber);

    对于上述更改,您需要使用默认的构造函数重载。然后你需要使用你的标志,即 mapRow() 方法中的实例变量来分别处理每种情况。

    【讨论】:

      【解决方案6】:

      您可以修改 SQL 以匹配映射器,而不是按条件获取列,例如将其他字段设置为空字符串或 null(我不记得 getString() 是否因 null 或其他原因崩溃)。

      例如,您的第三个查询如下所示:

      select distinct customer_name, null as "locomotive_id",'' as "road_number", null as model_type, [etc.] from get_rdf_explorer.get_rdf_locomotive_detail order by customer_name asc
      

      因此,每个查询都有相同的列,您不必适应。如果您真的不想/不能更改 rowMapper(或只想为该对象设置一个),这就是解决方案。

      但老实说,我会选择 ikketu 的解决方案。您应该为第三个查询制作一个单独的映射器(另外,它不会很复杂)。不使用 ORM 是一种选择,但无论如何你都会遇到冗余问题。我什至要补充一点,您应该在代码中分离一些逻辑,这些方法似乎在做不同的事情(业务逻辑取决于输入和数据库访问)不是很清楚(在第三个 if 之后,创建一个像“ getdistinctName()" 什么的)。

      【讨论】:

        【解决方案7】:

        既然您已经有 3 个单独的查询,为什么不设置 3 个单独的 RowMappers,每个查询一个。您的查询“知道”它们返回的列,因此您可以轻松地为 RowMapper 创建这些类。

        如果您真的想要高保真解决方案,您可以为公共部件创建抽象基础RowMapper,并为查询指定的部件创建 3 个子类。

        【讨论】:

        • 让我们考虑每个查询中只有很少的列名不同,这不违反 DRY 原则。在 3 个不同的地方重复同一段代码。
        • 您可以对公共部分使用公共超类。但在我看来,DRY 是一个很好的原则,但不能在任何地方强制执行。在这里,您有 3 个单独的查询,它们很可能会演变为彼此更加不同。这些是简单的类,它是具有不同 RowMappres 的更易读的代码代码,而不是试图创建一些事后无人能理解的自动魔法。
        【解决方案8】:

        您可以使用通用方法来调查ResultSet 的列

        @SuppressWarnings("unchecked")
        public <T> T getColIfPresent(ResultSet rs, String columnName) throws SQLException {
            ResultSetMetaData metaData = rs.getMetaData();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                if (columnName.equals(metaData.getColumnName(i))) {
                    return (T) rs.getObject(columnName);
                }
            }
            return null;// not present
        }
        

        然后,在行映射器中。

        customerData.setLocomotive_id(getColIfPresent(rs, "locomotive_id"));
        ...
        

        这具有 O(n*m) 复杂性,其中 n - 检查的列数,m - 返回的列数ResultSet

        如果您选择忽略SQLException,请至少将其记录在DEBUGTRACE 级别,以防出现SQLException 的不同子类型,以免丢失。

        【讨论】:

        • 我完全同意你的观点,但这肯定会影响性能,对吧?因为它必须检查列然后返回并分配值。
        • 我建议将调查的列存储在行映射器的私有属性中为Set&lt;String&gt; columns。然后在第一行用ResultSet 的列填充Set,其他行将只对这个集合执行包含检查if(columns.contains(columnName)){..} 检查,即O(1)。这样,每个查询只确定一次列,而不是每一行。
        猜你喜欢
        • 2016-08-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多