Unjuran JPA / Hibernate

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan belajar bagaimana memproyeksikan sifat entiti menggunakan JPA dan Hibernate .

2. Entiti

Pertama, mari kita lihat entiti yang akan kita gunakan sepanjang artikel ini:

@Entity public class Product { @Id private long id; private String name; private String description; private String category; private BigDecimal unitPrice; // setters and getters }

Ini adalah kelas entiti sederhana yang mewakili produk dengan pelbagai sifat.

3. Unjuran JPA

Walaupun spesifikasi JPA tidak menyebutkan unjuran secara eksplisit, ada banyak kes di mana kita menemukannya secara konsep.

Biasanya, pertanyaan JPQL mempunyai kelas entiti calon. Pertanyaan, semasa pelaksanaan, membuat objek dari kelas calon - mengisi semua sifat mereka menggunakan data yang diambil.

Tetapi, mungkin untuk mendapatkan subkumpulan sifat entiti, atau, iaitu unjuran data lajur.

Selain data lajur, kami juga dapat memproyeksikan hasil fungsi pengelompokan.

3.1. Unjuran Lajur Tunggal

Katakan kita mahu menyenaraikan nama semua produk. Dalam JPQL, kita dapat melakukan ini dengan memasukkan hanya nama dalam klausa pilih :

Query query = entityManager.createQuery("select name from Product"); List resultList = query.getResultList();

Atau, kita boleh melakukan perkara yang sama dengan CriteriaBuilder :

CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery query = builder.createQuery(String.class); Root product = query.from(Product.class); query.select(product.get("name")); List resultList = entityManager.createQuery(query).getResultList();

Kerana kita mengunjurkan lajur tunggal yang berlaku untuk menjadi jenis String , kami menjangka untuk mendapatkan senarai String s dalam keputusan. Oleh itu, kami telah menentukan kelas calon sebagai String dalam kaedah createQuery () .

Oleh kerana kami mahu memproyeksikan satu harta tanah, kami telah menggunakan kaedah Query.select () . Apa yang ada di sini adalah harta yang kita mahukan, jadi dalam kes kita, kita akan menggunakan nama harta dari entiti Produk kita .

Sekarang, mari kita lihat contoh output yang dihasilkan oleh dua pertanyaan di atas:

Product Name 1 Product Name 2 Product Name 3 Product Name 4

Perhatikan bahawa jika kita menggunakan properti id dalam unjuran dan bukan nama , pertanyaan akan mengembalikan senarai objek Panjang .

3.2. Unjuran Pelbagai Lajur

Untuk memproyeksikan beberapa lajur menggunakan JPQL, kita hanya perlu menambahkan semua lajur yang diperlukan pada klausa pilih :

Query query = session.createQuery("select id, name, unitPrice from Product"); List resultList = query.getResultList();

Tetapi, apabila menggunakan CriteriaBuilder , kita harus melakukan perkara yang sedikit berbeza:

CriteriaBuilder builder = session.getCriteriaBuilder(); CriteriaQuery query = builder.createQuery(Object[].class); Root product = query.from(Product.class); query.multiselect(product.get("id"), product.get("name"), product.get("unitPrice")); List resultList = entityManager.createQuery(query).getResultList();

Di sini, kami telah menggunakan kaedah multiselect () dan bukannya pilih () . Dengan menggunakan kaedah ini, kita dapat menentukan beberapa item yang akan dipilih.

Perubahan ketara yang lain adalah penggunaan Objek [] . Apabila kita memilih beberapa item, pertanyaan mengembalikan array objek dengan nilai untuk setiap item yang diproyeksikan. Ini juga berlaku dengan JPQL.

Mari lihat seperti apa data semasa kami mencetaknya:

[1, Product Name 1, 1.40] [2, Product Name 2, 4.30] [3, Product Name 3, 14.00] [4, Product Name 4, 3.90]

Seperti yang dapat kita lihat, data yang dikembalikan agak rumit untuk diproses. Tetapi, untungnya, kita dapat membuat JPA mengisi data ini ke dalam kelas khusus.

Juga, kita dapat menggunakan CriteriaBuilder.tuple () atau CriteriaBuilder.construct () untuk mendapatkan hasilnya sebagai senarai objek Tuple atau objek kelas khusus masing-masing.

3.3. Mengunjurkan Fungsi Agregat

Selain daripada data lajur, kadang-kadang kita mungkin ingin mengumpulkan data dan menggunakan fungsi agregat, seperti kiraan dan rata - rata.

Katakan kita mahu mencari jumlah produk dalam setiap kategori. Kita boleh melakukannya dengan menggunakan fungsi kiraan agregat dalam JPQL:

Query query = entityManager.createQuery("select p.category, count(p) from Product p group by p.category");

Atau kita boleh menggunakan CriteriaBuilder :

CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery query = builder.createQuery(Object[].class); Root product = query.from(Product.class); query.multiselect(product.get("category"), builder.count(product)); query.groupBy(product.get("category"));

Di sini, kami telah menggunakan CriteriaBuilder 's kiraan () kaedah.

Menggunakan salah satu perkara di atas akan menghasilkan senarai susunan objek:

[category1, 2] [category2, 1] [category3, 1]

Selain kiraan () , CriteriaBuilder menyediakan pelbagai fungsi agregat lain:

