
net.dongliu.dbutils.handlers.BeanRowProcessor Maven / Gradle / Ivy
package net.dongliu.dbutils.handlers;
import net.dongliu.commons.reflect.BeanProperty;
import net.dongliu.commons.reflect.ClassConstructor;
import net.dongliu.commons.reflect.JavaClass;
import net.dongliu.dbutils.RowProcessor;
import net.dongliu.dbutils.mapping.BeanMapping;
import net.dongliu.dbutils.mapping.BeanMappingUtils;
import java.sql.*;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
/**
*
* BeanRowProcessor matches column names to bean property names
* and converts ResultSet columns into objects for those bean
* properties. Subclasses should override the methods in the processing chain
* to customize behavior.
*
*
* This class is not thread-safe.
*
*/
public class BeanRowProcessor implements RowProcessor {
private final Class type;
private final ClassConstructor constructor;
/**
* ResultSet column to bean property name overrides.
*/
private final Map columnToPropertyOverrides;
/**
* column start with index 1
*/
private BeanProperty[] columnToProperty;
/**
* Constructor for BeanRowProcessor.
*/
public BeanRowProcessor(Class type) {
this(type, Collections.emptyMap());
}
/**
* Constructor for BeanRowProcessor configured with column to property name overrides.
*
* @param columnToPropertyOverrides ResultSet column to bean property name overrides
*/
public BeanRowProcessor(Class type, Map columnToPropertyOverrides) {
this.type = type;
this.columnToPropertyOverrides = Objects.requireNonNull(columnToPropertyOverrides);
this.constructor = JavaClass.of(type).getConstructor();
}
private void init(ResultSet rs) throws SQLException {
BeanMapping beanMapping = BeanMappingUtils.getBeanMapping(type);
ResultSetMetaData rsmd = rs.getMetaData();
this.columnToProperty = this.mapColumnsToProperties(rsmd, beanMapping);
}
@Override
public T convert(ResultSet rs, int row) throws SQLException {
if (row == 1) {
init(rs);
}
T bean = constructor.construct();
for (int i = 1; i < columnToProperty.length; i++) {
BeanProperty property = columnToProperty[i];
this.processColumn(rs, i, bean, property);
}
return bean;
}
/**
* Map resultSet columns to bean properties
*
* @param metaData The ResultSetMetaData containing column information.
* @throws SQLException if a database access error occurs
*/
private BeanProperty[] mapColumnsToProperties(ResultSetMetaData metaData, BeanMapping beanMapping)
throws SQLException {
final int count = metaData.getColumnCount();
final BeanProperty[] properties = new BeanProperty[count + 1];
for (int col = 1; col <= count; col++) {
String columnName = metaData.getColumnLabel(col);
if (columnName == null || columnName.isEmpty()) {
columnName = metaData.getColumnName(col);
}
String propertyName = columnName.toLowerCase();
String overrideName = columnToPropertyOverrides.get(propertyName);
if (overrideName != null) {
propertyName = overrideName;
}
BeanProperty property = beanMapping.getProperty(propertyName);
if (property == null) {
throw new SQLException("Bean property not found for column " + columnName + ", mapping to: "
+ propertyName);
}
properties[col] = property;
}
return properties;
}
/**
* Convert a ResultSet column into an object. Simple
* implementations could just call rs.getObject(index) while
* more complex implementations could perform type manipulation to match
* the column's type to the bean property type.
*
*
* This implementation calls the appropriate ResultSet getter
* method for the given property type to perform the type conversion. If
* the property type doesn't match one of the supported
* ResultSet types, getObject is called.
*
*
* @param rs The ResultSet currently being processed. It is
* positioned on a valid row before being passed into this method.
* @param index The current column index being processed.
* @throws SQLException if a database access error occurs
*/
private void processColumn(ResultSet rs, int index, Object bean, BeanProperty property)
throws SQLException {
Class> propertyType = property.type();
if (propertyType == String.class) {
property.set(bean, rs.getString(index));
} else if (propertyType == int.class) {
property.setInt(bean, rs.getInt(index));
} else if (propertyType == boolean.class) {
property.setBoolean(bean, rs.getBoolean(index));
} else if (propertyType == long.class) {
property.setLong(bean, rs.getLong(index));
} else if (propertyType == double.class) {
property.setDouble(bean, rs.getDouble(index));
} else if (propertyType == float.class) {
property.setFloat(bean, rs.getFloat(index));
} else if (propertyType == short.class) {
property.setShort(bean, rs.getShort(index));
} else if (propertyType == byte.class) {
property.setByte(bean, rs.getByte(index));
} else if (propertyType == byte[].class) {
property.set(bean, rs.getBytes(index));
} else if (propertyType == Timestamp.class) {
property.set(bean, rs.getTimestamp(index));
} else if (propertyType == Integer.class) {
int value = rs.getInt(index);
if (rs.wasNull()) {
property.set(bean, null);
} else {
property.set(bean, value);
}
} else if (propertyType == Boolean.class) {
boolean value = rs.getBoolean(index);
if (rs.wasNull()) {
property.set(bean, null);
} else {
property.set(bean, value);
}
} else if (propertyType == Long.class) {
long value = rs.getLong(index);
if (rs.wasNull()) {
property.set(bean, null);
} else {
property.set(bean, value);
}
} else if (propertyType == Double.class) {
double value = rs.getDouble(index);
if (rs.wasNull()) {
property.set(bean, null);
} else {
property.set(bean, value);
}
} else if (propertyType == Float.class) {
float value = rs.getFloat(index);
if (rs.wasNull()) {
property.set(bean, null);
} else {
property.set(bean, value);
}
} else if (propertyType == Short.class) {
short value = rs.getShort(index);
if (rs.wasNull()) {
property.set(bean, null);
} else {
property.set(bean, value);
}
} else if (propertyType == byte.class) {
byte value = rs.getByte(index);
if (rs.wasNull()) {
property.set(bean, null);
} else {
property.set(bean, value);
}
} else if (propertyType == Timestamp.class) {
property.set(bean, rs.getTimestamp(index));
} else if (propertyType == java.sql.Date.class) {
property.set(bean, rs.getDate(index));
} else if (propertyType == java.sql.Time.class) {
property.set(bean, rs.getTime(index));
} else if (propertyType == SQLXML.class) {
property.set(bean, rs.getSQLXML(index));
} else if (propertyType.getClass().isEnum()) {
String str = rs.getString(index);
if (str == null) {
property.set(bean, null);
} else {
property.set(bean, Enum.valueOf(propertyType.asSubclass(Enum.class), str));
}
} else if (propertyType == Instant.class) {
Timestamp timestamp = rs.getTimestamp(index);
if (timestamp != null) {
property.set(bean, timestamp.toInstant());
} else {
property.set(bean, null);
}
} else if (propertyType == LocalDateTime.class) {
Timestamp timestamp = rs.getTimestamp(index);
if (timestamp != null) {
property.set(bean, timestamp.toLocalDateTime());
} else {
property.set(bean, null);
}
} else if (propertyType == LocalDate.class) {
Date date = rs.getDate(index);
if (date != null) {
property.set(bean, date.toLocalDate());
} else {
property.set(bean, null);
}
} else if (propertyType == LocalTime.class) {
Time time = rs.getTime(index);
if (time != null) {
property.set(bean, time.toLocalTime());
} else {
property.set(bean, null);
}
} else {
Object value = rs.getObject(index);
property.set(bean, value);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy