简体   繁体   中英

How to speed up Reflection Code?

In my application, I use Apache's DBUtils class to read from a MySQL database. I wrote a custom BasicRowProcessor that uses annotations on the data object's fields to read columns from the database. It works really well code-wise, but the performance is rather slow when I'm pulling lots of rows. For example, a 1500 row x 35 column result set will take 800ms with this code, but only 80ms if I'm using the standard JDBC code.

Question - what can I do to speed up this code? Specifically, I'm using a lot of reflection and annotations - are there tricks I'm missing to speed this up?

 @Override
   public <T> T toBean(ResultSet rs, Class<T> type) throws SQLException
   {
         T data = type.newInstance();

         Field[] f = type.getFields();

         for (int i=0; i<f.length; i++)
         {
            Field field = f[i];

            if (field.isAnnotationPresent(DBColumn.class))
            {
               String columnName = field.getAnnotation(DBColumn.class).name();
            }

            if (field.getType().getName().equals("int"))
            {
               field.setInt(data, rs.getInt(columnName));
            }
            else if (field.getType().getName().equals("long"))
            {
              field.setLong(data, rs.getLong(columnName));
            }
            // .... for all the other types

        }

    return data;
 }

Perhaps build & cache the row-mapping logic, so you don't have to scan the fields, annotations, result-set metadata & types every time.

JDBC metadata access is particularly slow.

In this example, you could provide a string 'key' to enable efficient caching of the mappers for different result-set types (differing column structures).

public BasicRowProcessor getReflectiveRowMapper (ResultSet rs, String resultSetKey, Class<T> type) {
    String key = resultSetKey+"-"+type;
    BasicRowProcessor result = rrmCache.get( key);
    if (result != null) {
        result = buildReflectiveRowMapper (rs, resultSetKey, type);
        rrmCache.put( key, result);
    }
    return result;
}

public BuiltRowProcessor buildReflectiveRowMapper (ResultSet rs, String resultSetKey, Class<T> type) {
}

Then..

public class BuiltRowProcessor extends BasicRowProcessor {
    protected FieldMapping[] mappings;

    @Override
    public <T> T toBean (ResultSet rs, Class<T> type) throws SQLException {
        T data = type.newInstance();
        for (FieldMapping field : mappings) {
            field.mapField( rs, data);
        }
        return data;
    }
}

abstract public class FieldMapping {
    protected Field field;
    protected int columnIndex;

    // constructor..

    abstract public void mapField (ResultSet rs, Object target) throws SQLException;

    protected void writeField (Object target, Object value) {
        try {
            field.setValue(target, value);   // or whatever API.
        } catch (Exception x) {
            throw new RuntimeException("error writing field: "+field, x);
        }
    }
}

public IntMapping extends FieldMapping {
    // constructor..

    public void mapField (ResultSet rs, Object target) throws SQLException {
        int value = rs.getInt(columnIndex);
        writeField( target, value);
    }
}

Don't compare the names of the types. Compare the types, to int.class, long.class, etc.

In fact you don't need all those 'if' statements. Just call Field.set(), using ResultSet.getObject() as the argument. All the right things will happen internally. But I don't say this will be any faster.

You would be better off using the reflection facilities of the java.beans Introspector, as it caches all the reflective stuff for you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM