26 Haziran 2018 Salı

JPA @Entity Anotasyonu - No-Arg Constructor Gerektirir

Giriş
Şu satırı dahil ederiz.
import javax.persistence.Entity;
Kurallar
JPA tarafından belirtilen kurallar şöyle
According to the JPA specification, Entity is a Java class that meets the following requirements:

1. Annotated with @Entity annotation
2. Has no-args constructor
3. Is not final
4. Has an ID field (or fields) annotated with @Id 
Kural 1
Bir sınıfın JPA ile kullanılabilmesi için @Entity ile işaretlenmesi gerekir.
@Entity
public class MyEntity {
...
}
Kural 2 - Entity Sınıfı No-Arg Constructor
Entity sınıfı public veya protected bir no-arg constructor'a sahip olmalıdır. Açıklaması şöyle. Aslında bu kural @Entity, @MappedSuperclass, @Embeddable için de geçerli.
The entity class must have a no-arg constructor. It may have other constructors as well. The no-arg constructor must be public or protected.
Eğer no-arg constructor yoksa Hibernate şöyle bir exception fırlatır
org.hibernate.InstantiationException: No default constructor for entity .
Bu kural son zamanlarda sorgulanıyor. Should We Have a Constructor on JPA? yazısına bakabilirsiniz. Öneri şöyle
"JPA requiring default constructors pretty much everywhere is a severe limitation to the entity design for dozens of reasons. Records make that pretty obvious. So, while you can argue that Persistence doesn't 'need ' to do anything regarding this aspect, I think it should. Because improving on this would broadly benefit Persistence, not only in persisting records." Oliver Drotbohm
Eğer bu öneri hayata geçerse şöyle kodlar olabilir
@Entity
public record Person(@Id @NotNull Long id,  @NotBlank @Column String name){}
Kural 3 - Entity Sınıfı final Olmamalıdır
Açıklaması şöyle
The entity class must not be final. No methods or persistent instance variables of the entity class may be final.
Bu maddenin gerekçesi proxy nesneler kullanarak "lazy loading" yapabilmek. Açıklaması şöyle
As per the JPA specification, all JPA-related classes and properties must be open. Some JPA providers don’t enforce this rule. For example, Hibernate does not throw an exception when it encounters a final entity class. However, a final class cannot be subclassed, hence the proxying mechanism of Hibernate turns off. No proxies and no lazy loading. Effectively, this means that all ToOne associations will be always eagerly fetched. This can lead to significant performance issues. The situation is different for EclipseLink with static weaving, as it doesn’t use subclassing for its lazy loading mechanism.
Örnek
Bu örnek JPA'ya göre yanlıştır.
@Entity
public final class MyEntity {
    ...
}
Kural 4- equals() ve hashCode() metodları
Bu yazılı bir kural değil. Benim kuralım. Dikkat edilmesi gereken nokta, hashCode() metodunun her zaman aynı değeri, dönmesi. Lombok kullanırken kaydedilmemiş bir entity ve kaydedildikten sonra @Id anotasyonu yüzünden farklı sonuç dönebiliyor. Özellikle Lombok bu tür hatalara sebep olabiliyor. "jpa Buddy" bu tür şeyleri yakalıyor

Örnek
Şu kod yanlış.
@EqualsAndHashCode
public class TestEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(nullable = false)
  private Long id;

}

TestEntity testEntity = new TestEntity();
Set<TestEntity> set = new HashSet<>();

set.add(testEntity);
testEntityRepository.save(testEntity);

Assert.isTrue(set.contains(testEntity), "Entity not found in the set");
Düzeltmek için şöyle yaparız
@Override  
public boolean equals(Object o) {  
  if (this == o) return true;  
  if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o))   
    return false;  
  User user = (User) o;  
  return id != null && Objects.equals(id, user.id);  
}  

@Override  
public int hashCode() {  
  return getClass().hashCode();  
}  
Entity Sınıfı Enum veya Interface Olmamalıdır
 Açıklaması şöyle
  • The entity class must a be top-level class. An enum or interface must not not be designated as an entity.
  • If an entity instance is to be passed by value as a detached object (e.g., through a remote interface), the entity class must implement the Serializable interface.
  • Both abstract and concrete classes can be entities. Entities may extend non-entity classes as well as entity classes, and non-entity classes may extend entity classes.
Entity ve Simple Class Name
@Entity ile işaretli sınıfa "Full Class Name" veya "Unqualified Class Name" kullanılarak erişilebilir. Şöyle yaparız.
entityManager.createQuery(
String.format("delete from %s where id = :id", Foo.class.getName()))
  .setParameter("id", fooId)
  .executeUpdate();
Hibernate İle Kullanımı
Bir çok JPA sağlayıcısı, JPA ile gelmeyen diğer özellikler de sunar. Bu özellikler kullanılabilir.
@Entity
@org.hibernate.annotations.Entity(optimisticLock=OptimisticLockType.ALL)
public class MyEntity implements Serializable {
...
}

Entity ve DTO
Hibernate'in açıklaması şöyle. Yani veri tabanından gelen nesneleri DTO'ya çevirmeden üst katmanlara verebiliyor ve yaratabilirsiniz diyor. Ancak ben bu öneriye katılmıyorum
1. You can reuse persistent classes outside the context of persistence, in unit tests or in the presentation layer, for example. You can create instances in any runtime environment with the regular Java new operator, preserving testability and reusability;

2. Hibernate entities do not need to be explicitly Serializable.
DTO kullanmak bence her zaman faydalı bir şey çünkü DTO bizi veri tabanından ayırır ve nesneyi zenginleştirme imkanı sağlar. Şeklen şöyle











Hiç yorum yok:

Yorum Gönder