  • avg - Mengira nilai purata untuk lajur dalam kumpulan
  • max - Mengira nilai maksimum untuk lajur dalam kumpulan
  • min - Mengira nilai minimum untuk lajur dalam kumpulan
  • paling sedikit - Mencari nilai lajur paling sedikit (contohnya, mengikut abjad atau tarikh)
  • sum - Mengira jumlah nilai lajur dalam kumpulan

4. Unjuran Hibernate

Unlike JPA, Hibernate provides org.hibernate.criterion.Projection for projecting with a Criteria query. It also provides a class called org.hibernate.criterion.Projections, a factory for Projection instances.

4.1. Single-Column Projections

First, let's see how we can project a single column. We'll use the example we saw earlier:

Criteria criteria = session.createCriteria(Product.class); criteria = criteria.setProjection(Projections.property("name")); 

We've used the Criteria.setProjection() method to specify the property that we want in the query result. Projections.property() does the same work for us as Root.get() did when indicating the column to select.

4.2. Multi-Column Projections

To project multiple columns, we'll have to first create a ProjectionList. ProjectionList is a special kind of Projection that wraps other projections to allow selecting multiple values.

We can create a ProjectionListusing the Projections.projectionList() method, like showing the Product‘s id and name:

Criteria criteria = session.createCriteria(Product.class); criteria = criteria.setProjection( Projections.projectionList() .add(Projections.id()) .add(Projections.property("name")));

4.3. Projecting Aggregate Functions

Just like CriteriaBuilder, the Projections class also provides methods for aggregate functions.

Let's see how we can implement the count example we saw earlier:

Criteria criteria = session.createCriteria(Product.class); criteria = criteria.setProjection( Projections.projectionList() .add(Projections.groupProperty("category")) .add(Projections.rowCount()));

It's important to note that we didn't directly specify the GROUP BY in the Criteria object. Calling groupProperty triggers this for us.

Apart from the rowCount() function, Projections also provides the aggregate functions we saw earlier.

4.4. Using an Alias for a Projection

An interesting feature of the Hibernate Criteria API is the use of an alias for a projection.

This is especially useful when using an aggregate function, as we can then refer to the alias in the Criterion and Order instances:

Criteria criteria = session.createCriteria(Product.class); criteria = criteria.setProjection(Projections.projectionList() .add(Projections.groupProperty("category")) .add(Projections.alias(Projections.rowCount(), "count"))); criteria.addOrder(Order.asc("count"));

5. Conclusion

In this article, we saw how to project entity properties using JPA and Hibernate.

Penting untuk diperhatikan bahawa Hibernate telah menghentikan API Kriteria dari versi 5.2 dan seterusnya yang memihak kepada JPA CriteriaQuery API . Tetapi, ini hanya kerana pasukan Hibernate tidak mempunyai masa untuk menyimpan dua API berbeza, yang hampir sama melakukan perkara yang sama.

Dan tentu saja, kod yang digunakan dalam artikel ini boleh didapati di GitHub.