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

org.noorm.jdbc.BeanMapper Maven / Gradle / Ivy

There is a newer version: 1.5.0
Show newest version
package org.noorm.jdbc;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Generic mapper for mapping a JDBC ResultSet to a Bean or for mapping a Bean
 * to a parameter map. This class is primarily used by the JDBCProcedureProcessor
 * to convert the JDBC ResultSets into Beans, resp. Lists of Beans.
 * Internally, the BeanMapper uses reflection to find the correct mapping with help
 * of the JDBCColumn annotations for the distinct attributes of the Beans.
 * Attributes without JDBCColumn annotation are considered transient and they are
 * omitted from the mapping procedure.
 *
 * @author Ulf Pietruschka / [email protected]
 */
public class BeanMapper {

	private static BeanMapper mapper = new BeanMapper();
	private static final Logger log = LoggerFactory.getLogger(BeanMapper.class);

	public static  BeanMapper getInstance() {

		return mapper;
	}

	private BeanMapper() {
	}

	/**
	 * Maps the given ResultSet to a list of Beans.
	 *
	 * @param pResultSet the ResultSet subject to conversion to a Bean list
	 * @param pBeanClass the type of the Bean
	 * @return the Bean list filled with the data from the ResultSet
	 * @throws SQLException JDBC driver exception
	 */
	public List toBeanList(final ResultSet pResultSet,
							  final Class pBeanClass,
							  final boolean pFetchPagingTotal) throws SQLException {

        if (log.isTraceEnabled()) {
            log.trace("Converting database results to list of Bean class ".concat(pBeanClass.getName()));
        }
		final List beanList = new ArrayList();
		T bean;
		final Field[] fields = BeanMetaDataUtil.getDeclaredFieldsInclParent(pBeanClass);
		if (fields == null || fields.length == 0) {
			return beanList;
		}
		while (pResultSet.next()) {
			try {
				if (pBeanClass.equals(Long.class)) {
					// Support for num_array based on java.lang.Long.
					bean = (T) new Long(pResultSet.getLong(1));
				} else {
					bean = pBeanClass.newInstance();
					populateFields(pResultSet, bean, fields, pFetchPagingTotal);
				}
			} catch (InstantiationException ex) {
				throw new DataAccessException(ex);
			} catch (IllegalAccessException ex) {
				throw new DataAccessException(ex);
			}
			beanList.add(bean);
		}

		return beanList;
	}

	/**
	 * Converts the given Bean to a Map, containing a mapping from the attribute name to
	 * the attributes value. The attribute name used is the database column name.
	 *
	 * @param pBean the Bean subject to conversion
	 * @return a map containing the content of the bean.
	 */
	public Map toMap(final T pBean) {

        if (log.isTraceEnabled()) {
            log.trace("Converting Bean to parameter map.");
        }
		final Map fieldMap = new HashMap();
		final Field[] fields = BeanMetaDataUtil.getDeclaredFieldsInclParent(pBean.getClass());
		if (fields == null || fields.length == 0) {
			return fieldMap;
		}

		String fieldName;
		for (final Field field : fields) {
			// Ignore serialVersionUID
			if (BeanMetaDataUtil.SERIAL_VERSION_UID.equals(field.getName())) {
				continue;
			}
			field.setAccessible(true);
            final JDBCColumn colAnn = BeanMetaDataUtil.getJDBCColumnAnnotation(field);
            if (colAnn != null) {
            	// All non-insertable columns are also non-updatable, so no more distinction required
                if (!colAnn.insertable()) {
                    continue;
                }
                fieldName = colAnn.name();
            } else {
				// Ignore fields without JDBCColumn annotation (interpreted transient)
				continue;
			}

			try {
				fieldMap.put(fieldName, field.get(pBean));
			} catch (IllegalAccessException ex) {
				throw new DataAccessException(ex);
			}
		}

		return fieldMap;
	}

