如何使用apache-commons-dbutils将数据库列名映射到Java类字段

时间:2015-04-24 13:46:41

标签: java jdbc apache-commons-dbutils

我想使用Apache DBUtils库从DB填充POJO(State.java)。但是,由于Bean属性的名称与DB列名称不完全匹配,因此某些属性未填充。

现在,我通过谷歌搜索做了一些研究,发现这可以通过以下方式实现:

  1. 编写SQL查询时的列别名(我不喜欢,因为我在一些较大的表中有多个连接,因此需要大量别名)
  2. 使用BeanProcessor(无法在任何地方找到一个非常好的例子)
  3. 有人能提供一个很好的例子来说明如何使用BeanProcessor将列名映射到属性吗?调整这个例子,我会更好。

    数据库表

    CREATE TABLE public.states (
      state_id INTEGER DEFAULT nextval('states_seq'::regclass) NOT NULL,
      state_cd VARCHAR(2) NOT NULL,
      name VARCHAR(100) NOT NULL,
      tax_pct NUMERIC(10,2) DEFAULT 0.00 NOT NULL,
      active CHAR(1) DEFAULT 'Y'::bpchar NOT NULL,
    ) 
    

    State.java

      public class State implements Serializable {
    
        private int stateId;
        private String stateCode;
        private String name;
        private BigDecimal taxPct = new BigDecimal(0);
        private Date expiryDate;
        private String createdBy;
        private Date createdOn;
        private String active;
    
        //getters and setters here
    }
    

    Main.java

        public class Main {
    
        public static void main(String[] args) {
            String url = "jdbc:postgresql://gsi-547576.gsiccorp.net:5432/istore-db";
            String driver = "org.postgresql.Driver";
            String user = "postgres";
            String pwd = "postgres";
            Connection conn = null;
            List<State> states = null;
    
            try {
                DbUtils.loadDriver(driver);
                conn = DriverManager.getConnection(url, user, pwd);
    
                states = (List<State>) new QueryRunner().query(conn, "select * from states a where a.active='Y'", new BeanListHandler(State.class);
    
                System.out.println("states::  " + states);
    
            } catch (SQLException ex) {
                ex.printStackTrace();
            } finally {
                DbUtils.closeQuietly(conn);
            }
        }
    
    }
    

5 个答案:

答案 0 :(得分:3)

Map<String,String> mapColumnsToProperties = new HashMap<>();
//mapping you database to entity here;
mapColumnsToProperties.put("database_column","entity_property");
BeanProcessor beanProcessor = new BeanProcessor(mapColumnsToProperties);
RowProcessor rowProcessor = new BasicRowProcessor( beanProcessor); 
ResultSetHandler<List<Entity>> resultSetHandler = new BeanListHandler<Entity>(Entity.class,rowProcessor);
List<Entity> entityLists = queryRunner.query(findListSQL, resultSetHandler);    

答案 1 :(得分:2)

我来自中国,我的英语不是很好; 但是这个问题,这是我的决心: 因为dbutils是开源的,你可以修改源代码,并使用maven只为你创建一个jar,我只修改了BeanProcessor类,你可以像这样添加一个名为changeColumnName的方法:

public  String changeColumnName(String columnName){
        if(columnName == null){
            return null;
        }
        if(columnName.contains("_")){
            char[] cs = columnName.toCharArray();
            int flag = -1;
            for(int index=0;index<columnName.toCharArray().length;index++){
                if(cs[index] == '_'){
                    flag = index;
                    break;
                }
            }
            columnName = columnName.substring(0, flag) + columnName.substring(flag+1,flag+2).toUpperCase() + columnName.substring(flag+2);
            return changeColumnName(columnName);
        }else{
            return columnName;
        }
    }

并在方法中

protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,
        PropertyDescriptor[] props) throws SQLException {

    int cols = rsmd.getColumnCount();
    int[] columnToProperty = new int[cols + 1];
    Arrays.fill(columnToProperty, PROPERTY_NOT_FOUND);

    for (int col = 1; col <= cols; col++) {
        String columnName = rsmd.getColumnLabel(col);
        if (null == columnName || 0 == columnName.length()) {
          columnName = rsmd.getColumnName(col);
        }
        String propertyName = columnToPropertyOverrides.get(columnName);
        if (propertyName == null) {
            propertyName = changeColumnName(columnName);//add here
        }
        for (int i = 0; i < props.length; i++) {

            if (propertyName.equalsIgnoreCase(props[i].getName())) {
                columnToProperty[col] = i;
                break;
            }
        }
    }

    return columnToProperty;
}

