11 Kasım 2021 Perşembe

SpringData Projections - Open Projections - SpEL Kullanır

Giriş
Open Projections içinde SpEL kullanılır. Bu kullanımda Spring sorgu cümlesini optimize etmez. Açıklaması şöyle.
Spring Data cannot apply query execution optimizations in this case, because the SpEL expression could use any attribute of the aggregate root.
Örnek
Elimizde şöyle bir @Entity kodu olsun
@Data
@Entity
@Table(name = "CUSTOMER_ORDER")
public class OrderEntity {

  @Id
  @Column(name = "ID")
  private Integer id;

  @Column(name = "ORDER_NUMBER")
  private String orderNumber;

  @Column(name = "TOTAL_AMOUNT")
  private String totalAmount;

  @OneToOne
  @JoinColumn(name="CUSTOMER_ID", nullable=false)
  private CustomerEntity customer;
}
Daha sonra projection kodunu tanımlamak için şöyle yaparız
public interface CustomerDetailsDTO {

  Integer getCustomerId();

  @Value("#{target.firstName + ' ' + target.lastName}")
  String getCustomerName();

  String getCity();

  String getCountry();

  @Value("#{@mapperUtility.buildOrderDTO(target.orderNumber, target.totalAmount)}")
  OrderDTO getOrder();
}
Burada SpEL içinde bir başka kod kullanılıyor. O da şöyle
@Component
public class MapperUtility {

  public OrderDTO buildOrderDTO(Long orderNumber, Double totalAmount) {
    OrderDTO order = new OrderDTO();
    order.setOrderNumber(orderNumber);
    order.setTotalAmount("$" + totalAmount);
    return order;
  }
}

public class OrderDTO {
  private Long orderNumber;
  private String totalAmount;

  // Getters and Setter
}
Repository kodumuz şöyledir. Repository belirtilen SQL cümlesini çalıştırır, ancak dönüş tipi @Entity değil de bizim kodladığımı DTO olduğu için projection devreye girer.
@Repository
public interface CustomerRepository extends JpaRepository<CustomerEntity, Integer> {
  
  @Query(name = "customerEntity.getCustomerDetails", nativeQuery = true)
  List<CustomerDetailsDTO> getCustomerDetails();
}
Örnek
Elimizde şöyle bir @Entity kodu olsun. Post nesnesi ve buna ait Tag listesi olsun
class Post {
  @Id
  Long id;
  String title;
 @ManyToMany(fetch = FetchType.LAZY,
                cascade = {
                    CascadeType.PERSIST,
                    CascadeType.MERGE,

                })
  @JoinTable(name = "post_tags",
             joinColumns = { @JoinColumn(name = "post_id") },
             inverseJoinColumns = { @JoinColumn(name = "tag_id") })

  Set<Tag> tags;
  // constructor
  // getters
  // setters
}

class Tag{
  @Id
  Long id
  String name;
  // getters, setter, constructor
}
Daha sonra projection kodunu tanımlamak için şöyle yaparız.
interface PostProjection{
  Long getId();
  String getTitle();
  @Value("#{target.tags.size()}")
  int  getNumberOfTags();
}
SpEL yerine kodla projection yapılabilir. Şöyle yaparız.
import static java.util.stream.Collectors.toSet;

public interface PostProjection {
  String getTitle();

  Set<Tag> getTags();

  default Set<String> getTagsNames() {
    return getTags().stream().map(Tag::getName).collect(toSet());
  }

}

Hiç yorum yok:

Yorum Gönder