	private void populateFields(final ResultSet pResultSet,
								final T pBean,
								final Field[] pFields,
								final boolean pFetchPagingTotal) throws IllegalAccessException, SQLException {

		String fieldName;
		for (final Field field : pFields) {
			// Ignore serialVersionUID
			if (BeanMetaDataUtil.SERIAL_VERSION_UID.equals(field.getName())) {
				continue;
			}
			field.setAccessible(true);
			final Class fieldType = field.getType();
            final JDBCColumn colAnn = BeanMetaDataUtil.getJDBCColumnAnnotation(field);
            if (colAnn != null) {
                fieldName = colAnn.name();
            } else {
                // Ignore fields without JDBCColumn annotation (interpreted transient)
                continue;
            }
			if (!pFetchPagingTotal && fieldName.equals(IBean.PAGING_TOTAL)) {
            	// Column PAGING_TOTAL is only available for paging queries.
				continue;
			}

			if (log.isTraceEnabled()) {
				StringBuilder logMessage = new StringBuilder();
				logMessage.append("Mapping database field : ");
				logMessage.append(fieldName);
				logMessage.append(" to Bean field ");
				logMessage.append(field.getName());
				logMessage.append(":");
				logMessage.append(fieldType.getName());
				log.trace(logMessage.toString());
			}

			// Principally, for matching types, using "field.set(beans, pResultSet.getObject(fieldName))" would
			// work. However, for non-matching types subject to automatic conversion by means of the
			// JDBC driver, we would run into problems.
			// The explicit casting based on the types of the Bean as follows is the most flexible approach
			// in providing a zero-configuration way to choose custom (but compatible!) types in the
			// Bean specification.

			if (fieldType == String.class) {
                final String value = pResultSet.getString(fieldName);
                if (value != null) {
                    field.set(pBean, value.trim());
                }
                continue;
			}

            if (fieldType == Long.class) {
                final Long value = pResultSet.getLong(fieldName);
                if (!pResultSet.wasNull()) {
                    field.set(pBean, value);
                }
                continue;
            }

			if (fieldType == Integer.class) {
				final Integer value = pResultSet.getInt(fieldName);
				if (!pResultSet.wasNull()) {
					field.set(pBean, value);
				}
                continue;
			}

			if (fieldType == Double.class) {
				final Double value = pResultSet.getDouble(fieldName);
				if (!pResultSet.wasNull()) {
					field.set(pBean, value);
				}
                continue;
			}
			if (fieldType == java.util.Date.class || fieldType == Timestamp.class) {
				field.set(pBean, pResultSet.getTimestamp(fieldName));
                continue;
			}

            if (fieldType == java.sql.Date.class) {
                Timestamp timestamp = pResultSet.getTimestamp(fieldName);
                if (!pResultSet.wasNull()) {
                    field.set(pBean, new java.sql.Date(timestamp.getTime()));
                }
                continue;
            }

			if (fieldType == BigDecimal.class) {
				field.set(pBean, pResultSet.getBigDecimal(fieldName));
                continue;
			}

			if (fieldType == Boolean.class) {
				field.set(pBean, pResultSet.getBoolean(fieldName));
                continue;
			}

			if (fieldType == Float.class) {
				final Float value = pResultSet.getFloat(fieldName);
				if (!pResultSet.wasNull()) {
					field.set(pBean, value);
				}
                continue;
			}

			if (fieldType == Short.class) {
				final Short value = pResultSet.getShort(fieldName);
				if (!pResultSet.wasNull()) {
					field.set(pBean, value);
				}
                continue;
			}

			if (fieldType == byte[].class) {
                field.set(pBean, pResultSet.getBytes(fieldName));
                continue;
			}
            if (fieldType == Clob.class) {
                final Clob value = pResultSet.getClob(fieldName);
                if (!pResultSet.wasNull()) {
                    field.set(pBean, value);
                }
                continue;
            }
            if (fieldType == NClob.class) {
                final NClob value = pResultSet.getNClob(fieldName);
                if (!pResultSet.wasNull()) {
                    field.set(pBean, value);
                }
                continue;
            }
            if (fieldType == Blob.class) {
                final Blob value = pResultSet.getBlob(fieldName);
                if (!pResultSet.wasNull()) {
                    field.set(pBean, value);
                }
                continue;
            }
            if (fieldType == SQLXML.class) {
                final SQLXML value = pResultSet.getSQLXML(fieldName);
                if (!pResultSet.wasNull()) {
                    field.set(pBean, value);
                }
                continue;
            }
            final String errMsg = "Datatype conversion failed for [".concat(fieldName)
                    .concat(" / ").concat(fieldType.getName()).concat("].");
            throw new DataAccessException(DataAccessException.Type.UNSUPPORTED_DATATYPE, errMsg);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy