15

私は簡単なセットアップをしていて、不可解な(少なくとも私にとっては)問題に遭遇しました:

互いに関連する3つのpojoがあります。

@NodeEntity
public class Unit {
    @GraphId Long nodeId;
    @Indexed int type;
    String description;
}


@NodeEntity
public class User {
    @GraphId Long nodeId;
    @RelatedTo(type="user", direction = Direction.INCOMING)
    @Fetch private Iterable<Worker> worker;
    @Fetch Unit currentUnit;

    String name;

}

@NodeEntity
public class Worker {
    @GraphId Long nodeId;
    @Fetch User user;
    @Fetch Unit unit;
    String description;
}

したがって、「現在のユニット」に直接ジャンプできるユーザーをマークする「現在のユニット」を持つUser-Worker-Unitがあります。各ユーザーは複数のワーカーを持つことができますが、1つのワーカーは1つのユニットにのみ割り当てられます(1つのユニットは複数のワーカーを持つことができます)。

私が疑問に思っていたのは、「User.worker」の@Fetchアノテーションを制御する方法です。私はほとんどの場合「Worker」でしか作業しないので、実際には必要なときにだけこれをロードしたいと思っています。

http://static.springsource.org/spring-data/data-neo4j/docs/2.0.0.RELEASE/reference/html/を確認しましたが、はっきりしていません。

  • ワーカーは読み取り専用(着信関係)である必要があるため、反復可能です。ドキュメントではこれが明確に示されていますが、例では「セット」がほとんどの場合に使用されます。なんで?またはそれは重要ではありません...
  • ワーカーにアクセス時にのみロードさせるにはどうすればよいですか?(遅延読み込み)
  • 単純な関係(worker.unit)でさえ@Fetchでアノテーションを付ける必要があるのはなぜですか。もっと良い方法はありませんか?このような単純な関係が多数ある別のエンティティがあります。1つのオブジェクトのプロパティが必要であるという理由だけで、グラフ全体をロードする必要はありません。
  • 遅延読み込みで動作するように、スプリング構成が欠落していますか?
  • 追加の呼び出しを介して関係(@Fetchとしてマークされていない)をロードする方法はありますか?

私の見方からすると、このコンストラクトは、ほとんどの場合ユーザーを気にしない場合でも、ワーカーが必要になるとすぐにデータベース全体をロードします。

私が見つけた唯一の回避策は、リポジトリを使用し、必要に応じてエンティティを手動でロードすることです。

- - - - アップデート - - - -

私はかなり前からneo4jを使用していて、フェッチを常に呼び出す必要がない(したがって、グラフ全体をロードしない)上記の問題の解決策を見つけました。唯一の欠点:それは実行時の側面です:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.support.Neo4jTemplate;

import my.modelUtils.BaseObject;

@Aspect
public class Neo4jFetchAspect {

    // thew neo4j template - make sure to fill it 
    @Autowired private Neo4jTemplate template;

    @Around("modelGetter()")
    public Object autoFetch(ProceedingJoinPoint pjp) throws Throwable {
        Object o = pjp.proceed();
        if(o != null) {
            if(o.getClass().isAnnotationPresent(NodeEntity.class)) {
                if(o instanceof BaseObject<?>) {
                    BaseObject<?> bo = (BaseObject<?>)o;
                    if(bo.getId() != null && !bo.isFetched()) {
                        return template.fetch(o);
                    }
                    return o;
                }
                try {
                    return template.fetch(o);
                } catch(MappingException me) {
                    me.printStackTrace();
                }
            }
        }
        return o;
    }

    @Pointcut("execution(public my.model.package.*.get*())")
    public void modelGetter() {}

}

アスペクトを適用するクラスパスmy.model.packageを調整する必要があります。.get()) ")

モデルクラスのすべてのgetメソッドにアスペクトを適用します。これには、いくつかの前提条件が必要です。

  • モデルクラスでゲッターを使用する必要があります(アスペクトはパブリック属性では機能しません-とにかく使用しないでください)
  • すべてのモデルクラスは同じパッケージに含まれています(コードを少し調整する必要があります)-フィルターを調整できると思います
  • ランタイムコンポーネントとしてのaspectjが必要です(tomcatを使用する場合は少し注意が必要です)-しかし、それは機能します:)
  • すべてのモデルクラスは、以下を提供するBaseObjectインターフェイスを実装する必要があります。

    public interface BaseObject {public boolean isFetched(); }

これにより、ダブルフェッチが防止されます。必須のサブクラスまたは属性(つまり、名前またはnodeId以外のもの)をチェックして、実際にフェッチされているかどうかを確認します。Neo4jはオブジェクトを作成しますが、nodeIdを埋めるだけで、他のすべては変更されません(したがって、他のすべてはNULLになります)。

すなわち

@NodeEntity
public class User implements BaseObject{
    @GraphId
    private Long nodeId;

        String username = null;

    @Override
    public boolean isFetched() {
        return username != null;
    }
}

