博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
聊聊spring jdbc的RowMapper
阅读量:5873 次
发布时间:2019-06-19

本文共 14084 字,大约阅读时间需要 46 分钟。

本文主要介绍下spring jdbc的RowMapper

RowMapper

spring-jdbc-4.3.10.RELEASE-sources.jar!/org/springframework/jdbc/core/RowMapper.java

public interface RowMapper
{ /** * Implementations must implement this method to map each row of data * in the ResultSet. This method should not call {@code next()} on * the ResultSet; it is only supposed to map values of the current row. * @param rs the ResultSet to map (pre-initialized for the current row) * @param rowNum the number of the current row * @return the result object for the current row * @throws SQLException if a SQLException is encountered getting * column values (that is, there's no need to catch SQLException) */ T mapRow(ResultSet rs, int rowNum) throws SQLException;}复制代码

spring定义了这个RowMapper,来让应用去自定义数据库结果集与实体的映射,这样来把变化的部分隔离出去

ColumnMapRowMapper

spring-jdbc-4.3.7.RELEASE-sources.jar!/org/springframework/jdbc/core/ColumnMapRowMapper.java

public class ColumnMapRowMapper implements RowMapper
> { @Override public Map
mapRow(ResultSet rs, int rowNum) throws SQLException { ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); Map
mapOfColValues = createColumnMap(columnCount); for (int i = 1; i <= columnCount; i++) { String key = getColumnKey(JdbcUtils.lookupColumnName(rsmd, i)); Object obj = getColumnValue(rs, i); mapOfColValues.put(key, obj); } return mapOfColValues; } /** * Create a Map instance to be used as column map. *

By default, a linked case-insensitive Map will be created. * @param columnCount the column count, to be used as initial * capacity for the Map * @return the new Map instance * @see org.springframework.util.LinkedCaseInsensitiveMap */ protected Map

createColumnMap(int columnCount) { return new LinkedCaseInsensitiveMap
(columnCount); } /** * Determine the key to use for the given column in the column Map. * @param columnName the column name as returned by the ResultSet * @return the column key to use * @see java.sql.ResultSetMetaData#getColumnName */ protected String getColumnKey(String columnName) { return columnName; } /** * Retrieve a JDBC object value for the specified column. *

The default implementation uses the {@code getObject} method. * Additionally, this implementation includes a "hack" to get around Oracle * returning a non standard object for their TIMESTAMP datatype. * @param rs is the ResultSet holding the data * @param index is the column index * @return the Object returned * @see org.springframework.jdbc.support.JdbcUtils#getResultSetValue */ protected Object getColumnValue(ResultSet rs, int index) throws SQLException { return JdbcUtils.getResultSetValue(rs, index); }}复制代码

将结果集映射为map

SingleColumnRowMapper

spring-jdbc-4.3.10.RELEASE-sources.jar!/org/springframework/jdbc/core/SingleColumnRowMapper.java

