JPA照会問題の探究

5462 ワード

引用する
最近、実体図に関するブログを見て、海外のお兄さんはJPAの実体間の関連は何度もクエリーを実行し、データ量が大きいと性能が非常に悪いと言っています.
ちょうど実体図は前に華軟を書いたときに勉強したことがありますが、理解していないだけで、プロジェクトで実際に応用していないので、ちょうどこの機会に勉強してみました.
実際には、jpaのプロジェクトを構築し、jpaがどのように検索されているのかを実際にテストします.
今日発見されたのは、Spring Boot2.1.5に更新されたことです.
探究する
エンティティ関係
最も簡単な教務システムモデル、教師、クラス、学生.
ベースデータ
二人の教師、一人の教師は二人のクラスを連れて、一人のクラスの中で二人の学生です.
単一テーブルクエリ
Iterable teachers = teacherRepository.findAll();
for (Teacher teacher : teachers) {
    System.out.println(teacher.getName());
}

最も簡単なクエリ文は、findAllの後にnameフィールドを取得します.
Hibernate: select teacher0_.id as id1_2_, teacher0_.name as name2_2_ from teacher teacher0_

単純な単一テーブルクエリでは、ベースフィールドidnameが検出されます.OneToMany複数テーブル関連クエリー
Iterable teachers = teacherRepository.findAll();
for (Teacher teacher : teachers) {
    System.out.println(teacher.getName());
    
    for (Klass klass : teacher.getKlasses()) {
        System.out.println(klass.getName());
        
        for (Student student : klass.getStudents()) {
            System.out.println(student.getName());
        }
    }
}

普通のコードを見ていると、みんな書いたはずですが、実は中には学問があります.
テスト環境
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.yunzhiclub.jpa.entity.Teacher.klasses, could not initialize proxy - no Session

テスト環境の実行エラー、no session.Hibernateでは、すべてのデータベースに対する操作がsessionで実行される必要があります.これは、データベースの直接操作の代わりに、データベースのエージェントオブジェクトとして理解することができます.HibernateSessionJDBCConnectionの違いについては、このお兄さんの答えを参考にすることができます:What is the difference between a Session and a Connection in Hibernate?-StackOverflow OneToManyはデフォルトで不活性ロードなので、使用するときにクエリーします.
ここでは、ユニットテストにおけるsessionの役割ドメインが1行しかないことを示しています.findAll以降sessionは閉まっています.
ソリューション
どちらのソリューションもベストプラクティスではありません.
事務を加える
プラストランザクションはsession閉鎖の問題を解決することができます.ここではユニットテストであり、トランザクションも必要であるため、ユニットテストでの不活性なロードによる問題は、トランザクションを追加する方法で実現されます.
原因は調べられなかったが、ここで推測した.Hibernateとはあくまで原生のjdbcメソッドをカプセル化したものであり,原理は以下のようなコードを生成することである.
Session session = factory.openSession();
Transaction tx = null;
try {
    tx = session.beginTransaction();
    // do some work
    ...
    tx.commit();
} catch (Exception e) {
    if (tx != null) tx.rollback();
    e.printStackTrace(); 
} finally {
    session.close();
}

トランザクションの正常な実行を維持するために、トランザクションのコミットとロールバックはsessionの有効範囲内にあることがわかります.言い換えれば、sessionはトランザクションが完了した後に閉じられ、トランザクションの管理の下で実行する方法で、sessionを正常に使用することができます.EAGERロードEAGERは急いで、一度にすべて調べて、ここもでたらめに翻訳する勇気がなくて、意会はすぐです.
/**
 *         
 */
@OneToMany(mappedBy = "teacher",fetch = FetchType.EAGER)
private List klasses = new ArrayList<>();

/**
 *        
 */
@OneToMany(mappedBy = "klass", fetch = FetchType.EAGER)
private List students = new ArrayList<>();

これは実現できますが、これは最悪の解決策です.
エンティティの注釈に加えられているので、異なる方法で使用し、関連していないエンティティを使用しないで、一度にすべて調べます. のクエリは、データテーブル全体を編集する必要があることを知っています.そのため、調べにくいです.EAGERに変更すると、深刻なパフォーマンスの問題が発生します.Hibernate注記設計Hibernate対の注釈の設計を見てみましょう.
複数対複数注釈、デフォルトLAZY:
1対のマルチ注釈、デフォルトLAZY:
複数対1注釈、デフォルトEAGER:
1対1の注釈、デフォルトEAGER:
データベースの分野では、Hibernateは牛に違いない.牛がこのように設計されている以上、私たちも自然に従うべきだ.
同時に、性能の面では、 のクエリー性能がよく、 のクエリー性能はそれほどよくないことも理解されるはずです.
実行環境
同じコードをServiceに入れ、apiインタフェースを作成します.
その後、正常に動作し、使用するときにデータベースをクエリーし、no sessionのエラーは発生しなかったので、ここでは、Serviceのクエリー方法では、sessionの役割ドメインがテストよりも広いと推測します.
問題の再現
私は華軟の中でno sessionの問題に遭遇しました.どうやって現れたのですか.loadUserByUsernameメソッドのうちusernameに従ってuserを調べ、userメソッドをcreateUserメソッドに渡して処理し、userメソッドをgetAllAuthMenuByUserメソッドに渡してこのユーザのすべてのライセンスメニューを取得する.
そしてno sessionになりました.
私が考えていたのは、方法の間で相手を伝え合って、sessionがなぜか閉まって、それから間違ったことを報告しました.createUserのコードをloadUserByUsernameに入れると正常だったのを覚えていますが、変更が長すぎて、このように変更しませんでした.最後にメソッドに加えたトランザクション.
それから私は試み始めて、Serviceの間で互いに相手を伝え始めました.残念ながら、どのように伝えても使いやすいです.問題の再現に失敗したのは、Spring Securityと何の関係があるのか分からない.
総括と思考
ネット上には、複数の文の性能が悪いという文章がある.教師が学生とクラスを持ち出すには、SQL文を複数実行する必要があり、クエリーの効率化に最適化できます.
私は自分の観点を述べます:1対がどれだけ遅いかは、どれだけの文を実行したからではなく、データベースが1対多、多対多を調べるのが遅いからです.
実体図を使用してSQLに最適化できても、データベースがどのように1対多、多対多を調べるか、どのように調べるかは、文が少なく見えるだけで、性能は実際には向上していないと思います.
実体図は、私が教師を必要とするname、クラスのname、学生のnameを配置し、私たちのデータベースで学んだように、私のこの方法を調べてみました.
select teacher.name, klass.name, student.name where xxxxx;
Javaの盛んな発展に伴い、Javaの性能はC++に及ばないが、運行効率も極めて高い.効率のボトルネックか、データベースかを見てみましょう.Redisはあまりありませんか?