15 Eylül 2020 Salı

JPA @GeneratedValue Anotasyonu - Id Alanı İçin Değer Üretilmesini Sağlar

Giriş
Şu satırları dahil ederiz.
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
İskeleti şöyle
public @interface GeneratedValue {

  GenerationType strategy() default AUTO;

  String generator() default "";
}
Açıklaması şöyle.
The @GeneratedValue annotation denotes that a value for a column, which must be annotated with @Id is generated. The elements strategy and generator on the annotation describe how the generated value is obtained.
strategy alanı Id Alanı için değeri veri tabanı üretecekse kullanılır. 
generator alanı Id Alanı için değeri kodla biz üreteceksek kullanılır. 

Varsayılan Değelerle Kullanmak

Şöyle yaparız. Burada Id değerlerini veri tabanı üretiyor ve ayarları JPA sağlayıcısına bırakıyoruz
@Entity
public class Cheese {

  @Id
  @GeneratedValue
  private int id;
  ...
}
1. generator Alanı
generator kendi kodumuzla bir değer üreteceksek kullanılır. Örneğin Hibernate projesine ait @GenericGenerator kullanılabilir.

Örnek
Şöyle yaparız.
public class StringIdGenerator implements IdentifierGenerator {

  public Serializable generate(SessionImplementor session, Object object)
  throws HibernateException {
    ...
  }
}

@Entity
@GenericGenerator(name = "pt_sequence", strategy = "dao.StringIdGenerator")
public class P {
  @Id
  @GeneratedValue(strategy=GenerationType.IDENTITY,generator = "pt_sequence")
  String id; 
  ...
} 
2. strategy Alanı
Eğer veri tabanı bir değer üretecekse kullanılır.

1. AUTO Seçeneği
AUTO identifier GeneratorType yazısına taşıdım

2. IDENTITY Seçeneği - Veri tabanına Özeldir
IDENTITY identifier GeneratorType yazısına taşıdım

3. SEQUENCE Seçeneği - Veri tabanına Özeldir
İki kullanım şekli var
1. JPA kendisi bir sequence yaratır ve kullanır
2. JPA bizim belirttiğimiz sequence'ı kullanır

JPA kendisi bir sequence yaratırsa
Açıklaması şöyle.
Some database vendors support the use of a database sequence object for maintaining primary keys. To use a sequence, you set the GenerationType strategy to SEQUENCE, specify the name of the generator annotation, and then provide the @SequenceGenerator annotation that has attributes for defining both the name of the sequence annotation, and the name of the actual sequence object in the database.
Eğer kendi sequence tanımımızı yapmazsak Hibernate ismi hibernate_sequence olan bir taneyi kullanmaya çalışır. Eğer bu sequence yoksa şu exception fırlatılır
org.hibernate.HibernateException: Missing sequence or table: hibernate_sequence
Örnek
PostgreSQL'de şöyle yaparız. Bu durumda Hibernate ismi hibernate_sequence nesnesinden sayıları sıra ile çeker.
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)  
private Integer pid;
JPA bizim belirttiğimiz sequence'ı kullanırsa
Eğer kendi sequence tanımımızı yapmak istersek @SequenceGenerator anotasyonunu kullanmak gerekir.


4. Table Seçeneği
Açıklaması şöyle. Ben bu yöntemi bir kere Vitess veri tabanı ile kullanmıştım
The GenerationType.TABLE gets only rarely used nowadays. It simulates a sequence by storing and updating its current value in a database table which requires the use of pessimistic locks which put all transactions into a sequential order. This slows down your application, and you should, therefore, prefer the GenerationType.SEQUENCE, if your database supports sequences, which most popular databases do.
5. UUID Seçeneği
Açıklaması şöyle
Jakarta Persistence 3.1 brings java.util.UUID is to be used as a basic type of field, which is very convenient for entity IDs for the cloud environment since many databases don't automatically generate UUID.
Örnek
Şöyle yaparız
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import java.util.UUID;

@Entity
public class User {

  @Id
  @GeneratedValue(strategy = GenerationType.UUID)
  private UUID id;
  ...
}
Diğer
AUTO ve SEQUENCE İçin Performans Artırımı
Varsayılan AUTO ve SEQUENCE kullanımı yeterli performansı sağlamayabilir. Hibernate kullanımında sebeplerinden bazıları şöyle
1. Hibernate tek bir sequence yaratır ve pooling yapmaz. Sequence şöyle yaratılır
CREATE SEQUENCE hibernate_sequence START 1 INCREMENT 1;
Bu durumda INSERT cümleleri şöyledir. Her INSERT için sequence bir kere daha kullanılır
SELECT NEXTVAL ('hibernate_sequence')
INSERT INTO pet (name, id) VALUES (?, ?)
SELECT NEXTVAL ('hibernate_sequence') INSERT INTO pet (name, id) VALUES
(?, ?)
Eğer sequence için isim verirsek bu sefer sequence biraz farklı yaratılır. Şöyle yapalım
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pet_seq")
@Column(name = "id", nullable = false)
private Long id;
Sequence şöyle yaratılır
CREATE SEQUENCE hibernate_sequence START 1 INCREMENT 50;
Havuz kullanımı şöyle
The optimization works in the following way: 

Step 1: Hibernate executes one SELECT to get the ID from the sequence.
Step 2: If the selected value is equal to the sequence initial value, the Hibernate selects the next ID from the sequence as a high value, setting the initial value as a range low value. Otherwise, it goes to step 4.
Step 3: Hibernates inserts data assigning IDs from low to `high` range.
Step 4: As soon as Hibernate needs the next batch, it selects the next ID value from the sequence (which is bigger than the initial value). Hibernate calculates the available range for this ID based on the allocationSize parameter. Low value = ID – allocationSize, high = ID. Then Hibernate goes to step 3. 
Kod olarak şöyle. Yani havuz boşalınca sequence'tan sonraki değer çeker. Bu değer üst sınırdır. Eldeki değere bakarak alt değeri bulur
SELECT NEXTVAL ('pet_seq'); //selects 1 – got initial value, need to select next value
SELECT NEXTVAL ('pet_seq'); //selects 51 as range high value
INSERT INTO pet (name, id) VALUES (?, ?);// id=1 INSERT INTO pet (name, id) VALUES (?, ?);//id=2
//insert other 48 entities //selects 101 as range next high value, calculates 101 – 50 = 51 as the low SELECT NEXTVAL ('pet_seq'); INSERT INTO pet (name, id) VALUES (?, ?);//id=51
//etc.
Veri tabanı bağlantısı kapatılırsa havuzdaki değeler kaybolur. Dolayısıyla eğer 50'şer artırım yüksekse ayarlamak için şöyle yaparız
@Id
@SequenceGenerator(name = "pet_seq", 
        sequenceName = "pet_sequence", 
        initialValue = 1, allocationSize = 20)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pet_seq")
@Column(name = "id", nullable = false)
private Long id;






Hiç yorum yok:

Yorum Gönder