web-dev-qa-db-ja.com

複数のメソッド引数の@Cacheableキー

spring documentation から:

@Cacheable(value="bookCache", key="isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

キーとしてisbnおよびcheckWarehouseを使用するように@Cachableを指定するにはどうすればよいですか?

60
phury

Update:現在指定されていない場合、現在のSpringキャッシュ実装はすべてのメソッドパラメータをキャッシュキーとして使用します。選択したキーを使用する場合は、 Arjan's answer を参照してください。これは、一意のキーを作成する最も簡単な方法であるSpELリスト{#isbn, #includeUsed}を使用します。

From Spring Documentation

デフォルトのキー生成戦略は、Spring 4.0のリリースで変更されました。 Springの以前のバージョンでは、複数のキーパラメーターに対して、equals()ではなくパラメーターのhashCode()のみを考慮したキー生成戦略を使用していました。これにより、予期しないキーの衝突が発生する可能性があります(背景については、SPR-10237を参照してください)。新しい「SimpleKeyGenerator」は、このようなシナリオに複合キーを使用します。

Spring 4.0以前

Spel式のパラメーターの値をkey="#checkWarehouse.toString() + #isbn.toString()")のようなものと連結することをお勧めします。 SPEL式でintを指定する必要はありません。

衝突確率が高いハッシュコードについては、をキーとして使用できません

このスレッドの誰かがT(Java.util.Objects).hash(#p0,#p1, #p2)を使用することを提案していますが、うまくいかず、このアプローチは簡単に破られます。たとえば、 SPR-9377 からのデータを使用しました:

    System.out.println( Objects.hash("someisbn", new Integer(109), new Integer(434)));
    System.out.println( Objects.hash("someisbn", new Integer(110), new Integer(403)));

私の環境では両方の行が-636517714を出力します。

追伸実際に参照ドキュメントにあります

@Cacheable(value="books", key="T(someType).hash(#isbn)") 
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

キーは一意である必要があるため、この例は間違っており、誤解を招くものであり、ドキュメントから削除する必要があると思います。

P.P.S.デフォルトのキー生成に関する興味深いアイデアについては、 https://jira.springsource.org/browse/SPR-9036 も参照してください。

正確さのために、そしてSHA256のような安全な 暗号化ハッシュ関数 を使用して、そのような関数のプロパティのために_が_であることを面白い事実として追加したいと思いますこのタスクでは可能ですが、毎回計算するのは費用がかかりすぎる可能性があります。

76
Boris Treukhov

Spring 3.2での限られたテストの後、SpELリスト{..., ..., ...}を使用できるようです。これには、null値を含めることもできます。 Springは、リストをキーとして実際のキャッシュ実装に渡します。 Ehcacheを使用すると、そのような時点で List#hashCode() が呼び出され、すべてのアイテムが考慮されます。 (Ehcacheonlyがハッシュコードに依存しているかどうかはわかりません。)

これを共有キャッシュに使用します。共有キャッシュでは、キーにも メソッド名を含む 、Springのデフォルトキージェネレーター 含まない を使用します。この方法で、さまざまな方法で一致するキーを危険にさらすことなく(多すぎる...)、(単一の)キャッシュを簡単に消去できます。好む:

@Cacheable(value="bookCache", 
  key="{ #root.methodName, #isbn?.id, #checkWarehouse }")
public Book findBook(ISBN isbn, boolean checkWarehouse) 
...

@Cacheable(value="bookCache", 
  key="{ #root.methodName, #asin, #checkWarehouse }")
public Book findBookByAmazonId(String asin, boolean checkWarehouse)
...

もちろん、多くのメソッドがこれを必要とし、常にキーにすべてのパラメーターを使用している場合、クラスとメソッド名を含むカスタムキージェネレーターを定義することもできます。

<cache:annotation-driven mode="..." key-generator="cacheKeyGenerator" />
<bean id="cacheKeyGenerator" class="net.example.cache.CacheKeyGenerator" />

... with:

public class CacheKeyGenerator 
  implements org.springframework.cache.interceptor.KeyGenerator {

    @Override
    public Object generate(final Object target, final Method method, 
      final Object... params) {

        final List<Object> key = new ArrayList<>();
        key.add(method.getDeclaringClass().getName());
        key.add(method.getName());

        for (final Object o : params) {
            key.add(o);
        }
        return key;
    }
}
63
Arjan

たとえば、JDK 1.7でSpring-EL式を使用できます。

@Cacheable(value="bookCache", key="T(Java.util.Objects).hash(#p0,#p1, #p2)")
4
Biju Kunjummen

これは動作します

@Cacheable(value="bookCache", key="#checkwarehouse.toString().append(#isbn.toString())")
0
Niraj Singh