All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.dongliu.dbutils.mapper.BeanRowMapper Maven / Gradle / Ivy

package net.dongliu.dbutils.mapper;

import net.dongliu.commons.collection.Sets;
import net.dongliu.dbutils.exception.MissingPropertyException;
import net.dongliu.dbutils.exception.ReflectionException;
import net.dongliu.dbutils.mapping.BeanMapping;
import net.dongliu.dbutils.mapping.Property;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.*;
import java.time.*;
import java.util.HashSet;
import java.util.Set;

/**
 * Convert row to bean
 *
 * @param 
 */
public class BeanRowMapper implements RowMapper {
    private final Class cls;
    private final boolean requireAllColumns;
    private final Constructor constructor;
    private final BeanMapping beanMapping;

    private BeanRowMapper(Class cls, boolean requireAllColumns) {
        this.cls = cls;
        this.beanMapping = BeanMapping.getBeanMapping(cls);
        this.requireAllColumns = requireAllColumns;
        try {
            this.constructor = cls.getConstructor();
        } catch (NoSuchMethodException e) {
            throw new ReflectionException(e);
        }
    }

    public static  BeanRowMapper getInstance(Class cls, boolean requireAllColumns) {
        return new BeanRowMapper<>(cls, requireAllColumns);
    }

    @Override
    public T map(ColumnNamesProvider provider, ResultSet rs) throws SQLException {
        String[] names = provider.get();
        T bean;
        try {
            bean = constructor.newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new ReflectionException(e);
        }
        for (int i = 0; i < names.length; i++) {
            String name = names[i];
            Property property = beanMapping.getProperty(name);
            if (property == null) {
                property = beanMapping.getProperty(name.replace("_", ""));
            }
            if (property == null) {
                if (requireAllColumns) {
                    throw new MissingPropertyException(cls.getName(), name);
                }
                continue;
            }
            setColumnValue(rs, i + 1, bean, property);
        }
        return bean;
    }


    private static final Set> wrappers = Sets.of(Byte.class, Short.class, Integer.class, Long.class,
            Float.class, Double.class, Character.class, Boolean.class);

    // Additional support for java8 time types.
    // Many drivers do not support java8 time well, so handle this using java.sql.* as bridge.
    // Note that this will lose the nano seconds.
    private static final Set> java8TimeTypes = Sets.of(
            LocalDate.class, LocalDateTime.class, LocalTime.class, OffsetDateTime.class, OffsetTime.class
    );

    // Types that has a jdbc getXXX method support, and not primitive and wrapper types.
    // The getXXX methods may have wilder conversion than getObject.
    private static final Set> jdbcConvectionTypes = Sets.of(
            String.class,
            BigDecimal.class,
            byte[].class,
            java.sql.Date.class, Time.class, Timestamp.class,
            Clob.class, Blob.class,
            Array.class,
            Struct.class,
            Ref.class,
            URL.class,
            RowId.class,
            SQLXML.class
    );

    /**
     * Set a ResultSet column value into an object.
     */
    private void setColumnValue(ResultSet rs, int index, Object bean, Property property) throws SQLException {

        Class type = property.type();
        if (type == String.class) {
            // String is most frequent used type, place it here
            property.set(bean, rs.getString(index));
        } else if (type.isPrimitive()) {
            setPrimitive(rs, index, bean, property, type);
        } else if (wrappers.contains(type)) {
            setWrapper(rs, index, bean, property, type);
        } else if (jdbcConvectionTypes.contains(type)) {
            setJdbcTypes(rs, index, bean, property, type);
        } else if (java8TimeTypes.contains(type)) {
            setJava8Times(rs, index, bean, property, type);
        } else if (type.getClass().isEnum()) {
            // support for java enum, by use the name of enum
            String str = rs.getString(index);
            if (str == null) {
                property.set(bean, null);
            } else {
                property.set(bean, Enum.valueOf(type.asSubclass(Enum.class), str));
            }
        } else {
            // Note: java8 LocalDate/LocalTime/LocalDateTime/OffsetDateTime/OffsetTime may be supported here.
            Object value = rs.getObject(index, type);
            property.set(bean, value);
        }
    }

