lv.
lv.

Reputation: 466

Is this the right way to cache reflection data?

I'm working on some webservices, which I'm implementing with hibernate. The issue is that I need to access the names of the entity tables (say, those in @Table("Table_A")). The code is generated, so I cannot alter the entity classes themselves to give me what I need.

This is what I was doing so far:

public static
<T extends Entity<K>, K extends Serializable>
String getTableName(Class<T> objClass) {
    Table table = objClass.getAnnotation(Table.class);
    return (table != null)? table.name() : null;
}

However, after some research I discovered that reflection is not the best performance-wise, and since this method will be called a lot I'm looking for another way to go at it.

I'm trying to follow the advice given here: Performance of calling Method/Field.getAnnotation(Class) several times vs. Pre-caching this data in a Map.

This is what I came up with:

public final class EntityUtils {
    private static HashMap<String, String> entityCache;

    private EntityUtils() {
        entityCache = new HashMap<String, String>();
    }

    public static
    <T extends Entity<K>, K extends Serializable>
    String getTableName_New(Class<T> objClass) {
        String tableName = entityCache.get(objClass.getCanonicalName());

        if (tableName == null) {
            Table table = objClass.getAnnotation(Table.class);

            if (table != null) {
                tableName = table.name();
                entityCache.put(objClass.getCanonicalName(), tableName);
            }
        }

        return tableName;
    }
}

However, I'm not sure about this. Is it a good idea to cache reflection data in a static map? Is there any alternative way to accomplish this?

Upvotes: 2

Views: 3719

Answers (1)

Sean Patrick Floyd
Sean Patrick Floyd

Reputation: 298908

Ideally, I would use a Guava cache, with weak keys, that way you're not keeping any references to the class object in case you use some advanced ClassLoader magic.

LoadingCache<Class<?>, String> tableNames = CacheBuilder.newBuilder()
       .weakKeys()
       .build(
           new CacheLoader<Class<?>, String>() {
             public String load(Class<?> key) {
                 Table table = objClass.getAnnotation(Table.class);
                 return table == null ? key.getSimpleName() : table.name();
             }
           });

Usage:

String tablename = tableNames.get(yourClass);

See: Caches Explained


On the other hand: annotations are cached by the JDK also, so your previous approach is probably fine

Upvotes: 3

Related Questions