Blog top image

Data projection in Spring Boot


ডাটা প্রজেকশন কি?

JPA এবং হাইবারনেট এর কনটেক্সে প্রজেকশন হলো আমার কাছে অনেক বড় একটা অবজেক্ট আছে যার রয়েছে অনেক প্রপার্টিস। আমার ওয়েব সাইটের সব জায়গায় সব ইনফরমেশন দেখানো লাগেনা। তাই যেসব জায়গায় কম ইনফরমেশন দেখতে হয় তাদের জন্য ডাটাবেস থেকে কম ডাটা তুলব। এতে নেটওয়ার্ক ল্যাটেন্সি কমে আসবে, এপ্লিকেশন দ্রুত কাজ করবে। এটা লেজি লোডিং না। কারণ লেজি লোডিং শুধু মাত্র কালেশনের ক্ষেত্রে কাজ করে। আপনি একটা স্ট্রিং ফিল্ড লেজি দিয়ে বাদ দিতে পারবেন না।


বাস্তব উদাহরণ

সাধারণত ব্লগের পোস্ট অনেক বড় হয় এবং অনেক প্রকার ডাটা থাকে। যখন একটা ব্লগ পড়া হয় তখন সমস্ত ডাটা সার্ভার থেকে নিয়ে আসা দরকার। কিন্তু যখন হোম পেজে আমরা অনেক পোস্টার শুধু টাইটেল এবং সামান্য বর্ণনা দেখাই, তখন এই সব পোস্টের সম্পূর্ণ ডাটা নিয়ে আসার কোন মানেই হয় না। স্পেশালি পোস্টের বডি, যেখানে সম্পূর্ণ পোস্ট থাকে।


কোড

আমার  ব্লগের পোস্ট অবজেক্ট প্রায়ই পরিবর্তন হয়

@Table(name = "POST")
public class Post extends BaseAuditEntity {

    private static final long serialVersionUID = 1L;

    @Id @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    @Column(length = 32)
    protected String id;

    @NotNull
    @Size(max = 100)
    private String name;

    @Size(max = 150)
    private String title;

    @ManyToOne
    private Author author;

    @ManyToOne
    private FileMeta topImage;

    @Size(max = 250)
    private String description;

    private Integer position;

    @Lob
    @Basic(fetch=FetchType.LAZY)
    private String body;

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "post")
    private Set<PostComment> comments;

    @Enumerated(EnumType.STRING)
    private PostStatus postStatus;

    @OneToMany(fetch = FetchType.LAZY)
    private Set<FileMeta> images;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "POST_TAG",
            joinColumns = @JoinColumn(name = "post_id"),
            inverseJoinColumns = @JoinColumn(name = "tag_id")
    )
    private Set<Tag> tags;

    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
    private Seo seo;
}


এখানে নিচের কোডে লেজি কাজ করে না। মানে কোন ভাবেই পোস্ট কুয়েরি করলে বডি চলে আসে।

    @Lob
    @Basic(fetch=FetchType.LAZY)
    private String body;


এখানে একটা চমৎকার সমাধান হলো প্রজেকশন। দুই ভাবে প্রজেকশন করা যায়।

১. DTO এর মাধ্যমে

২. Interface ব্যবহার করে


DTO মাধ্যমে নেস্টেড অবজেক্টের প্রজেকশন করা যায় না। শুধুমাত্র একটা অবজেক্টের উপর কাজ করে। আপনি যদি একটা অবজেক্টের ভিতর আরেকটা অবজেক্ট আছে এবং সব গুলাকে প্রজেকশনের আওতায় আনতে চান তাহলে Interface প্রজেক্টশন করতে হবে।


আমি ইন্টারফেস প্রজেকশন দেখাবো। আমার পোস্টের ইন্টারফেস প্রজেকশন নিচের মতো

public interface PostProjection {

     String getId();

     String getName();

     String getTitle();

     FileMeta getTopImage();

     String getDescription();

    Date getCreatedAt();
}


যখন স্প্রিং ডাটা দিয়ে কুয়েরি করবেন, তখন আসল অবজেক্টের জায়গায় ইন্টারফের দিয়ে দিলেই সে শুধু মাত্র ইন্টারফেসের গেট ডাটা গুলা তুলে নিয়ে আসবে। নিচে রিপোজিটরি কুয়েরি দেয়া হলো

public interface PostRepository extends JpaRepository<Post, String> {
    @Query("SELECT p FROM Post p")
    Page<PostProjection> findAll(Pageable pageable);
}


এখন যদি কুয়েরি রেজাল্ট দেখেন অনেকটা নিচের মতো হবে

content": [
    {
      "description": null,
      "topImage": null,
      "createdAt": "2019-06-02T09:52:32.086+0000",
      "title": "স্প্রিং, JPA এবং হইবারনেট এর কনটেক্সে প্রোজেকশন কি?  কিভাবে ব্যবহার করে অশেষ ফায়দা নেয়া যায়। ",
      "name": "JPA, স্প্রিং ডাটা  প্রোজেকশন ",
      "id": "8a1c80976b15a299016b179ad756000f"
    },
    {
      "description": null,
      "topImage": null,
      "createdAt": "2019-06-02T08:16:24.036+0000",
      "title": "কি ভাবে  NGINX কনফিগার করতে হয় এঙ্গুলার ক্লায়েন্ট, সার্ভার এবং  API  এর জন্য ",
      "name": "প্রক্সি সার্ভারের  জন্য  NGINX কনফিগার করা ",
      "id": "8a1c80976b15a299016b1742d3e6000b"
    },
........
]