语法错误 SQL 使用 rowmapper 读取值时出现异常
Bad grammar SQL Exception while reading the values using rowmapper
这是我的模特class
//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 class
//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 会出现,而其他列则不会。
这将是一个很大的帮助。提前致谢。
我的建议是将您的 getCustomersData
分成三种不同的方法。如果您确实想忽略此建议,快速而肮脏的解决方案是保护 rowMapper 中的 rs.getString(...)
调用。像这样:
try {
customerData.setLocomotive_id(rs.getString("locomotive_id"));
} catch (SQLException e) {
// ignore this exception. DO NOT TRY THIS AT HOME!
}
您可以使用通用方法调查 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
,至少将其记录在 DEBUG
或 TRACE
级别,以防出现 SQLException
的不同子类型,因此它是没丢。
既然您已经有 3 个单独的查询,为什么不用 3 个单独的 RowMapper
,每个查询一个。您查询 "know" 哪些列 return,因此您可以轻松地为 RowMapper
创建那些 类。
如果您真的想要高保真解决方案,您可以为公共部分创建抽象基础 RowMapper
,为查询指定的部分创建 3 个子类。
而不是按条件获取列,您可以修改 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
所以每个查询都会有相同的列,您不必进行调整。如果您真的 want/can 不更改 rowMapper(或只想为此对象设置一个),这就是解决方案。
但老实说,我会选择 ikketu 的解决方案。您应该为第三个查询制作一个单独的映射器(另外,它不会很复杂)。不使用 ORM 是一种选择,但无论如何你都会遇到冗余问题。我什至要补充一点,你应该在你的代码中分离一些逻辑,这个方法似乎在做不同的事情(业务逻辑取决于输入和数据库访问)它不是很清楚(在第三个 if 之后,创建一个像 "getdistinctName()" 之类的)。
Santosh,一个快速的解决方法是将一个标志传递给您的 rowmapper,同时将其提供给 jdbcTemplate。我已经做了很多次以避免使用多个 rowmapper。
resultSet = (CustomerData) jdbcTemplate.queryForObject(str, new CustomerDataResponseMapper(1), custName, roadNumber);
对于上述变化,您需要使用默认构造函数重载构造函数。然后你需要使用你的标志,即 mapRow() 方法中的实例变量来分别处理每种情况。
根据您的查询逻辑,我看到在 get_rdf_explorer.get_rdf_locomotive_detail
中执行 sql 查询之前,有一些记录和 3 个获取必要(唯一)记录的选项是可能的:
- 来自
locomotive_id
- 来自
customer_name
和 road_number
- 任何记录(所有记录必须具有相同的
customer_name
,否则SQL distinct
没有任何条件return多于1行)
因此,在第 3 个选项中,您可以获得所有属性等于 NULL
和 NOT 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";`
您可以使用 BeanPropertyRowMapper
,它将直接映射目标 class 的字段名称。这里是javadoc。
名字可以直接匹配,也可以用"camel"大小写将下划线分隔的名字转换为同名。所以,你可以任意使用other classes 只要你想直接映射到 class。只需确保所选字段保留在目标 class 中。 默认或无参数构造函数。
以下示例使用 BeanPropertyRowMapper
获得 CustomerData
RowMapper<CustomerData> mapper = new BeanPropertyRowMapper<>(CustomerData.class);
List<CustomerData> result = jdbc.query("your query string...", mapper, query_args...);
所以,那么你可以 return 第一个对象或任何对象。
如果列数不固定,那么您应该根据其实现使用 ColumnMapRowMapper
,即使您不需要创建 RowMapper
的单独具体 class(即 CustomerDataResponseMapper
) 您只需要在查询中传递 ColumnMapRowMapper
的实例,如下所示:
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;
}
这更具可读性,删除样板代码,如果列名不在映射中,则不会抛出任何异常(如果列名不在映射中,它将简单地 returns null
)
参考ColumnMapRowMapper
:
这是我的模特class
//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 class
//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 会出现,而其他列则不会。
这将是一个很大的帮助。提前致谢。
我的建议是将您的 getCustomersData
分成三种不同的方法。如果您确实想忽略此建议,快速而肮脏的解决方案是保护 rowMapper 中的 rs.getString(...)
调用。像这样:
try {
customerData.setLocomotive_id(rs.getString("locomotive_id"));
} catch (SQLException e) {
// ignore this exception. DO NOT TRY THIS AT HOME!
}
您可以使用通用方法调查 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
,至少将其记录在 DEBUG
或 TRACE
级别,以防出现 SQLException
的不同子类型,因此它是没丢。
既然您已经有 3 个单独的查询,为什么不用 3 个单独的 RowMapper
,每个查询一个。您查询 "know" 哪些列 return,因此您可以轻松地为 RowMapper
创建那些 类。
如果您真的想要高保真解决方案,您可以为公共部分创建抽象基础 RowMapper
,为查询指定的部分创建 3 个子类。
而不是按条件获取列,您可以修改 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
所以每个查询都会有相同的列,您不必进行调整。如果您真的 want/can 不更改 rowMapper(或只想为此对象设置一个),这就是解决方案。
但老实说,我会选择 ikketu 的解决方案。您应该为第三个查询制作一个单独的映射器(另外,它不会很复杂)。不使用 ORM 是一种选择,但无论如何你都会遇到冗余问题。我什至要补充一点,你应该在你的代码中分离一些逻辑,这个方法似乎在做不同的事情(业务逻辑取决于输入和数据库访问)它不是很清楚(在第三个 if 之后,创建一个像 "getdistinctName()" 之类的)。
Santosh,一个快速的解决方法是将一个标志传递给您的 rowmapper,同时将其提供给 jdbcTemplate。我已经做了很多次以避免使用多个 rowmapper。 resultSet = (CustomerData) jdbcTemplate.queryForObject(str, new CustomerDataResponseMapper(1), custName, roadNumber);
对于上述变化,您需要使用默认构造函数重载构造函数。然后你需要使用你的标志,即 mapRow() 方法中的实例变量来分别处理每种情况。
根据您的查询逻辑,我看到在 get_rdf_explorer.get_rdf_locomotive_detail
中执行 sql 查询之前,有一些记录和 3 个获取必要(唯一)记录的选项是可能的:
- 来自
locomotive_id
- 来自
customer_name
和road_number
- 任何记录(所有记录必须具有相同的
customer_name
,否则SQL distinct
没有任何条件return多于1行)
因此,在第 3 个选项中,您可以获得所有属性等于 NULL
和 NOT 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";`
您可以使用 BeanPropertyRowMapper
,它将直接映射目标 class 的字段名称。这里是javadoc。
名字可以直接匹配,也可以用"camel"大小写将下划线分隔的名字转换为同名。所以,你可以任意使用other classes 只要你想直接映射到 class。只需确保所选字段保留在目标 class 中。 默认或无参数构造函数。
以下示例使用 BeanPropertyRowMapper
CustomerData
RowMapper<CustomerData> mapper = new BeanPropertyRowMapper<>(CustomerData.class);
List<CustomerData> result = jdbc.query("your query string...", mapper, query_args...);
所以,那么你可以 return 第一个对象或任何对象。
如果列数不固定,那么您应该根据其实现使用 ColumnMapRowMapper
,即使您不需要创建 RowMapper
的单独具体 class(即 CustomerDataResponseMapper
) 您只需要在查询中传递 ColumnMapRowMapper
的实例,如下所示:
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;
}
这更具可读性,删除样板代码,如果列名不在映射中,则不会抛出任何异常(如果列名不在映射中,它将简单地 returns null
)
参考ColumnMapRowMapper
: