将一行从 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 库。它将为您管理连接(不再关闭 ConnectionStatementResultSet)并具有许多便利,包括行映射。

我们已经使用 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 对象?

3.Hibernate 映射类型

如何将 spark sql 查询结果映射到对象?

springboot+kotlin+gradle+hibernate学习笔记

对象关系映射(ORM)