public class SingleColumnRowMapper
implements RowMapper
{ private Class
requiredType; /** * Create a new {@code SingleColumnRowMapper} for bean-style configuration. * @see #setRequiredType */ public SingleColumnRowMapper() { } /** * Create a new {@code SingleColumnRowMapper}. *

Consider using the {@link #newInstance} factory method instead, * which allows for specifying the required type once only. * @param requiredType the type that each result object is expected to match */ public SingleColumnRowMapper(Class

requiredType) { setRequiredType(requiredType); } /** * Set the type that each result object is expected to match. *

If not specified, the column value will be exposed as * returned by the JDBC driver. */ public void setRequiredType(Class

requiredType) { this.requiredType = ClassUtils.resolvePrimitiveIfNecessary(requiredType); } /** * Extract a value for the single column in the current row. *

Validates that there is only one column selected, * then delegates to {@code getColumnValue()} and also * {@code convertValueToRequiredType}, if necessary. * @see java.sql.ResultSetMetaData#getColumnCount() * @see #getColumnValue(java.sql.ResultSet, int, Class) * @see #convertValueToRequiredType(Object, Class) */ @Override @SuppressWarnings("unchecked") public T mapRow(ResultSet rs, int rowNum) throws SQLException { // Validate column count. ResultSetMetaData rsmd = rs.getMetaData(); int nrOfColumns = rsmd.getColumnCount(); if (nrOfColumns != 1) { throw new IncorrectResultSetColumnCountException(1, nrOfColumns); } // Extract column value from JDBC ResultSet. Object result = getColumnValue(rs, 1, this.requiredType); if (result != null && this.requiredType != null && !this.requiredType.isInstance(result)) { // Extracted value does not match already: try to convert it. try { return (T) convertValueToRequiredType(result, this.requiredType); } catch (IllegalArgumentException ex) { throw new TypeMismatchDataAccessException( "Type mismatch affecting row number " + rowNum + " and column type '" + rsmd.getColumnTypeName(1) + "': " + ex.getMessage()); } } return (T) result; } /** * Retrieve a JDBC object value for the specified column. *

The default implementation calls * {@link JdbcUtils#getResultSetValue(java.sql.ResultSet, int, Class)}. * If no required type has been specified, this method delegates to * {@code getColumnValue(rs, index)}, which basically calls * {@code ResultSet.getObject(index)} but applies some additional * default conversion to appropriate value types. * @param rs is the ResultSet holding the data * @param index is the column index * @param requiredType the type that each result object is expected to match * (or {@code null} if none specified) * @return the Object value * @throws SQLException in case of extraction failure * @see org.springframework.jdbc.support.JdbcUtils#getResultSetValue(java.sql.ResultSet, int, Class) * @see #getColumnValue(java.sql.ResultSet, int) */ protected Object getColumnValue(ResultSet rs, int index, Class

requiredType) throws SQLException { if (requiredType != null) { return JdbcUtils.getResultSetValue(rs, index, requiredType); } else { // No required type specified -> perform default extraction. return getColumnValue(rs, index); } } /** * Retrieve a JDBC object value for the specified column, using the most * appropriate value type. Called if no required type has been specified. *

The default implementation delegates to {@code JdbcUtils.getResultSetValue()}, * which uses the {@code ResultSet.getObject(index)} method. Additionally, * it includes a "hack" to get around Oracle returning a non-standard object for * their TIMESTAMP datatype. See the {@code JdbcUtils#getResultSetValue()} * javadoc for details. * @param rs is the ResultSet holding the data * @param index is the column index * @return the Object value * @throws SQLException in case of extraction failure * @see org.springframework.jdbc.support.JdbcUtils#getResultSetValue(java.sql.ResultSet, int) */ protected Object getColumnValue(ResultSet rs, int index) throws SQLException { return JdbcUtils.getResultSetValue(rs, index); } /** * Convert the given column value to the specified required type. * Only called if the extracted column value does not match already. *

If the required type is String, the value will simply get stringified * via {@code toString()}. In case of a Number, the value will be * converted into a Number, either through number conversion or through * String parsing (depending on the value type). * @param value the column value as extracted from {@code getColumnValue()} * (never {@code null}) * @param requiredType the type that each result object is expected to match * (never {@code null}) * @return the converted value * @see #getColumnValue(java.sql.ResultSet, int, Class) */ @SuppressWarnings("unchecked") protected Object convertValueToRequiredType(Object value, Class

requiredType) { if (String.class == requiredType) { return value.toString(); } else if (Number.class.isAssignableFrom(requiredType)) { if (value instanceof Number) { // Convert original Number to target Number class. return NumberUtils.convertNumberToTargetClass(((Number) value), (Class
) requiredType); } else { // Convert stringified value to target Number class. return NumberUtils.parseNumber(value.toString(),(Class
) requiredType); } } else { throw new IllegalArgumentException( "Value [" + value + "] is of type [" + value.getClass().getName() + "] and cannot be converted to required type [" + requiredType.getName() + "]"); } } /** * Static factory method to create a new {@code SingleColumnRowMapper} * (with the required type specified only once). * @param requiredType the type that each result object is expected to match * @since 4.1 */ public static
SingleColumnRowMapper
newInstance(Class
requiredType) { return new SingleColumnRowMapper
(requiredType); }}复制代码

映射单个字段,比如count(*)这种

BeanPropertyRowMapper

spring-jdbc-4.3.7.RELEASE-sources.jar!/org/springframework/jdbc/core/BeanPropertyRowMapper.java

