将一行从 SQL 数据映射到 Java 对象
Posted
技术标签:
【中文标题】将一行从 SQL 数据映射到 Java 对象【英文标题】:Mapping a row from a SQL data to a Java object 【发布时间】:2012-01-24 19:56:13 【问题描述】:我有一个带有与 SQL 数据库表的列名匹配的实例字段(和匹配的 setter 方法)的 Java 类。我想优雅地从表中提取一行(到 ResultSet 中)并将其映射到此类的一个实例。
例如:
我有一个“Student”类,其中包含实例字段“FNAME”、“LNAME”、“GRADE”以及每个相应的 getter 和 setter 方法。
我还有一个包含三列同名的 SQL 表。
现在我正在做这样的事情:
rs = statement.executeQuery(query);
Student student = new Student();
student.setFNAME(rs.getString("FNAME"));
student.setLNAME(rs.getString("LNAME"));
student.setGRADE(rs.getString("GRADE"));
必须有一种不那么冗长的方法,对吧?当我添加列时,这可能会变得非常烦人和混乱。
【问题讨论】:
您还可以使用 SpringJDBC,您必须创建 RowMappers、为您的表和 DAO 创建模型类以处理数据。在很多情况下,我发现 SpringJDBC 比 hibernate 更易于使用,但这取决于我想在这一点上的一个习惯。给你vaannila.com/spring/spring-jdbc-tutorial-1.html 【参考方案1】:我建议使用Spring JDBC。您不需要使用 Spring 的其余部分来使用他们的 JDBC 库。它将为您管理连接(不再关闭 Connection
、Statement
或 ResultSet
)并具有许多便利,包括行映射。
我们已经使用 Spring JDBC 轻松地改造了遗留代码。
这里是 Spring JDBC 概述的演示文稿 (PDF)。它已经有几年历史了,但它的工作原理基本相同,即使没有让 Spring 注入依赖项。
Spring JDBC Presentation PDF
【讨论】:
【参考方案2】:您可以通过以下简单方法进行一般性操作:
用作方法指针的接口:
public interface I_DBtoJavaObjectConvertable<T>
public T createFromDB(ResultSet i_rs) throws SQLException;
处理从 SQL 到 java 对象的每个映射的通用类:
public class DBManager
static volatile Connection conn;
//set here a static c'tor to handle the connection to the database
//The General generic method:
public static <T> List<T> GetObjectsFromDB(String i_Query, I_DBtoJavaObjectConvertable i_Converter)
List<T> ResList = new ArrayList<>();
try
Statement st = conn.createStatement();
for (ResultSet rs = st.executeQuery(i_Query); rs.next();)
ResList.add((T) i_Converter.createFromDB(rs));
catch (SQLException ex)
_LOG_ERROR(ex.getMessage());
return ResList;
现在通过使用 Lanbda 表达式,可以通过给定的转换方法轻松地将 sql 行转换为对象,例如:
public static User FetchUserFromDB(ResultSet i_rs)
User userToCreate = null;
try
String FirstName = i_rs.getString("FirstName");
String LastName = i_rs.getString("LastName");
String Password = i_rs.getString("Password");
userToCreate = new User(FirstName, LastName, Password);
catch (SQLException ex)
_LOG_ERROR("Error in fetching user from DB: \n" + ex.getMessage());
return userToCreate;
现在你可以使用这个方法来带来任何你想要的用户:
public static List<User> GetAllUsersFromDB() throws SQLException
String Query = "select * "
+ "from UsersTable";
return DBManager.GetObjectsFromDB(Query, rs -> FetchUserFromDB(rs));
或者:
public static List<String> GetAllNamesFromDB() throws SQLException
String Query = "select FirstName "
+ "from UsersTable";
return DBManager.GetObjectsFromDB(Query, rs -> rs.getString("FirstName"));
【讨论】:
我真的很喜欢这个。【参考方案3】:您可以使用类似 JPA 提供程序之一的 ORM,例如Hibernate。这使您可以设置对象和表之间的映射。
【讨论】:
【参考方案4】:如果您使用 JDBC,它就是这样工作的。如果你想避免在 Java 中添加这样的列,你可以考虑使用一些 ORM 框架。
【讨论】:
【参考方案5】:一个稍微不那么冗长的方法是给Student
一个接受3个字符串的构造函数。然后你可以这样做:
Student student = new Student(rs.getString("FNAME"), rs.getString("LNAME"), rs.getString("GRADE"));
另一种方法是使用像 Hibernate 这样的 ORM,但 Hibernate 只值得为处理大量表的真正大型项目进行大量设置工作。
【讨论】:
这是一个内部使用的网络应用程序,大约有 2-3 人使用,可能涉及 5 张桌子。值得努力吗? @Martin 您必须将所有表中的每个表和每一列都告诉 Hibernate。您还必须指定列类型和其他一些东西。 Hibernate 对于映射一对多/一对一关联变得非常有用。如果您只是从数据库中加载非常简单的数据,那可能不值得。【参考方案6】:有许多 ORM 库可以简化或消除 JDBC 的苦差事。有关一些示例,请参阅Source Forge ORM。我喜欢我的库sormula,因为它可以以最少的配置使用。
【讨论】:
【参考方案7】:如果您不想使用任何其他框架,您可以创建标准映射方法并在每个 Result 之后使用它。
public class CurrencyDAO()
public Currency findById(int id)
String sql = "SELECT * FROM CCR.CURRENCY WHERE id = ?";
Currency currency = null;
Connection c = null;
try
c = DBConnection.getConnection();
PreparedStatement ps = c.prepareStatement(sql);
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
if (rs.next())
currency = processRow(rs);
catch (Exception e)
e.printStackTrace();
throw new RuntimeException(e);
finally
DBConnection.close(c);
return currency;
protected Currency processRow(ResultSet rs) throws SQLException
Currency currency = new Currency();
currency.setId(rs.getInt("id"));
currency.setEUR(rs.getString("EUR"));
currency.setUSD(rs.getString("USD"));
currency.setRate(rs.getString("rate"));
return currency;
【讨论】:
【参考方案8】:试试q2o。它是一个基于 JPA 的对象映射器,可帮助您完成许多繁琐的 SQL 和 JDBC ResultSet 相关任务,但没有 ORM 框架所带来的所有复杂性。
将 Student 类绑定到其对应的表:
@Table(name = "STUDENTS")
public class Student (
private String FNAME;
private String LNAME;
private String GRADE;
...
)
按年级选择一些学生:
List<Student> students = Q2ObjList.fromClause(Student.class, "GRADE = ?", grade);
更改学生的成绩并将更改保存到数据库:
student.setGRADE(grade);
Q2obj.update(student);
即使您依赖 Spring JDBC,q2o 也很有帮助:
jdbcTemplate.queryForObject("...", new RowMapper<Student>()
@Override
public Student mapRow(final ResultSet rs, final int rowNum) throws SQLException
return Q2Obj.fromResultSet(rs, Student.class);
);
这很容易,不是吗? Find more about q2o here.
【讨论】:
【参考方案9】:当您执行查询时,您可以从 ResultSet 中获取元数据。您可以从此访问列。这是一个例子:
@RestController
public class MyController
@GetMapping("/characters")
public List<Payload> characters()
List<Payload> results = new ArrayList<>();
try (Connection conn = new Connection())
conn.makeConnection();
Statement stmt = conn.createStatement();
ResultSet result = stmt.executeQuery("SELECT * FROM public.hello;");
ResultSetMetaData resultMetaData = result.getMetaData();
Set<String> columns = new HashSet<>();
for (int i = 1; i <= resultMetaData.getColumnCount(); i++)
columns.add(resultMetaData.getColumnName(i));
while (result.next())
results.add(new Data(result, columns));
catch (Exception e)
results.add(new Fail("404", e.getMessage()));
return results;
public class Data implements Payload
private final Map<String, Object> data = new HashMap<>();
public Data(ResultSet result, Set<String> columns) throws SQLException
for (String column : columns)
data.put(column, result.getString(column));
public Map<String, Object> getData()
return data;
现在您可以拥有一个类对象来解析任何表的列和数据。您永远不会真正关心有哪些列。不利的一面是,您的所有信息现在都存储在数据字段中。所以有效载荷看起来像:
[
"data":"id":"1","name":"Rick Sanchez",
"data":"id":"2","name":"Morty Smith",
"data":"id":"3","message":"Summer Smith"
]
【讨论】:
以上是关于将一行从 SQL 数据映射到 Java 对象的主要内容,如果未能解决你的问题,请参考以下文章
将 SQL(不是 JPQL)映射到简单 Java 对象的集合?
如何使用 Hibernate 将 SQL 查询的结果最好地映射到非实体 Java 对象?