它可以解决问题。欢迎与我讨论。我的谷歌电子邮件是guomin.bai@gmail.com

答案 2 :(得分:1)

如果你看一下BeanProcessor的Java文档:

  

protected int [] mapColumnsToProperties(ResultSetMetaData rsmd,                              PropertyDescriptor []道具)                                   抛出SQLException

     

返回数组中的位置代表列号。每个存储的值   position表示bean的PropertyDescriptor []中的索引   与列名匹配的属性。如果没有找到bean属性   对于列,位置设置为PROPERTY_NOT_FOUND。参数:   rsmd - 包含列信息的ResultSetMetaData。道具 -   bean属性描述符。返回:带有列索引的int []   属性索引映射。第0个元素没有意义,因为JDBC   列索引从1开始。

看起来您需要创建一个从BeanProcessor扩展的类并覆盖mapColumnsToProperties方法,如下所示:

public class StateBeanProcessor extends BeanProcessor {

    @Override
    protected  int[] mapColumnsToProperties(ResultSetMetaData rsmd, PropertyDescriptor[] props) throws SQLException {
          int[] mapping = super.mapColumnsToProperties(rsmd, props);
          /*Map database columns to fields in the order in which they appear
            1st column in the DB will be mapped to 1st field in the Java
            class and so on.. */
          for(int i=0;i<mapping.length;++i) {
             mapping[i]=i;
          }
      }
  }

然后,您可以将上述StateBeanProcessor插入到您的代码中,如下所示:

states = (List<State>) new QueryRunner().query(conn, "select * from states", new BeanListHandler(State.class,new BasicRowProcessor(new StateBeanProcessor())));

免责声明:我尚未测试此代码。它旨在向您展示您可以组合在一起以获得自定义字段映射的各个部分。如果您发现它有问题,可以告诉我,以便我可以查看。

答案 3 :(得分:1)

我已经回答了类似的so question here

您可以使用GenerousBeanProcessor这是BeanProcessor的子类,它会忽略下划线和&amp;列名称的区分大小写。您不必为此特定情况实施自己的自定义BeanProcessor

GenerousBeanProcessor commons-dbutils 的1.6版本开始提供。

用法:

// TODO initialize
QueryRunner queryRunner = null;

ResultSetHandler<List<State>> resultSetHandler =
                new BeanListHandler<State>(State.class, new BasicRowProcessor(new GenerousBeanProcessor()));

// best practice is mentioning only required columns in the query
final List<State> states = queryRunner.query("select * from states a where a.active='Y'", resultSetHandler);

for (State state : states) {
    System.out.println(state.getStateId());
}

答案 4 :(得分:0)

这可以使用 MapListHandler 和 Jackson 轻松完成,无需定义自定义 bean 处理器或提供列映射。

  1. 将您的 POJO 更改为

     public class State implements Serializable {
    
     @JsonProperty("state_id")//db column name
     private int stateId;
    
     @JsonProperty("state_cd")
     private String stateCode;
    
     //and so on ....
    

    }

  2. 使用 MapListHandler 和 Jackson 获取响应并将其映射到 POJO。

ObjectMapper objectMapper = new ObjectMapper();
List<State> stateList = new QueryRunner().query(connection, query, new MapListHandler())
.stream().map(response -> objectMapper.convertValue(response, State.class))
.collect(Collectors.toList());

在这种方法中,您首先获取 List> 这是一个记录列表,每个记录都来自列名和值的映射,可以使用 Jackson 的 convertValue 将其映射到 POJO方法(将使用@JsonProperty 注解将列映射到 POJO 字段)。

这种方法可以让您更好地控制将 DB 列映射到 POJO 字段。