我有一个具有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<User> users
users.add(u);
}
即检索所有列,然后通过将所有列值插入User构造函数来创建用户对象。
有没有人知道更快,更整洁的方式呢?
答案 0 :(得分:40)
如果您不想使用任何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<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);
答案 1 :(得分:12)
无需将resultSet值存储到String中并再次设置为POJO类。而是在您检索时设置。
或者最好的方法是切换到像hibernate这样的ORM工具,而不是将POJO对象直接映射到数据库的JDBC。
但截至目前使用此:
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);
}
答案 2 :(得分:7)
假设你想要使用核心Java,没有任何战略框架。如果可以保证,实体的字段名称将等于数据库中的列,则可以使用Reflection API(否则创建注释并在那里定义映射名称)
按字段名称
/**
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);
}
按注释
@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,仅用于传输数据,并且不包含大量不必要的字段,那就没问题。最后,它比使用样板方法要好得多。
希望这有助于某人。
答案 3 :(得分:3)
使用@ {3}}
中的@ TEH-EMPRAH构思和通用演员来完成解决方案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;
}
}
}
然后就它与数据库的关系而言,我有以下几点:
// 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();
}
答案 4 :(得分:0)
我知道发问者正在使用MySQL,但是由于许多没有MySQL限制但有相同问题的人都会登陆此页面,因此我想对q2o进行提示。它是一个基于JPA的Java对象映射器,可以帮助完成许多繁琐的SQL和JDBC ResultSet相关任务,但没有ORM框架所带来的所有复杂性。借助它的帮助,将ResultSet映射到对象就像这样简单:
while(rs.next()) {
users.add(Q2Obj.fromResultSet(rs, User.class));
}
答案 5 :(得分:0)
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
}
尝试上面的代码。这可以用作将JDBC结果映射到相应DTO类的通用方法。
答案 6 :(得分:-1)
如果要检索更多记录,请使用“语句提取大小”。像这样。
Statement statement = connection.createStatement();
statement.setFetchSize(1000);
除此之外,我没有看到你在表现方面的问题
就整洁而言。始终使用单独的方法委托将结果集映射到POJO对象。这可以在以后的同一类中重复使用
喜欢
private User mapResultSet(ResultSet rs){
User user = new User();
// Map Results
return user;
}
如果columnName和object的fieldName具有相同的名称,您还可以编写反射实用程序以将记录加载回POJO。并使用MetaData读取columnNames。但对于使用反射的小规模项目来说不是问题。但正如我之前所说,你的方式没有任何问题。
答案 7 :(得分:-1)
使用DbUtils ...
我对该lib的唯一问题是,有时您的bean类中有关系,而DBUtils不会映射它。它仅映射Bean类中的属性,如果您还有其他复杂的属性(由于数据库关系而引用其他Bean),则必须像我所说的那样创建“间接设置器”,这些设置器会将值放入那些复杂的设置器中属性的属性。