Panduan untuk Pemetaan SqlResultSet

1. Pengenalan

Dalam panduan ini, kita akan melihat SqlResultSetMapping , keluar dari Java Persistence API (JPA).

Fungsi teras di sini melibatkan set hasil pemetaan dari pernyataan SQL pangkalan data ke objek Java.

2. Persediaan

Sebelum kita melihat penggunaannya, mari kita lakukan beberapa persediaan.

2.1. Ketergantungan Maven

Pergantungan Maven yang kami perlukan adalah Pangkalan Data Hibernate dan H2. Hibernate memberi kita pelaksanaan spesifikasi JPA. Kami menggunakan Pangkalan Data H2 untuk pangkalan data dalam memori.

2.2. Pangkalan data

Seterusnya, kami akan membuat dua jadual seperti yang dilihat di sini:

CREATE TABLE EMPLOYEE (id BIGINT, name VARCHAR(10));

Jadual EMPLOYEE menyimpan satu objek Entiti hasil . SCHEDULE_DAYS mengandungi rekod yang dihubungkan ke jadual PEKERJA oleh lajur pekerjaId:

CREATE TABLE SCHEDULE_DAYS (id IDENTITY, employeeId BIGINT, dayOfWeek VARCHAR(10));

Skrip untuk pembuatan data boleh didapati dalam kod panduan ini.

2.3. Objek Entiti

Objek Entiti kami akan kelihatan serupa:

@Entity public class Employee { @Id private Long id; private String name; }

Objek entiti mungkin dinamakan berbeza daripada jadual pangkalan data. Kami dapat memberi penjelasan kelas dengan @ Jadual untuk memetakannya secara eksplisit:

@Entity @Table(name = "SCHEDULE_DAYS") public class ScheduledDay { @Id @GeneratedValue private Long id; private Long employeeId; private String dayOfWeek; }

3. Pemetaan Skalar

Setelah kita mempunyai data, kita dapat mulai memetakan hasil pertanyaan.

3.1. Keputusan Lajur

Walaupun anotasi SqlResultSetMapping dan Query berfungsi di kelas Repositori juga, kami menggunakan anotasi pada kelas Entity dalam contoh ini.

Setiap anotasi SqlResultSetMapping hanya memerlukan satu harta, nama. Walau bagaimanapun, tanpa salah satu jenis ahli, tidak ada yang akan dipetakan. Jenis ahli adalah ColumnResult , ConstructorResult , dan EntityResult .

Dalam kes ini, ColumnResult memetakan mana-mana lajur ke jenis hasil skalar:

@SqlResultSetMapping( name="FridayEmployeeResult", columns={@ColumnResult(name="employeeId")})

The ColumnResult harta nama mengenal pasti ruang dalam pertanyaan kami:

@NamedNativeQuery( name = "FridayEmployees", query = "SELECT employeeId FROM schedule_days WHERE dayOfWeek = 'FRIDAY'", resultSetMapping = "FridayEmployeeResult") 

Perhatikan bahawa nilai resultSetMapping dalam anotasi NamedNativeQuery kami penting kerana ia sepadan dengan harta nama dari deklarasi ResultSetMapping kami .

Hasilnya, set hasil NamedNativeQuery dipetakan seperti yang diharapkan. Begitu juga, StoredProcedure API memerlukan persatuan ini.

3.2. Ujian ColumnResult

Kami memerlukan beberapa objek khusus Hibernate untuk menjalankan kod kami:

@BeforeAll public static void setup() { emFactory = Persistence.createEntityManagerFactory("java-jpa-scheduled-day"); em = emFactory.createEntityManager(); }

Akhirnya, kami memanggil pertanyaan bernama untuk menjalankan ujian kami:

@Test public void whenNamedQuery_thenColumnResult() { List employeeIds = em.createNamedQuery("FridayEmployees").getResultList(); assertEquals(2, employeeIds.size()); }

4. Pemetaan Konstruktor

Mari kita perhatikan kapan kita perlu memetakan hasil yang ditetapkan ke seluruh objek.

4.1. Keputusan Pembina

Sama dengan kami ColumnResult contoh, kita akan menambah SqlResultMapping anotasi pada kami Entity kelas, ScheduledDay . Walau bagaimanapun, untuk memetakan menggunakan konstruktor, kita perlu membuatnya:

public ScheduledDay ( Long id, Long employeeId, Integer hourIn, Integer hourOut, String dayofWeek) { this.id = id; this.employeeId = employeeId; this.dayOfWeek = dayofWeek; }