誰かがその奇妙な回避策なしでこれを行う方法を見つけた場合は、あなたの解決策を追加してください:)これは機能するので、私はaspectjのないものが大好きです。

カスタムフィールドチェックを必要としないベースオブジェクトデザイン

最適化の1つは、ブールフィールド(ブールロード)を実際に使用してそれをチェックするインターフェイスの代わりに基本クラスを作成することです(手動チェックについて心配する必要はありません)。

public abstract class BaseObject {
    private Boolean loaded;
    public boolean isFetched() {
        return loaded != null;
    }
    /**
     * getLoaded will always return true (is read when saving the object)
     */
    public Boolean getLoaded() {
        return true;
    }

    /**
     * setLoaded is called when loading from neo4j
     */
    public void setLoaded(Boolean val) {
        this.loaded = val;
    }
}

これが機能するのは、オブジェクトを保存すると、ロード時に「true」が返されるためです。アスペクトがオブジェクトを見るとき、isFetched()を使用します。これは、オブジェクトがまだ取得されていない場合、nullを返します。オブジェクトが取得されると、setLoadedが呼び出され、ロードされた変数がtrueに設定されます。

ジャクソンが遅延読み込みをトリガーするのを防ぐ方法は?

(コメントの質問への回答として-この問題がなかったので、まだ試していなかったことに注意してください)。

jacksonでは、カスタムシリアライザーを使用することをお勧めします(つまり、http: //www.baeldung.com/jackson-custom-serializationを参照)。これにより、値を取得する前にエンティティを確認できます。すでにフェッチされているかどうかをチェックし、シリアル化全体を続行するか、IDを使用するだけです。

public class ItemSerializer extends JsonSerializer<BaseObject> {
    @Override
    public void serialize(BaseObject value, JsonGenerator jgen, SerializerProvider provider)
      throws IOException, JsonProcessingException {
        // serialize the whole object
        if(value.isFetched()) {
            super.serialize(value, jgen, provider);
            return;
        }
        // only serialize the id
        jgen.writeStartObject();
        jgen.writeNumberField("id", value.nodeId);
        jgen.writeEndObject();
    }
}

スプリング構成

これは私が使用しているSpring構成のサンプルです。プロジェクトに合わせてパッケージを調整する必要があります。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

    <context:annotation-config/>
    <context:spring-configured/>

    <neo4j:repositories base-package="my.dao"/> <!-- repositories = dao -->

    <context:component-scan base-package="my.controller">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <!--  that would be our services -->
    </context:component-scan>
    <tx:annotation-driven mode="aspectj" transaction-manager="neo4jTransactionManager"/>    
    <bean class="corinis.util.aspects.Neo4jFetchAspect" factory-method="aspectOf"/> 
</beans>

AOP構成

これは、これが機能するための/META-INF/aop.xmlです。

<!DOCTYPE aspectj PUBLIC
        "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
    <aspectj>
        <weaver>
            <!-- only weave classes in our application-specific packages -->
            <include within="my.model.*" />
        </weaver>
        <aspects>
            <!-- weave in just this aspect -->
            <aspect name="my.util.aspects.Neo4jFetchAspect" />
        </aspects>
    </aspectj>
4

3 に答える 3

12

自分ですべての質問に対する答えを見つけました:

@Iterable:はい、iterableは読み取り専用に使用できます

@load on access:デフォルトでは何もロードされません。自動遅延読み込みは利用できません(少なくとも私が収集できる限り)

残りの部分:関係が必要な場合は、@ Fetchを使用するか、neo4jtemplate.fetchメソッドを使用する必要があります。

@NodeEntity
public class User {
    @GraphId Long nodeId;
    @RelatedTo(type="user", direction = Direction.INCOMING)
    private Iterable<Worker> worker;
    @Fetch Unit currentUnit;

    String name;

}

class GetService {
  @Autowired private Neo4jTemplate template;

  public void doSomethingFunction() {
    User u = ....;
    // worker is not avaiable here

    template.fetch(u.worker);
    // do something with the worker
  }  
}
于 2012-01-13T21:34:13.037 に答える
3

透過的ではありませんが、それでも遅延フェッチです。

template.fetch(person.getDirectReports());

そして、@ Fetchは、あなたの回答ですでに述べたように、熱心なフェッチを行います。

于 2013-04-23T00:40:36.973 に答える
0

遅延読み込みを処理する現在のスプリングデータの方法の制限を回避するアスペクトアプローチが好きです。

@niko-私はあなたのコードサンプルを基本的なMavenプロジェクトに入れ、そのソリューションをほとんど成功させようとしませんでした:

https://github.com/samuel-kerrien/neo4j-aspect-auto-fetching

何らかの理由でアスペクトは初期化されていますが、アドバイスは実行されていないようです。問題を再現するには、次のJUnitテストを実行するだけです。

playground.neo4j.domain.UserTest
于 2015-05-15T10:20:49.380 に答える