tabla - mostrar datos de mysql en jcombobox java
AsignaciĆ³n de un conjunto de resultados JDBC a un objeto (6)
Tengo una clase de usuario que tiene 16 atributos, como el nombre, el apellido, el dob, el nombre de usuario, la contraseña, etc. Todos estos están almacenados en una base de datos MySQL y cuando quiero recuperar usuarios, uso un ResultSet. Quiero asignar cada una de las columnas a los atributos del usuario, pero la forma en que lo estoy haciendo parece terriblemente ineficiente. Por ejemplo estoy haciendo:
//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);
}
es decir, recupero todas las columnas y luego creo objetos de usuario insertando todos los valores de columna en el constructor Usuario.
¿Alguien sabe de una manera más rápida y ordenada de hacer esto?
¿Es posible? intente obtener resultMap definido en el xml de Mybatis y luego convierta el ResultSet nativo de JDBC a la lista mediante alguna función MyBatis
No es necesario almacenar los valores de resultSet en String y nuevamente en la clase POJO. En su lugar, establezca en el momento en que está recuperando.
O la mejor manera de cambiar a herramientas ORM como hibernar en lugar de JDBC que mapea su objeto POJO directamente a la base de datos.
Pero a partir de ahora usa esto:
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);
}
Si no desea utilizar ningún proveedor de JPA como openJPA o hibernate, solo puede probar Apache dbutils.
http://commons.apache.org/proper/commons-dbutils/examples.html
entonces tu código se verá así
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);
Solución completa utilizando las ideas de @ TEH-EMPRAH y el lanzamiento genérico de objeto fundido a tipo genérico para regresar
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;
}
}
}
y luego, en términos de cómo se relaciona con la base de datos, tengo lo siguiente:
// 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();
}
Supongamos que desea utilizar el núcleo de Java, sin ningún marco estratégico. Si puede garantizar que el nombre de campo de una entidad será igual a la columna en la base de datos, puede usar la API de Reflection (de lo contrario, crear una anotación y definir el nombre del mapeo allí)
Por nombre de campo
/**
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);
}
Por anotación
@Retention(RetentionPolicy.RUNTIME)
public @interface Col {
String name();
}
DTO:
class SomeClass {
@Col(name = "column_in_db_name")
private String columnInDbName;
public SomeClass() {}
// ..
}
Igual pero
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);
}
Pensamientos
De hecho, la iteración en todos los campos puede parecer ineficaz, por lo que almacenaría el mapeo en algún lugar, en lugar de iterar cada vez. Sin embargo, si nuestro T
es un DTO con el único propósito de transferir datos y no contendrá muchos campos innecesarios, está bien. Al final, es mucho mejor que usar los métodos repetitivos todo el tiempo.
Espero que esto ayude a alguien.
Use el Tamaño de recuperación de la declaración, si está recuperando más cantidad de registros. Me gusta esto.
Statement statement = connection.createStatement();
statement.setFetchSize(1000);
Aparte de eso, no veo un problema con la forma en que lo está haciendo en términos de rendimiento
En términos de Neat. Siempre use un delegado de método separado para asignar el conjunto de resultados al objeto POJO. el cual puede ser reutilizado posteriormente en la misma clase.
me gusta
private User mapResultSet(ResultSet rs){
User user = new User();
// Map Results
return user;
}
Si tiene el mismo nombre para columnName y fieldName del objeto, también puede escribir la utilidad de reflexión para cargar los registros de nuevo en POJO. y usa MetaData para leer los nombres de columna. pero para proyectos de pequeña escala, la reflexión no es un problema. Pero como dije antes, no hay nada de malo en la forma en que lo estás haciendo.