    private void setJava8Times(ResultSet rs, int index, Object bean, Property property, Class type)
            throws SQLException {
        if (type == LocalDate.class) {
            Date date = rs.getDate(index);
            if (date == null) {
                property.set(bean, null);
            } else {
                property.set(bean, date.toLocalDate());
            }
        } else if (type == LocalTime.class) {
            Time time = rs.getTime(index);
            if (time == null) {
                property.set(bean, null);
            } else {
                property.set(bean, time.toLocalTime());
            }
        } else if (type == LocalDateTime.class) {
            Timestamp timestamp = rs.getTimestamp(index);
            if (timestamp == null) {
                property.set(bean, null);
            } else {
                property.set(bean, timestamp.toLocalDateTime());
            }
        } else if (type == OffsetDateTime.class) {
            Timestamp timestamp = rs.getTimestamp(index);
            if (timestamp == null) {
                property.set(bean, null);
            } else {
                property.set(bean, OffsetDateTime.ofInstant(Instant.ofEpochMilli(timestamp.getTime()), ZoneId.systemDefault()));
            }
        } else if (type == OffsetTime.class) {
            Timestamp timestamp = rs.getTimestamp(index);
            if (timestamp == null) {
                property.set(bean, null);
            } else {
                property.set(bean, OffsetTime.ofInstant(Instant.ofEpochMilli(timestamp.getTime()), ZoneId.systemDefault()));
            }
        } else {
            throw new RuntimeException("not handle java8 types: " + type.getName());
        }
    }

    private void setJdbcTypes(ResultSet rs, int index, Object bean, Property property, Class type)
            throws SQLException {
        if (type == String.class) {
            property.set(bean, rs.getString(index));
        } else if (type == BigDecimal.class) {
            property.set(bean, rs.getBigDecimal(index));
        } else if (type == byte[].class) {
            property.set(bean, rs.getBytes(index));
        } else if (type == Timestamp.class) {
            property.set(bean, rs.getTimestamp(index));
        } else if (type == java.sql.Date.class) {
            property.set(bean, rs.getDate(index));
        } else if (type == Time.class) {
            property.set(bean, rs.getTime(index));
        } else if (type == Blob.class) {
            property.set(bean, rs.getBlob(index));
        } else if (type == Clob.class) {
            property.set(bean, rs.getClob(index));
        } else if (type == Array.class) {
            property.set(bean, rs.getArray(index));
        } else if (type == Struct.class) {
            property.set(bean, rs.getObject(index, type));
        } else if (type == Ref.class) {
            property.set(bean, rs.getRef(index));
        } else if (type == URL.class) {
            property.set(bean, rs.getURL(index));
        } else if (type == RowId.class) {
            property.set(bean, rs.getRowId(index));
        } else if (type == SQLXML.class) {
            property.set(bean, rs.getSQLXML(index));
        } else {
            throw new RuntimeException("not handle jdbc type: " + type.getName());
        }
    }

    private void setWrapper(ResultSet rs, int index, Object bean, Property property, Class type)
            throws SQLException {
        if (type == Integer.class) {
            int value = rs.getInt(index);
            if (rs.wasNull()) {
                property.set(bean, null);
            } else {
                property.set(bean, value);
            }
        } else if (type == Boolean.class) {
            boolean value = rs.getBoolean(index);
            if (rs.wasNull()) {
                property.set(bean, null);
            } else {
                property.set(bean, value);
            }
        } else if (type == Long.class) {
            long value = rs.getLong(index);
            if (rs.wasNull()) {
                property.set(bean, null);
            } else {
                property.set(bean, value);
            }
        } else if (type == Double.class) {
            double value = rs.getDouble(index);
            if (rs.wasNull()) {
                property.set(bean, null);
            } else {
                property.set(bean, value);
            }
        } else if (type == Float.class) {
            float value = rs.getFloat(index);
            if (rs.wasNull()) {
                property.set(bean, null);
            } else {
                property.set(bean, value);
            }
        } else if (type == Short.class) {
            short value = rs.getShort(index);
            if (rs.wasNull()) {
                property.set(bean, null);
            } else {
                property.set(bean, value);
            }
        } else if (type == Byte.class) {
            byte value = rs.getByte(index);
            if (rs.wasNull()) {
                property.set(bean, null);
            } else {
                property.set(bean, value);
            }
        } else if (type == Character.class) {
            property.set(bean, rs.getObject(index, type));
        } else {
            throw new RuntimeException("Not box type: " + type);
        }
    }

    private void setPrimitive(ResultSet rs, int index, Object bean, Property property, Class type)
            throws SQLException {
        if (type == int.class) {
            property.set(bean, rs.getInt(index));
        } else if (type == boolean.class) {
            property.set(bean, rs.getBoolean(index));
        } else if (type == long.class) {
            property.set(bean, rs.getLong(index));
        } else if (type == double.class) {
            property.set(bean, rs.getDouble(index));
        } else if (type == float.class) {
            property.set(bean, rs.getFloat(index));
        } else if (type == short.class) {
            property.set(bean, rs.getShort(index));
        } else if (type == byte.class) {
            property.set(bean, rs.getByte(index));
        } else if (type == char.class) {
            property.set(bean, rs.getObject(index, type));
        } else {
            throw new RuntimeException("Not primitive type: " + type);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy