Gambaran Keseluruhan Pengenal dalam Hibernate / JPA

1. Pengenalan

Pengecam dalam Hibernate mewakili kunci utama entiti. Ini menyiratkan nilai-nilai unik sehingga mereka dapat mengenal pasti entiti tertentu, yang tidak nol dan tidak akan diubah.

Hibernate menyediakan beberapa cara berbeza untuk menentukan pengecam. Dalam artikel ini, kami akan mengkaji setiap kaedah pemetaan id entiti menggunakan perpustakaan.

2. Pengecam Mudah

Cara paling mudah untuk menentukan pengecam adalah dengan menggunakan anotasi @Id .

Id ringkas dipetakan menggunakan @Id ke satu sifat dari salah satu jenis ini: Jenis pembungkus primitif dan primitif Java, String, Date, BigDecimal, BigInteger.

Mari lihat contoh ringkas menentukan entiti dengan kunci utama jenis panjang:

@Entity public class Student { @Id private long studentId; // standard constructor, getters, setters }

3. Pengenal Dihasilkan

Sekiranya kita mahu nilai kunci utama dihasilkan secara automatik untuk kita, kita boleh menambahkan anotasi @GeneratedValue .

Ini boleh menggunakan 4 jenis generasi: AUTO, IDENTITY, SEQUENCE, TABLE.

Sekiranya kita tidak menentukan nilai secara eksplisit, jenis generasi akan ditetapkan secara automatik kepada AUTO.

3.1. Penjanaan AUTO

Sekiranya kita menggunakan jenis generasi lalai, penyedia kegigihan akan menentukan nilai berdasarkan jenis atribut kunci utama. Jenis ini boleh berangka atau UUID.

Untuk nilai numerik, penjanaannya berdasarkan turutan atau penjana jadual, sementara nilai UUID akan menggunakan UUIDGenerator.

Mari lihat contoh pemetaan kunci utama entiti menggunakan strategi penjanaan AUTO:

@Entity public class Student { @Id @GeneratedValue private long studentId; // ... }

Dalam kes ini, nilai kunci utama akan unik di peringkat pangkalan data.

Ciri menarik yang diperkenalkan dalam Hibernate 5 adalah UUIDGenerator. Untuk menggunakan ini, yang perlu kita buat adalah menyatakan id jenis UUID dengan anotasi @GeneratedValue :

@Entity public class Course { @Id @GeneratedValue private UUID courseId; // ... }

Hibernate akan menghasilkan id dari bentuk "8dd5f315-9788-4d00-87bb-10eed9eff566".

3.2. Penjanaan IDENTITI

Jenis generasi ini bergantung pada IdentityGenerator yang mengharapkan nilai yang dihasilkan oleh lajur identiti dalam pangkalan data, yang bermaksud ia akan meningkat secara automatik.

Untuk menggunakan jenis generasi ini, kita hanya perlu menetapkan parameter strategi :

@Entity public class Student { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) private long studentId; // ... }

Satu perkara yang perlu diberi perhatian ialah generasi IDENTITI melumpuhkan kemas kini kumpulan.

3.3. SEQUENCE Generation

Untuk menggunakan id berdasarkan urutan, Hibernate menyediakan kelas SequenceStyleGenerator .

Penjana ini menggunakan urutan jika disokong oleh pangkalan data kami, dan beralih ke penjanaan jadual jika tidak.

Untuk menyesuaikan nama urutan, kita boleh menggunakan anotasi @GenericGenerator dengan strategi SequenceStyleGenerator:

@Entity public class User { @Id @GeneratedValue(generator = "sequence-generator") @GenericGenerator( name = "sequence-generator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = { @Parameter(name = "sequence_name", value = "user_sequence"), @Parameter(name = "initial_value", value = "4"), @Parameter(name = "increment_size", value = "1") } ) private long userId; // ... }

Dalam contoh ini, kami juga menetapkan nilai awal untuk urutan, yang bermaksud penjanaan kunci utama akan bermula pada 4.

SEQUENCE adalah jenis generasi yang disyorkan oleh dokumentasi Hibernate.

Nilai yang dihasilkan unik setiap urutan. Sekiranya anda tidak menentukan nama urutan, Hibernate akan menggunakan semula hibernate_sequence yang sama untuk pelbagai jenis.

3.4. Penjanaan JADUAL

The TableGenerator menggunakan jadual pangkalan data asas yang memegang segmen nilai generasi pengecam.

Mari sesuaikan nama jadual menggunakan anotasi @TableGenerator :

@Entity public class Department { @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "table-generator") @TableGenerator(name = "table-generator", table = "dep_ids", pkColumnName = "seq_id", valueColumnName = "seq_value") private long depId; // ... }

Dalam contoh ini, kita dapat melihat bahawa atribut lain seperti pkColumnName dan valueColumnName juga dapat disesuaikan.

Kelemahan kaedah ini adalah bahawa ia tidak skala dengan baik dan boleh mempengaruhi prestasi secara negatif.

Kesimpulannya, empat jenis generasi ini akan menghasilkan nilai serupa yang dihasilkan tetapi menggunakan mekanisme pangkalan data yang berbeza.

3.5. Penjana Tersuai

Sekiranya kita tidak mahu menggunakan strategi luar, kita dapat menentukan penjana tersuai kita dengan menerapkan antara muka IdentifierGenerator .

Mari buat penjana yang membina pengecam yang mengandungi awalan String dan nombor:

public class MyGenerator implements IdentifierGenerator, Configurable { private String prefix; @Override public Serializable generate( SharedSessionContractImplementor session, Object obj) throws HibernateException { String query = String.format("select %s from %s", session.getEntityPersister(obj.getClass().getName(), obj) .getIdentifierPropertyName(), obj.getClass().getSimpleName()); Stream ids = session.createQuery(query).stream(); Long max = ids.map(o -> o.replace(prefix + "-", "")) .mapToLong(Long::parseLong) .max() .orElse(0L); return prefix + "-" + (max + 1); } @Override public void configure(Type type, Properties properties, ServiceRegistry serviceRegistry) throws MappingException { prefix = properties.getProperty("prefix"); } }

In this example, we override the generate() method from the IdentifierGenerator interface and first find the highest number from the existing primary keys of the form prefix-XX.

Then we add 1 to the maximum number found and append the prefix property to obtain the newly generated id value.

Our class also implements the Configurable interface, so that we can set the prefix property value in the configure() method.

Next, let's add this custom generator to an entity. For this, we can use the @GenericGenerator annotation with a strategy parameter that contains the full class name of our generator class:

@Entity public class Product { @Id @GeneratedValue(generator = "prod-generator") @GenericGenerator(name = "prod-generator", parameters = @Parameter(name = "prefix", value = "prod"), strategy = "com.baeldung.hibernate.pojo.generator.MyGenerator") private String prodId; // ... }

Also, notice we've set the prefix parameter to “prod”.

Let's see a quick JUnit test for a clearer understanding of the id values generated:

@Test public void whenSaveCustomGeneratedId_thenOk() { Product product = new Product(); session.save(product); Product product2 = new Product(); session.save(product2); assertThat(product2.getProdId()).isEqualTo("prod-2"); }

Here, the first value generated using the “prod” prefix was “prod-1”, followed by “prod-2”.

4. Composite Identifiers

Besides the simple identifiers we've seen so far, Hibernate also allows us to define composite identifiers.

A composite id is represented by a primary key class with one or more persistent attributes.

The primary key class must fulfill several conditions:

  • it should be defined using @EmbeddedId or @IdClass annotations
  • it should be public, serializable and have a public no-arg constructor
  • it should implement equals() and hashCode() methods

The class's attributes can be basic, composite or ManyToOne while avoiding collections and OneToOne attributes.

4.1. @EmbeddedId

To define an id using @EmbeddedId, first we need a primary key class annotated with @Embeddable:

@Embeddable public class OrderEntryPK implements Serializable { private long orderId; private long productId; // standard constructor, getters, setters // equals() and hashCode() }

Next, we can add an id of type OrderEntryPK to an entity using @EmbeddedId:

@Entity public class OrderEntry { @EmbeddedId private OrderEntryPK entryId; // ... }

Let's see how we can use this type of composite id to set the primary key for an entity:

@Test public void whenSaveCompositeIdEntity_thenOk() { OrderEntryPK entryPK = new OrderEntryPK(); entryPK.setOrderId(1L); entryPK.setProductId(30L); OrderEntry entry = new OrderEntry(); entry.setEntryId(entryPK); session.save(entry); assertThat(entry.getEntryId().getOrderId()).isEqualTo(1L); }

Here the OrderEntry object has an OrderEntryPK primary id formed of two attributes: orderId and productId.

4.2. @IdClass

The @IdClass annotation is similar to the @EmbeddedId, except the attributes are defined in the main entity class using @Id for each one.

The primary-key class will look the same as before.

Let's rewrite the OrderEntry example with an @IdClass:

@Entity @IdClass(OrderEntryPK.class) public class OrderEntry { @Id private long orderId; @Id private long productId; // ... }

Then we can set the id values directly on the OrderEntry object:

@Test public void whenSaveIdClassEntity_thenOk() { OrderEntry entry = new OrderEntry(); entry.setOrderId(1L); entry.setProductId(30L); session.save(entry); assertThat(entry.getOrderId()).isEqualTo(1L); }

Note that for both types of composite ids, the primary key class can also contain @ManyToOne attributes.

Hibernate also allows defining primary-keys made up of @ManyToOne associations combined with @Id annotation. In this case, the entity class should also fulfill the conditions of a primary-key class.

The disadvantage of this method is that there's no separation between the entity object and the identifier.

5. Derived Identifiers

Derived identifiers are obtained from an entity's association using the @MapsId annotation.

First, let's create a UserProfile entity which derives its id from a one-to-one association with the User entity:

@Entity public class UserProfile { @Id private long profileId; @OneToOne @MapsId private User user; // ... }

Next, let's verify that a UserProfile instance has the same id as its associated User instance:

@Test public void whenSaveDerivedIdEntity_thenOk() { User user = new User(); session.save(user); UserProfile profile = new UserProfile(); profile.setUser(user); session.save(profile); assertThat(profile.getProfileId()).isEqualTo(user.getUserId()); }

6. Conclusion

Dalam artikel ini, kami telah melihat pelbagai cara untuk menentukan pengecam di Hibernate.

Kod sumber lengkap contoh boleh didapati di GitHub.