Juga, pemetaan menentukan kelas sasaran dan lajur (keduanya diperlukan):

@SqlResultSetMapping( name="ScheduleResult", classes={ @ConstructorResult( targetClass=com.baeldung.sqlresultsetmapping.ScheduledDay.class, columns={ @ColumnResult(name="id", type=Long.class), @ColumnResult(name="employeeId", type=Long.class), @ColumnResult(name="dayOfWeek")})})

Urutan ColumnResults sangat penting. Sekiranya lajur tidak teratur, konstruktor akan gagal dikenal pasti. Dalam contoh kami, susunannya sesuai dengan lajur jadual, jadi sebenarnya tidak diperlukan.

@NamedNativeQuery(name = "Schedules", query = "SELECT * FROM schedule_days WHERE employeeId = 8", resultSetMapping = "ScheduleResult")

Perbezaan unik lain untuk ConstructorResult adalah bahawa instansiasi objek yang dihasilkan sebagai "baru" atau "terpisah". Entiti yang dipetakan akan berada dalam keadaan terpisah apabila kunci utama yang sesuai ada di EntityManager jika tidak, ia akan menjadi baru.

Kadang-kadang kita mungkin menghadapi ralat runtime kerana tidak sepadan dengan jenis data SQL ke jenis data Java. Oleh itu, kita dapat menyatakannya secara jelas dengan jenis.

4.2. Ujian Konstruktor

Mari kita menguji ConstructorResult dalam ujian unit:

@Test public void whenNamedQuery_thenConstructorResult() { List scheduleDays = Collections.checkedList( em.createNamedQuery("Schedules", ScheduledDay.class).getResultList(), ScheduledDay.class); assertEquals(3, scheduleDays.size()); assertTrue(scheduleDays.stream().allMatch(c -> c.getEmployeeId().longValue() == 3)); }

5. Pemetaan Entiti

Akhirnya, untuk pemetaan entiti sederhana dengan kod yang lebih sedikit, mari kita lihat EntityResult .

5.1. Entiti Tunggal

EntityResult requires us to specify the entity class, Employee. We use the optional fields property for more control. Combined with FieldResult, we can map aliases and fields that do not match:

@SqlResultSetMapping( name="EmployeeResult", entities={ @EntityResult( entityClass = com.baeldung.sqlresultsetmapping.Employee.class, fields={ @FieldResult(name="id",column="employeeNumber"), @FieldResult(name="name", column="name")})})

Now our query should include the aliased column:

@NamedNativeQuery( name="Employees", query="SELECT id as employeeNumber, name FROM EMPLOYEE", resultSetMapping = "EmployeeResult")

Similarly to ConstructorResult, EntityResult requires a constructor. However, a default one works here.

5.2. Multiple Entities

Mapping multiple entities is pretty straightforward once we have mapped a single Entity:

@SqlResultSetMapping( name = "EmployeeScheduleResults", entities = { @EntityResult(entityClass = com.baeldung.sqlresultsetmapping.Employee.class), @EntityResult(entityClass = com.baeldung.sqlresultsetmapping.ScheduledDay.class)

5.3. EntityResult Tests

Let's have a look at EntityResult in action:

@Test public void whenNamedQuery_thenSingleEntityResult() { List employees = Collections.checkedList( em.createNamedQuery("Employees").getResultList(), Employee.class); assertEquals(3, employees.size()); assertTrue(employees.stream().allMatch(c -> c.getClass() == Employee.class)); }

Since the multiple entity results join two entities, the query annotation on only one of the classes is confusing.

Atas sebab itu, kami menentukan pertanyaan dalam ujian:

@Test public void whenNamedQuery_thenMultipleEntityResult() { Query query = em.createNativeQuery( "SELECT e.id, e.name, d.id, d.employeeId, d.dayOfWeek " + " FROM employee e, schedule_days d " + " WHERE e.id = d.employeeId", "EmployeeScheduleResults"); List results = query.getResultList(); assertEquals(4, results.size()); assertTrue(results.get(0).length == 2); Employee emp = (Employee) results.get(1)[0]; ScheduledDay day = (ScheduledDay) results.get(1)[1]; assertTrue(day.getEmployeeId() == emp.getId()); }

6. Kesimpulannya

Dalam panduan ini, kami melihat pelbagai pilihan untuk menggunakan anotasi SqlResultSetMapping . SqlResultSetMapping adalah bahagian penting untuk Java Persistence API.

Coretan kod boleh didapati di GitHub.