将JDBC ResultSet映射到对象

我有一个具有16个属性的用户类,如firstname,lastname,dob,username,password等…这些都存储在MySQL数据库中,当我想要检索用户时,我使用ResultSet。 我想将每个列映射回用户属性,但我这样做的方式看起来非常低效。 例如我在做:

//ResultSet rs; while(rs.next()) { String uid = rs.getString("UserId"); String fname = rs.getString("FirstName"); ... ... ... User u = new User(uid,fname,...); //ArrayList users users.add(u); } 

即我检索所有列,然后通过将所有列值插入到用户构造函数中来创建用户对象。

有谁知道更快,更整洁,这样做的方式?

无需将resultSet值存储到String中并再次设置为POJO类。 而是在您检索时设置。

或者最好的方法是切换到像hibernate这样的ORM工具,而不是将POJO对象直接映射到数据库的JDBC。

但截至目前使用此:

 List users=new ArrayList(); while(rs.next()) { User user = new User(); user.setUserId(rs.getString("UserId")); user.setFName(rs.getString("FirstName")); ... ... ... users.add(user); } 

如果您不想使用任何JPA提供程序(如openJPA或hibernate),您可以尝试使用apache dbutils。

http://commons.apache.org/proper/commons-dbutils/examples.html

那么你的代码看起来就像这样

 QueryRunner run = new QueryRunner(dataSource); // Use the BeanListHandler implementation to convert all // ResultSet rows into a List of Person JavaBeans. ResultSetHandler> h = new BeanListHandler(Person.class); // Execute the SQL statement and return the results in a List of // Person objects generated by the BeanListHandler. List persons = run.query("SELECT * FROM Person", h); 

假设你想要使用核心Java,没有任何战略框架。 如果可以保证,实体的字段名称将等于数据库中的列,则可以使用Reflection API(否则创建注释并在那里定义映射名称)

按FieldName

 /** Class clazz - a list of object types you want to be fetched ResultSet resultSet - pointer to your retrieved results */ List fields = Arrays.asList(clazz.getDeclaredFields()); for(Field field: fields) { field.setAccessible(true); } List list = new ArrayList<>(); while(resultSet.next()) { T dto = clazz.getConstructor().newInstance(); for(Field field: fields) { String name = field.getName(); try{ String value = resultSet.getString(name); field.set(dto, field.getType().getConstructor(String.class).newInstance(value)); } catch (Exception e) { e.printStackTrace(); } } list.add(dto); } 

通过注释

 @Retention(RetentionPolicy.RUNTIME) public @interface Col { String name(); } 

DTO:

 class SomeClass { @Col(name = "column_in_db_name") private String columnInDbName; public SomeClass() {} // .. } 

相同,但是

  while(resultSet.next()) { T dto = clazz.getConstructor().newInstance(); for(Field field: fields) { Col col = field.getAnnotation(Col.class); if(col!=null) { String name = col.name(); try{ String value = resultSet.getString(name); field.set(dto, field.getType().getConstructor(String.class).newInstance(value)); } catch (Exception e) { e.printStackTrace(); } } } list.add(dto); } 

思考

事实上,遍历所有字段可能看起来无效,所以我会将映射存储在某个地方,而不是每次都进行迭代。 但是,如果我们的T是一个仅用于传输数据的DTO,并且不包含大量不必要的字段,那就没问题。 最后,它比使用样板方法要好得多。

希望这有助于某人。

完整的解决方案使用@TEH-EMPRAH创意和从Cast对象到Generic Type的通用投射返回

 import annotations.Column; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.sql.SQLException; import java.util.*; public class ObjectMapper { private Class clazz; private Map fields = new HashMap<>(); Map errors = new HashMap<>(); public DataMapper(Class clazz) { this.clazz = clazz; List fieldList = Arrays.asList(clazz.getDeclaredFields()); for (Field field : fieldList) { Column col = field.getAnnotation(Column.class); if (col != null) { field.setAccessible(true); fields.put(col.name(), field); } } } public T map(Map row) throws SQLException { try { T dto = (T) clazz.getConstructor().newInstance(); for (Map.Entry entity : row.entrySet()) { if (entity.getValue() == null) { continue; // Don't set DBNULL } String column = entity.getKey(); Field field = fields.get(column); if (field != null) { field.set(dto, convertInstanceOfObject(entity.getValue())); } } return dto; } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) { e.printStackTrace(); throw new SQLException("Problem with data Mapping. See logs."); } } public List map(List> rows) throws SQLException { List list = new LinkedList<>(); for (Map row : rows) { list.add(map(row)); } return list; } private T convertInstanceOfObject(Object o) { try { return (T) o; } catch (ClassCastException e) { return null; } } } 

然后就它如何与数据库联系而言,我有以下几点:

 // connect to database (autocloses) try (DataConnection conn = ds1.getConnection()) { // fetch rows List> rows = conn.nativeSelect("SELECT * FROM products"); // map rows to class ObjectMapper objectMapper = new ObjectMapper<>(Product.class); List products = objectMapper.map(rows); // display the rows System.out.println(rows); // display it as products for (Product prod : products) { System.out.println(prod); } } catch (Exception e) { e.printStackTrace(); } 

如果要检索更多记录,请使用“语句提取大小”。 喜欢这个。

 Statement statement = connection.createStatement(); statement.setFetchSize(1000); 

除此之外,我没有看到你在表现方面的问题

就整洁而言。 始终使用单独的方法委托将结果集映射到POJO对象。 这可以在以后的同一个类中重用

喜欢

 private User mapResultSet(ResultSet rs){ User user = new User(); // Map Results return user; } 

如果columnName和object的fieldName具有相同的名称,则还可以编写reflection实用程序以将记录加载回POJO。 并使用MetaData读取columnNames。 但对于使用reflection的小规模项目来说不是问题。 但正如我之前所说,你的行为方式没有任何问题。

可能吗? 尝试在Mybatis的xml中定义resultMap,然后通过一些MyBatis函数将JDBC本机ResultSet转换为List