public class BeanPropertyRowMapper
implements RowMapper
{//... protected void initialize(Class
mappedClass) { this.mappedClass = mappedClass; this.mappedFields = new HashMap
(); this.mappedProperties = new HashSet
(); PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass); for (PropertyDescriptor pd : pds) { if (pd.getWriteMethod() != null) { this.mappedFields.put(lowerCaseName(pd.getName()), pd); String underscoredName = underscoreName(pd.getName()); if (!lowerCaseName(pd.getName()).equals(underscoredName)) { this.mappedFields.put(underscoredName, pd); } this.mappedProperties.add(pd.getName()); } } } @Override public T mapRow(ResultSet rs, int rowNumber) throws SQLException { Assert.state(this.mappedClass != null, "Mapped class was not specified"); T mappedObject = BeanUtils.instantiateClass(this.mappedClass); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject); initBeanWrapper(bw); ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); Set
populatedProperties = (isCheckFullyPopulated() ? new HashSet
() : null); for (int index = 1; index <= columnCount; index++) { String column = JdbcUtils.lookupColumnName(rsmd, index); String field = lowerCaseName(column.replaceAll(" ", "")); PropertyDescriptor pd = this.mappedFields.get(field); if (pd != null) { try { Object value = getColumnValue(rs, index, pd); if (rowNumber == 0 && logger.isDebugEnabled()) { logger.debug("Mapping column '" + column + "' to property '" + pd.getName() + "' of type '" + ClassUtils.getQualifiedName(pd.getPropertyType()) + "'"); } try { bw.setPropertyValue(pd.getName(), value); } catch (TypeMismatchException ex) { if (value == null && this.primitivesDefaultedForNullValue) { if (logger.isDebugEnabled()) { logger.debug("Intercepted TypeMismatchException for row " + rowNumber + " and column '" + column + "' with null value when setting property '" + pd.getName() + "' of type '" + ClassUtils.getQualifiedName(pd.getPropertyType()) + "' on object: " + mappedObject, ex); } } else { throw ex; } } if (populatedProperties != null) { populatedProperties.add(pd.getName()); } } catch (NotWritablePropertyException ex) { throw new DataRetrievalFailureException( "Unable to map column '" + column + "' to property '" + pd.getName() + "'", ex); } } else { // No PropertyDescriptor found if (rowNumber == 0 && logger.isDebugEnabled()) { logger.debug("No property found for column '" + column + "' mapped to field '" + field + "'"); } } } if (populatedProperties != null && !populatedProperties.equals(this.mappedProperties)) { throw new InvalidDataAccessApiUsageException("Given ResultSet does not contain all fields " + "necessary to populate object of class [" + this.mappedClass.getName() + "]: " + this.mappedProperties); } return mappedObject; }}复制代码

初始化构造map的时候,存的key是下划线的

如果数据库字段命名跟实体类一致,或者是下划线变驼峰的这种,那么可以直接使用这个

实例

  • 使用BeanPropertyRowMapper的版本

    public Book findById(Integer id){     return jdbcTemplate.query("select * from book where book_id=?",new Object[]{id},new BeanPropertyRowMapper
    (Book.class)).get(0); }复制代码
  • 不使用BeanPropertyRowMapper的版本

    public Book findById2(Integer id){      return jdbcTemplate.query("select * from book where book_id=?",new Object[]{id},new RowMapper
    () { @Override public Book mapRow(ResultSet rs, int rowNum) throws SQLException { Book book = new Book(); book.setBookId(rs.getInt("book_id")); book.setTitle(rs.getString("title")); book.setCreatedAt(rs.getTimestamp("created_at")); return book; } }).get(0); }复制代码

有没有发现使用了BeanPropertyRowMapper更为简洁。

转载地址:http://yvhnx.baihongyu.com/

你可能感兴趣的文章
sqrt()平方根计算函数的实现1——二分法
查看>>
Web前端研发工程师编程能力飞升之路
查看>>
Linux内核 设备树操作常用API【转】
查看>>
“安装程序无法定位现有系统分区,也无法创建新的系统分区”提示
查看>>
volatile的深入理解--【sky原创】
查看>>
RabbitMQ指南之二:工作队列(Work Queues)
查看>>
js提交图片转换为base64
查看>>
面向对象 委托
查看>>
PassWord控件
查看>>
【带着canvas去流浪(5)】绘制K线图
查看>>
Linux 删除mysql数据库失败的解决方法
查看>>
浏览器缓存文件导致js文件更改无效
查看>>
如何才能学好javascript
查看>>
学习CodeIgniter框架之旅(二)继承自定义类
查看>>
yum被锁Another app is currently holding the yum lock; waiting for it to exit...
查看>>
Excel .net读取
查看>>
设计模式之简单工厂模式
查看>>
我的Python成长之路---第三天---Python基础(13)---2016年1月16日(雾霾)
查看>>
深度学习-数学-第一篇-标量,向量,矩阵,张量
查看>>
数据库分类
查看>>