Mapping a JDBC ResultSet to an object

前端 未结 8 1198
生来不讨喜
生来不讨喜 2020-12-04 18:14

I have a user class that has 16 attributes, things such as firstname, lastname, dob, username, password etc... These are all stored in a MySQL database and when I want to re

相关标签:
8条回答
  • 2020-12-04 18:28

    No need of storing resultSet values into String and again setting into POJO class. Instead set at the time you are retrieving.

    Or best way switch to ORM tools like hibernate instead of JDBC which maps your POJO object direct to database.

    But as of now use this:

    List<User> users=new ArrayList<User>();
    
    while(rs.next()) {
       User user = new User();      
       user.setUserId(rs.getString("UserId"));
       user.setFName(rs.getString("FirstName"));
      ...
      ...
      ...
    
    
      users.add(user);
    } 
    
    0 讨论(0)
  • 2020-12-04 18:28

    Complete solution using @TEH-EMPRAH ideas and Generic casting from Cast Object to Generic Type for returning

    import annotations.Column;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.sql.SQLException;
    import java.util.*;
    
    public class ObjectMapper<T> {
    
        private Class clazz;
        private Map<String, Field> fields = new HashMap<>();
        Map<String, String> errors = new HashMap<>();
    
        public DataMapper(Class clazz) {
            this.clazz = clazz;
    
            List<Field> 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<String, Object> row) throws SQLException {
            try {
                T dto = (T) clazz.getConstructor().newInstance();
                for (Map.Entry<String, Object> 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<T> map(List<Map<String, Object>> rows) throws SQLException {
            List<T> list = new LinkedList<>();
    
            for (Map<String, Object> row : rows) {
                list.add(map(row));
            }
    
            return list;
        }
    
        private T convertInstanceOfObject(Object o) {
            try {
                return (T) o;
            } catch (ClassCastException e) {
                return null;
            }
        }
    }
    

    and then in terms of how it ties in with the database, I have the following:

    // connect to database (autocloses)
    try (DataConnection conn = ds1.getConnection()) {
    
        // fetch rows
        List<Map<String, Object>> rows = conn.nativeSelect("SELECT * FROM products");
    
        // map rows to class
        ObjectMapper<Product> objectMapper = new ObjectMapper<>(Product.class);
        List<Product> 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();
    }
    
    0 讨论(0)
  • 2020-12-04 18:35

    Let's assume you want to use core Java, w/o any strategic frameworks. If you can guarantee, that field name of an entity will be equal to the column in database, you can use Reflection API (otherwise create annotation and define mapping name there)

    By FieldName

    /**
    
    Class<T> clazz - a list of object types you want to be fetched
    ResultSet resultSet - pointer to your retrieved results 
    
    */
    
        List<Field> fields = Arrays.asList(clazz.getDeclaredFields());
        for(Field field: fields) {
            field.setAccessible(true);
        }
    
        List<T> 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);
    
        }
    

    By annotation

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

    DTO:

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

    Same, but

        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);
    
        }
    

    Thoughts

    In fact, iterating over all Fields might seem ineffective, so I would store mapping somewhere, rather than iterating each time. However, if our T is a DTO with only purpose of transferring data and won't contain loads of unnecessary fields, that's ok. In the end it's much better than using boilerplate methods all the way.

    Hope this helps someone.

    0 讨论(0)
  • 2020-12-04 18:36

    If you don't want to use any JPA provider such as OpenJPA or Hibernate, you can just give Apache DbUtils a try.

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

    Then your code will look like this:

    QueryRunner run = new QueryRunner(dataSource);
    
    // Use the BeanListHandler implementation to convert all
    // ResultSet rows into a List of Person JavaBeans.
    ResultSetHandler<List<Person>> h = new BeanListHandler<Person>(Person.class);
    
    // Execute the SQL statement and return the results in a List of
    // Person objects generated by the BeanListHandler.
    List<Person> persons = run.query("SELECT * FROM Person", h);
    
    0 讨论(0)
  • 2020-12-04 18:46
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import org.json.simple.JSONObject;
    import com.google.gson.Gson;
    
    public class ObjectMapper {
    
    //generic method to convert JDBC resultSet into respective DTo class
    @SuppressWarnings("unchecked")
    public static Object mapValue(List<Map<String, Object>> rows,Class<?> className) throws Exception
    {
    
            List<Object> response=new ArrayList<>(); 
            Gson gson=new Gson();
    
            for(Map<String, Object> row:rows){
            org.json.simple.JSONObject jsonObject = new JSONObject();
            jsonObject.putAll(row);
            String json=jsonObject.toJSONString();
            Object actualObject=gson.fromJson(json, className);
            response.add(actualObject);
            }
            return response;
    
        }
    
        public static void main(String args[]) throws Exception{
    
            List<Map<String, Object>> rows=new ArrayList<Map<String, Object>>(); 
    
            //Hardcoded data for testing
            Map<String, Object> row1=new HashMap<String, Object>();
            row1.put("name", "Raja");
            row1.put("age", 22);
            row1.put("location", "India");
    
    
            Map<String, Object> row2=new HashMap<String, Object>();
            row2.put("name", "Rani");
            row2.put("age", 20);
            row2.put("location", "India");
    
            rows.add(row1);
            rows.add(row2);
    
    
            @SuppressWarnings("unchecked")
            List<Dto> res=(List<Dto>) mapValue(rows, Dto.class);
    
    
        }
    
        }
    
        public class Dto {
    
        private String name;
        private Integer age;
        private String location;
    
        //getters and setters
    
        }
    

    Try the above code .This can be used as a generic method to map JDBC result to respective DTO class.

    0 讨论(0)
  • 2020-12-04 18:47

    I would like to hint on q2o. It is a JPA based Java object mapper which helps with many of the tedious SQL and JDBC ResultSet related tasks, but without all the complexity an ORM framework comes with. With its help mapping a ResultSet to an object is as easy as this:

    while(rs.next()) {
        users.add(Q2Obj.fromResultSet(rs, User.class));
    }
    

    More about q2o can be found here.

    0 讨论(0)
提交回复
热议问题